SLEEP that allows automation event-triggers to fire

krikikriki Member, Moderator Posts: 9,094
edited 2008-09-01 in NAV Tips & Tricks
If you use some automation and want to wait for some event to fire, most of the time you use SLEEP. The problem I found is that SLEEP does NOT allow the event-triggers to fire (at least not the ones I found).
So I have invented a method that allows it: I created a function "RealSleep" that allows the event-triggers to fire:
RealSleep(IintSeconds : Integer)
// RealSleep
// The Navision SLEEP-command blocks automations.
// This Sleep does NOT block automations because it uses the WSH shell command
// PARAMETERS:
//   IintSeconds : No. of seconds to do a sleep

IF IintSeconds <= 0 THEN
  EXIT;

LtxtCRLF := 'xx';
LtxtCRLF[1] := 10;
LtxtCRLF[2] := 13;

LtxtTempVBS := GetTemporaryFilename(TRUE,'vbs');

Lfil.WRITEMODE(TRUE);
Lfil.TEXTMODE(TRUE);
Lfil.QUERYREPLACE(FALSE);
Lfil.CREATE(LtxtTempVBS);
Lfil.WRITE(STRSUBSTNO('WScript.Sleep %1',IintSeconds * 1000));
Lfil.CLOSE;

DosShell('cscript.exe //nologo ' + LtxtTempVBS,0,TRUE);

ERASE(LtxtTempVBS);

Remarks:
1) the "DosShell"-function you can find here : http://www.mibuso.com/forum/viewtopic.php?t=12417
2) I put the waittime in seconds, and not in milliseconds because the waittime will not be exactly 1 second because of the fact that a file is written and then executed).
Regards,Alain Krikilion
No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


Comments

  • EugeneEugene Member Posts: 309
    i cant imagine why one would ever need such a thing.

    It is time already to change mentality from single flow programming to multi thread event driven programming :) - you write your code in triggers that respond to some events in the environment your program is living in, the order of those events is a priori unpredictable.

    So in case you want to do something like Object.RunAndWaitForResponse where response is a firing of some trigger Object.OnResponseEvent

    you should change your program logic from
    Object.RunNoWait
    SLEEP(5000) OR REPEAT UNTIL GotResponse
    ContinuePlayingWith(Object)
    
    to
    Object.RunNoWait
    WaitingForObjectResponseUntil := CURRENTDATETIME+5000
    EXIT
    
    and trigger Object.OnResponseEvent code:
    IF CURRENTDATETIME > WaitingForObjectResponseUntil THEN EXIT;
    WaitingForObjectResponseUntil := 0DT; //no more waiting
    ContinuePlayingWith(Object)
    
  • krikikriki Member, Moderator Posts: 9,094
    Eugene wrote:
    Object.RunNoWait
    WaitingForObjectResponseUntil := CURRENTDATETIME+5000
    EXIT
    

    The problem with this code in Navision is the 3 lines are run and Navision continues with other things WITHOUT waiting.

    In general you C/AL code prepares something and then runs the automation.
    The command that calls the automation returns immediately WITHOUT waiting for the end of the task the automation should do.
    So the C/AL command after the automation-call is run but the automation has not finished yet.
    How can I let Navision wait for it?
    The only thing I found is:
    blnTheAutomationFinished := FALSE;
    REPEAT
      RealSleep(5);
    UNTIL blnTheAutomationFinished;
    

    and in some event-trigger of the automatin I put:
    blnTheAutomationFinished := TRUE;
    

    If you found a better wat, let us know.
    Regards,Alain Krikilion
    No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


  • EugeneEugene Member Posts: 309
    The problem with this code in Navision is the 3 lines are run and Navision continues with other things WITHOUT waiting.

    Exactly :) that's why you do not put any code after (i wrote EXIT to indicate that there is no code below) but instead place the rest of your code (which is supposed to be executed upon getting the response) in the corresponding trigger which receives the response from the object
    So the C/AL command after the automation-call is run but the automation has not finished yet.
    there is no point in putting the command where it does not belong to be :) Put the command in the trigger
  • krikikriki Member, Moderator Posts: 9,094
    Eugene wrote:
    The problem with this code in Navision is the 3 lines are run and Navision continues with other things WITHOUT waiting.

    Exactly :) that's why you do not put any code after (i wrote EXIT to indicate that there is no code below) but instead place the rest of your code (which is supposed to be executed upon getting the response) in the corresponding trigger which receives the response from the object
    So the C/AL command after the automation-call is run but the automation has not finished yet.
    there is no point in putting the command where it does not belong to be :) Put the command in the trigger
    The EXIT returns the control to the calling object/function or returns control to the system.
    In the last case, if it is a user session, the user can start doing other things. If it is a NAS, other triggers can be fired. So you are not sure that your event-trigger is fired.
    Regards,Alain Krikilion
    No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


  • EugeneEugene Member Posts: 309
    the user can start doing other things
    Is not the whole point of multitasking to allow users to do multiple other things simultaniously? User maybe even interested to see the list of tasks being executed.
    If it is a NAS, other triggers can be fired.
    again should not NAS be able to executed many tasks simultaniously ? That's exactly the problem with your REPEAT UNTIL loop - it prevents NAS from responding to new events while your task is being processed
    So you are not sure that your event-trigger is fired.
    To be sure define automation object in a single instance codeunit so it continues to sit in memory even after the trigger is executed.
  • krikikriki Member, Moderator Posts: 9,094
    The C/AL code in not re-entrant. This means that the C/AL code can only handle one thing at a time.
    Like :
    -C/AL command.
    -automation call that should process something and only come back when it has finished. If I can't give a parameter to let the automation wait for the end. So I need to put some code after the automation-call to wait until it has finished.
    -C/AL command that ONLY should execute when the automation has finished.
    Regards,Alain Krikilion
    No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


  • EugeneEugene Member Posts: 309
    The C/AL code in not re-entrant. This means that the C/AL code can only handle one thing at a time

    Please can you explain what do you mean by that ?

    For example:
    i create a form and define a single instance codeunit variable MyCU inside the form and then from form's button i call codeunit's function that creates an automation object MyAO defined in that codeunit and asks that object to do some tasks. I can click the button multiple times and if codeunit is designed to create multiple MyAO and run them then they will execute simultaniously.
    When i close the form my understanding is that MyCU variable is destroyed upon exit from the form but MyAO object still exists and continues user's task
  • krikikriki Member, Moderator Posts: 9,094
    First : if you use a singleinstance codeunit, once initialized, the instance will NOT be destroyed by destroying the instance of the object that uses it.
    Even more : ALL objects referencing it will use THE SAME instance.
    You can test that by defining a global in your singleinstance codeunit and running and object that gives a value to it and then running another objects that asks the value of it. (giving and asking the value must be done through functions in the singleinstance codeunit.

    Navision NEVER will execute code at the same time. Like Windows (with a single core singe processor): it seems to be multitasking but in reality it is taskswapping so fast that it seems to be multitasking.

    "not re-entrant":
    Example : you launched 2 times a pdf-printing automation (in a singleinstance codeunit) and you are waiting for the finishing-event of both. The finishing-event will be called twice (once for each pdf) but in that event, you will NEVER know which of the 2 has finished first! This is the reason you need to wait for the first to finish before launching the second. And thus my repeat-until to wait for it.
    Regards,Alain Krikilion
    No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


  • EugeneEugene Member Posts: 309
    The finishing-event will be called twice (once for each pdf) but in that event, you will NEVER know which of the 2 has finished first! This is the reason you need to wait for the first to finish before launching the second.

    When calling pdf printing one can check if it is not in process of printing another pdf and ask the user if he wants to launch the second one. If the user chooses to start the second pdf printing then one needs to use a separate automation object for it.

    I do agree however that it is much easier to program single-flow than to program multitasking and your REPEAT UNTIL is easier to write. But the idea of asking repeatedly if task is completed is ridiculous - when you give a task to a person you don't come to him every minute asking if he completed the task, instead you simply ask him to report you back when he completes his task
  • krikikriki Member, Moderator Posts: 9,094
    Eugene wrote:
    when you give a task to a person you don't come to him every minute asking if he completed the task, instead you simply ask him to report you back when he completes his task
    This is the way it should be...... :(

    It is a NAS that does this work. The NAS would just continue to work on something else (maybe for a long time) and the event-trigger would be postponed a lot because it wouldn't be triggered until the NAS isn't running and C/AL anymore. So this is the reason I didn't want to use your system (although it is more programmatically more correct).
    Regards,Alain Krikilion
    No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


  • EugeneEugene Member Posts: 309
    In NAS
    "the event-trigger would be postponed a lot because it wouldn't be triggered..."
    Actually there is a way to let NAS run multiple tasks
    in codeunit 1 on NAS trigger call your single instance codeunit. In codeunit write the following in OnRun trigger:


    OnRun()
    CREATE(Timer);
    Timer.Interval(1000);
    MESSAGE('My service started at %1 %2',TODAY,TIME);
    Timer.Enabled(TRUE);

    then in OnTimer tigger:
    Timer.Enabled := FALSE;
    ProcessMyScheduledTasks
    Timer.Enabled := TRUE;

    So after executing OnRun the NAS can process other requests
    but your service is now installed and timer will be called every second to check if there are tasks to be executed by your service
  • EugeneEugene Member Posts: 309
    oh almost forgot, you may also want to have this in on TimerError trigger:
    Timer.Enabled := TRUE;
  • krikikriki Member, Moderator Posts: 9,094
    I know. I am using that technique. But it still won't run the taks in parallel.
    If a trigger is fired, no other trigger will be fired UNTIL the first taks is finished.
    Regards,Alain Krikilion
    No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


  • EugeneEugene Member Posts: 309
    When you comment out timer enabling/disabling:
    //Timer.Enabled := FALSE;
    ProcessMyScheduledTasks
    //Timer.Enabled := TRUE;
    

    the timer trigger is not firing but i believe timer events are stacked into Navision's windows messaging queue. Would be interesting to test it - if i keep my task busy for 1 minute will the trigger fire 60 times right after exiting my task or will it fire just once ?
  • krikikriki Member, Moderator Posts: 9,094
    Just once.
    Regards,Alain Krikilion
    No PM,please use the forum. || May the <SOLVED>-attribute be in your title!


Sign In or Register to comment.