Wednesday, February 07, 2007

 

InStream and OutStream

Recently I encountered some problems using the InStream and OutStream, and since they were introduced in Navision, it has been very confusing on how they should be used.

One of the most convinient usages of Streams is the aid of reading and writting big texts, and by big we mean really big texts. Since Navision has a limit of around 1024 to 2048 characters, Streams provide one of the more convinient ways of dealing with webpages and long text documents directly in Navision.

However, bare in mind that there are 2 ways of writting and 2 ways of reading Streams: binary and text wised. So, whenever you are using OutStream.Write, you are writting binary, and when using OutStream.WriteTEXT, you are, literally writting only the text part of the parameter.

So, when using READ, versus READTEXT, you will be seeking for the NULL termination that the binary text will WRITE out. That is why you will get an AL error if this NULL termination was not found.

As an example:

text[3]: t1; // text of length 3
text[4]: t2; // text of length 4
IS:InStream;
OS:OutStream;

t1:= "ABC";
t2:= "ABC";

OS.WRITE(t1); // This will write to the Stream: 'A' 'B' 'C' chr(0)
OS.WRITE(t2); // This will write to the Stream: 'A' 'B' 'C' chr(0)

// If using the same Stream ....
IS.READ(t1); // Should read 'A' 'B' 'C' chr(0)
IS.READ(t2); // Should read 'A' 'B' 'C' chr(0)

This only works in 4.00.SP3 versions and will fail in previous versions, however, there is a side effect to the correction. Take this example:

text[3]: t1; // text of length 3
text[1]: t2; // text of length 1

integer: i;
IS:InStream;
OS:OutStream;

t1:= "ABC";

OS.WRITE(t1); // This will write to the Stream: 'A' 'B' 'C' chr(0)
OS.WRITE(t1); // This will write to the Stream: 'A' 'B' 'C' chr(0)

// If using the same Stream ....
i := IS.READ(t2); // This will read 'A' 'B' into a text[1] !!!

// i will be 2.

Also, notice that, as a side effect, this code is not valid:

text[1]: t2; // text of length 1
IS:InStream;
text[1024]: biggerText;
// Reading any Stream, for instance a text file

WHILE (IS.READ(textChar) > 0) DO
BEGIN
biggerText := biggerText + textChar;
END;

What happened here? It is actually simple: "IS.READ(textChar)"is not reading 1 character, but 2.

Wednesday, August 02, 2006

 
The NTimer problem

A couple of months back I was dealing with a problem that took me a while to solve. I have been looking at both the Automation source code, and the AL code, and, although I don’t consider myself an AL expert, I do know how the AL engine works, so I was expecting to find an error if it indeed exist.
After struggling quite a bit with the source code of the Automation server I decided to send my modified Automation to test and proceed to explain the tester how the code was working and, then it hit me! Of course, there was a logic error in the code that did not seem obvious at the beginning.
I’ll try to explain the problem in a nutshell, but please keep in mind that similar problems exist in AL code, and there is no easy way of solving it (other than changing your original design). Please also notice that this is not a bug, but a limitation and that this does not mean that there is no possible solution to your logic, but that you have to “think further”.
Think at this design:
2 Automation servers, both with Events. For illustrative purposes, one of them being the ‘'Navision Timer 1.0'.Timer’. The code would be similar to this:

Global variables:
Auto1 Automation 'MyAutomation’.MyLogic
NTimer Automation 'Navision Timer 1.0'.Timer

OnRun trigger:
CREATE(Auto1);
CREATE(NTimer);
NTimer.Interval(20); // in milliseconds
NTimer.Enabled(FALSE);

My Automation event:
...
GlobalVariable := myCurrentSomething;
NTimer.Enabled(TRUE);
...

Ntimer event:
...
Do_Some_Processing(GlobalVariable);
...
CLEAR(GlobalVariable);


NTimer::TimerError(ErrorString : Text[1024])
Do_Some_Error_Handling_Specific_to_the_context(ErrorString, GlobalVariable);

Now. The previous code looks quite fine. The main reason why people are using the NTimer is because they can get, programmatically, any AL error in the only parameter of the TimerError, which is very convenient. I will, in another item, explain how to get this error programmatically from an Automation component.
So that we all are on the same page, you should see from the previous code that we have three different events:
- My Automation event
- The NTimer event
- The TimerError event

Let’s test this code with a simple scenario:

My Automation event is raised, which enables the NTimer, which uses the GlobalVariable to save a state. Now, at this point, we don’t know if the NTimer event will be called first, or our Automation event. Right now, this is irrelevant.

Now, when the NTimer event is raised, it does some processing with the GlobalVariable and at the end it erases it, but if an AL error occurs, the TimerError event should be raised where we are also using the GlobalVariable. This is a problem!


Wednesday, July 12, 2006

 

Navision Platform Internals

The beginning

Most of my friends and colleagues have now their own BLOGS and I have been thinking about writing one myself for quite a long time, although I know I do not have the time to keep it updated as much as I would like. But I promise to make time whenever some becomes available.

I have spent the last seven years working with the Navision platform and even though I know there are lots of resources for the product, I would like to contribute with information that is interesting to me and that is not documented. I do read from time to time posts on the Internet (either newsgroups or other sources) to see which types of questions are around but many times I don’t have the information at that precise moment and get busy with other stuff, forgetting about my good intentions.

Well, since this is the first posting I make, and since I am learning how things work over here, I don’t want to spend too much time editing this text (I have just lost four paragraphs I previously wrote), but I hope soon to write about the Navision timer automation and a problem that took me a while to find out and I think this is a very recurring problem, so hopefully it will be useful for some.

Hope for the best (for myself) in this adventure.

Jorge

This page is powered by Blogger. Isn't yours?