Home AMX User Forum NetLinx Studio

Commands at Certain Times

Hi,

I want to get a screen to power on and off at specific times. The code I'm using is

WAIT 10
{
CALL 'PROJECTOR-STATUS'(LOOP)
LOOP = LOOP + 1
IF (LOOP = 7)
{LOOP = 1}
IF (TIME = '10:00:00')
{
SEND_COMMAND LCD4010, "$30,$30,$21,$0D"
}
IF (TIME = '18:00:00')
{
SEND_COMMAND LCD4010, "$30,$30,$22,$0D"
}
}

Yet at these times the screen isnt powering on - just as info these commands do work and the screen is connected ;)

Thanks

Ed

Comments

  • JeffJeff Posts: 374
    Well, your code is only firing once every second, and you've got a time thats specific down to milliseconds. This means that your code is probably running at 10:00:34 and 10:01:34 and 10:02:34 or something like that.

    Just a guess

    J
  • VLCNCRZRVLCNCRZR Posts: 216
    timed event

    This method using wildcards for the time is more reliable, because
    the sequence isnt restricted to an exact "on-the-second" process.

    As long as your processor time is correct, this should work every time.

    //DAILY TIMED PROCEDURE
    IF (TIME = '23:10:??') (* 12:00 MIDNIGHT *)
    {
    IF (SYSTEM_STATUS) (* IF SYSTEM IS ON *)
    {
    CALL 'SHUTDOWN_SEQUENCE' (* SHUTDOWN SYSTEM *)
    }
    }
  • JeffJeff Posts: 374
    i've always wondered this and not tested. Will that, exactly as written, fire more than once?i.e., should i put it inside a wait 1 second command like the OP?
  • Thanks

    Hi,

    Thanks for this - Ill give it a go now. Didn't realise about the wildcards - I thought it might be missing the exact second.

    Is it better to keep it inside the WAIT command or have it as part of the actual program code ?

    Thanks

    Ed
  • DHawthorneDHawthorne Posts: 4,584
    Jeff wrote:
    i've always wondered this and not tested. Will that, exactly as written, fire more than once?i.e., should i put it inside a wait 1 second command like the OP?

    It depends how busy the rest of the code is. NetLinx is multi-threaded, but the threads run sequentially, not concurrently. If the program has enough to to do, it may not fire more than once, but if the program passes through that stage of the code faster than the interval it's testing for, it's very possible it will. Myself, I always drop a flag variable in so it can't run more than once no matter what, and reset the flag when the sequence is restarted.

    On the other hand, the one second wait the OP put in can easily cause it never to fire, because every pass through the loop it's testing for an exact second. Waits are not that precise. When you initiate a WAIT, the code is shelved until the next pass through the main process loop. At that point, it is tested to see if the at least that much time has expired. If not, it is shelved again until it is. However, your wait may have initialized at 10:00:59:59 ... wait a full second, plus program execution time, even only a few milliseconds, and it may now be 10:00:01:01. The time test will then fail. In that particular example, a wait of less than a second would likely work just fine ... but make it too small and it will fire multiple times. I would go (if I were stuck on doing it that way) with a wait value of 6. That would be impossible to fire twice, but not likely to run into problems from the inaccuracy of the wait handling.
  • ericmedleyericmedley Posts: 4,177
    DHawthorne wrote:
    It depends how busy the rest of the code is. NetLinx is multi-threaded, but the threads run sequentially, not concurrently. If the program has enough to to do, it may not fire more than once, but if the program passes through that stage of the code faster than the interval it's testing for, it's very possible it will. Myself, I always drop a flag variable in so it can't run more than once no matter what, and reset the flag when the sequence is restarted.

    On the other hand, the one second wait the OP put in can easily cause it never to fire, because every pass through the loop it's testing for an exact second. Waits are not that precise. When you initiate a WAIT, the code is shelved until the next pass through the main process loop. At that point, it is tested to see if the at least that much time has expired. If not, it is shelved again until it is. However, your wait may have initialized at 10:00:59:59 ... wait a full second, plus program execution time, even only a few milliseconds, and it may now be 10:00:01:01. The time test will then fail. In that particular example, a wait of less than a second would likely work just fine ... but make it too small and it will fire multiple times. I would go (if I were stuck on doing it that way) with a wait value of 6. That would be impossible to fire twice, but not likely to run into problems from the inaccuracy of the wait handling.

    That is why I tend to do everything when time is HH:MM:58 seconds or greater.

    So, I'll do something like this
    // Do something at 6AM
    
    if(HOUR=5 AND MINUTE=59 AND SECONDS>58 AND FLAG=0)
    {
    // DO SOMETHING
    WAIT 50
      {
      FLAG=0
      }
    FLAG=1
    }
    
  • viningvining Posts: 4,368
    Try "Send_String" not "Send_Command".
  • DHawthorneDHawthorne Posts: 4,584
    vining wrote:
    Try "Send_String" not "Send_Command".

    Heh - here we are getting all technical, and there is a blatant error that will keep it from working anyway. Vining is quite correct - a SEND_COMMAND won't do a thing sent to a physical device, it's only for internal commands to the device handler.
  • GSLogicGSLogic Posts: 562
    This code triggers only one time, because nBlock stops re-triggering for 70 seconds. If you remove nBlock you'll see this code trigger many times within the DEFINE_PROGRAM loop.
    DEFINE_PROGRAM
    if(compare_string(time, '20:00:00') and nBlock == 0)
    {
       nBlock = 1;
       wait 700 nBlock = 0;
       powerOFF();
    }
    
  • This works for me.
    Define_Program
    
    IF (TIME='22:00:00') [color=green]    // Execute everyday at 10 PM[/color]
    {
      Wait 11
        fnSystem_Power(nOff) [color=green]// Excute system off function[/color]
    }
    

    It is dirt simple, excutes only once, and no, you do not need to name the wait. ;)
  • cmacma Posts: 94
    I prefer and have found this to be very reliable:


    DEFINE_EVENTS

    CHANNEL_EVENT[vdvTIME_EVENT,1]{
    ON:{
    PERFORM EVENT HERE
    }
    }

    DEFINE_PROGRAM

    IF((TIME_TO_HOUR=12)&&(TIME_TO_MINUTE=32)) ON[vdvTIME_EVENT,1]
    ELSE OFF[vdvTIME_EVENT,1]


    This will only trigger once while the time_to_minute function equals 32 and it wont matter how many times the program runs through the line of code.
  • viningvining Posts: 4,368
    cma wrote:
    This will only trigger once while the time_to_minute function equals 32 and it wont matter how many times the program runs through the line of code.

    Time to minute will equal 32 for 59 seconds which means it could execute 37,356 times. Ok, I made that number up but it's alot if in the mainline.
  • GSLogicGSLogic Posts: 562
    B_Clements wrote:
    IF (TIME='22:00:00') [color=green]    // Execute everyday at 10 PM[/color]
    {
      Wait 11
        fnSystem_Power(nOff) [color=green]// Excute system off function[/color]
    }
    
    If you put a notification in this code you can see it re-trigger. The reason it only execute the code one time is because the waits will cancel out the previous wait.
    Turn diagnostics on and try this code :
      IF (TIME='22:00:00')    // set NI time to 9:59:50PM
      {
    	Wait 11
    	   send_string 0, "'triggered'"; 
    	  
    	send_string 0, "'pre-trigger'"; 
      }
    
    My NI-3100 triggered 1143 times before triggering the Wait 'triggered' line.
    You should use a nBlock (see below) variable to stop re-triggers.
  • cmacma Posts: 94
    vining wrote:
    cma wrote:


    Time to minute will equal 32 for 59 seconds which means it could execute 37,356 times. Ok, I made that number up but it's alot if in the mainline.

    Except that basing the action on the channel event makes the programmed action happen once, even if it executes 37,000 times it will only actually execute one channel on command because once a channel is on it cannot trigger another channel on event from on to on again and the channel off command is then sent when the time to minute does not equal the time you are looking at. I have tried other combinations in the past and when performing a time function based on a time to second I have experienced misses where the programmed function did not activate because somehow it was missed.
  • viningvining Posts: 4,368
    cma wrote:
    Except that basing the action on the channel event makes the programmed action happen once
    True, if a channel is on already turning it on again will have no ill affect but the in fact is the code will execute on every pass of the mainline the entire time your minute = 32. If the executing statement just changes a channel state not a big deal but if your sending a string or executing a larger section of code it is. If you only want your code to fire once it should be written so that's what is actually does and not have it fire 37,000 doing the same thing. Granted in this instance there's not much difference from what you wrote and having channel feedback in the mainline but even feedback in the mainline I would put behind a wait 1 or 2.... just to keep it from being evaluated on every pass.

    Be kind to your processor and it will in turn be kind to you.
  • viningvining Posts: 4,368
    GSLogic wrote:
    The reason it only execute the code one time is because the waits will cancel out the previous wait.
    Not true! If that were indeed the case the wait would never execute as it would always be re-written and waiting 11. (see ealier posts)

    It only executes once because when the wait is in queue it can't be re-written.

    When the program comes across a wait it basically runs a function like this one below:
    DEFINE_FUNCTION CHAR fnWAIT(CHAR iNewWaitName[],INTEGER iWaitTime)
    
         {
         if(length_string(Wait_Name_Queue[1]))//if 1 exists continue to check
    	  {
    	  STACK_VAR INTEGER nQ ;
    	  STACK_VAR INTEGER nQcount ;
    	  STACK_VAR INTEGER nQIndex:
    	  
    	  for(nQ = 1 ; nQ <= length_array(Wait_Name_Queue) ; nQ ++)
    	       {
    	       if(length_string(Wait_Name_Queue[nQ]))//something here so check name
    		    {
    		    if(Wait_Name_Queue[nQ] == iNewWaitName)
    			 {
    			 RETURN FALSE ;//just exit already in queue.
    			 }
    		    nQIndex ++ ;
    		    }
    	       else //index position empty no need to go further
    		    {
    		    nQ = length_array(Wait_Name_Queue) ;
    		    }
    	       }
    	  //add new wait after last entry
    	  Wait_Name_Queue[nQIndex +1] = iNewWaitName ;
    	  Wait_Time_Queue[nQIndex +1] = iWaitTime ;
    	  
    	  RETURN TRUE ;
         	  }
         else //no waits in queue so add at the begining
    	  {
    	  Wait_Name_Queue[1] = iNewWaitName ;
    	  Wait_Time_Queue[1] = iWaitTime ;
    	  
    	  RETURN TRUE ;
    	  }
         }
    

    Obviously this is a very simplified interpretation of how I believe it to work and would probably be a structure and contain a pointer to the code that would need be executed. The system would check and see if anything is in queue and if so start a timeline that repeats every 100ms. The timeline would go through every entry and decrement each entries time value and when the time value reached zero it would execute that section of code and clear that entry and then shift the other entries to fill the gap and when there are no more entries in the queue it would the system simply stop the timeline.

    GSLogic wrote:
    You should use a nBlock (see below) variable to stop re-triggers.
    In B-Clements example the wait 11 was the block because his time event included seconds of 00 which means after the wait 11 (1.1 seconds) the current time would be xx:xx:01 and no longer a true statement and ther fore wouldn't execute again.
  • Thanks

    Thanks for all the info and the deep drilling down.

    I made the simple mistake of the SEND_COMMAND & swapped this and it seems to work. I will move it into an event thou I think - just to make it more easier to read.

    The wildcards in the time values don't work thou - just for info IE TIME = '23:00:??'

    Thanks Again

    Ed
  • GSLogicGSLogic Posts: 562
    vining wrote:
    In B-Clements example the wait 11 was the block because his time event included seconds of 00 which means after the wait 11 (1.1 seconds) the current time would be xx:xx:01 and no longer a true statement and ther fore wouldn't execute again.
    If you run the test code I posted, you can see it repeating over and over.

    I understand in a small project it's okay to ignore these repeats, but in large jobs we have numerous user timer events and having the processor try to repeat a half million times a minute is unacceptable. (64 timer events x 8,000 = 512,000 loop).
    You can get around all this non scene by just using a TIMELINE which repeats ever minute.

    footnote: number of processor repeats will vary do to the NI model, your code and the moon :)
  • viningvining Posts: 4,368
    GSLogic wrote:
    If you run the test code I posted, you can see it repeating over and over
    But that's your "pre-trigger" outside the wait that repeats not the "trigger" that is inside the wait. The "triggers" executes only once at XX:XX:00 and after execution the time is now XX:XX:01 so the IF condition is no longer valid.

    I completely agree no matter what size system you're running that code should only execute once it that is all it needs to do regardless of whether is has ill affect or not.
  • GSLogicGSLogic Posts: 562
    vining wrote:
    But that's your "pre-trigger" outside the wait that repeats not the "trigger" that is inside the wait. The "triggers" executes only once at XX:XX:00 and after execution the time is now XX:XX:01 so the IF condition is no longer valid.
    I see what you're saying and you are correct!
    I'm talking about entering the "IF (TIME='22:00:00')" statement, this IF statement becomes true and repeats 1143 times.
    What I'm trying to saying is, why enter the IF statement to just pound the WAIT command over and over.
    Entering the IF statement, do your business and get out. (wash your hands when your done)
  • DarksideDarkside Posts: 345
    GSLogic wrote:
    I see what you're saying and you are correct!
    I'm talking about entering the "IF (TIME='22:00:00')" statement, this IF statement becomes true and repeats 1143 times.
    What I'm trying to saying is, why enter the IF statement to just pound the WAIT command over and over.
    Entering the IF statement, do your business and get out. (wash your hands when your done)
    Using the IF(time = 23:00:00) approach, the routine will certainly be re-entered but the wait won't be stacked because it will exist after the first legal pass of IF(time = 23:00:00). This forces your 'nBlock' behavior on the routine in a way without having to write it.

    So, what is the difference if any, I wonder, between the routine executing and 'pounding' the existing wait inside, or having nBlock in the IF statement that is also going to get 'pounded' while 23:00:00 is true.

    Surely it's just 'pounding' a different door!

    :-)

    I use the wait 11 approach all the time - saves another var declaration if nothing else.

    Quick, and to me perfectly reasonable!
  • ericmedleyericmedley Posts: 4,177
    I like to use the set variable flag method because it's an easy way to see the routine fire in debug. and hey, it's only a byte. not a lot of RAM wasted...
  • The motto at AMX is SIMPLFY!
    GSLogic wrote:
    This code triggers only one time, because nBlock stops re-triggering for 70 seconds. If you remove nBlock you'll see this code trigger many times within the DEFINE_PROGRAM loop.
    DEFINE_PROGRAM
    if(compare_string(time, '20:00:00') and nBlock == 0)
    {
       nBlock = 1;
       wait 700 nBlock = 0;
       powerOFF();
    }
    
    IF (TIME='22:00:00')     [color=green]// Execute everyday at 10 PM[/color]
    {
      Wait 11
        fnSystem_Power(nOff) [color=green]// Excute system off function[/color]
    }
    

    Gary,

    Your code works every bit as well as mine, no problem. My method saves declaring another variable thus simplifing the solution.

    In my example and yours the IF statements in the Define_Program section are evaluated 1143 times per second plus or minus. The Wait queue gets evaluated automatically after each pass through the Define_Program section so there is no extra processing.

    Keep in mind the Define_Program section will run whenever the Event queue is empty, but no less than about 15 times a second no matter how big your program is or number of events pending.

    By the way, I really dig the passion on the forums.

    Happy Thanksgiving all.
  • GSLogicGSLogic Posts: 562
    B_Clements wrote:
    By the way, I really dig the passion on the forums.
    Happy Thanksgiving all.

    Hi Brian, hope all is well and YES it is a tough crew out there, but it's the only way it should be!

    Happy Turkey 01 u 01. :)
  • bobbob Posts: 296
    I think that the following will have the least impact on the CPU executing one compare each minute. Also, there is no flag variable needed.

    WAIT 600 IF (COMPARE_STRING('07:35:??', "TIME")) doDaily();
  • what about this?
    timeline_event[tl4]
        {
        if(_uStatus.nSystemPower = nOn)
    	{
    	if(compare_string(time, '18:3?:??') and nBlock == 0)
    	    {
    	    nBlock = 1;
    	    wait 700 nBlock = 0;
    	    fnwall('OFF');
    	    }
    	}
        if(_uStatus.nSystemPower = noff)
    	{
    	if(compare_string(time, '06:3?:??') and nBlock == 0)
    	    {
    	    nBlock = 1;
    	    wait 700 nBlock = 0;
    	    fnwall('OFF');
    	    }
    	}
        }
    

    This should run every five minutes. still less than the zillion times in mainline, no?
Sign In or Register to comment.