Home AMX User Forum AMX General Discussion

Another stupid Q...

This is probably embarrassing, but I couldn't find the answer on my own (I probably just don't know what I'm looking for). Anyway, where's the timer functionality in NetLinx? For instance, lights on at 8pm? Button events are easy, but are there time-based events? I am sure this is a common task, just not sure how to do it. Bear with the stupid questions, I'm learning as I go!

Thanks,
Dan

Comments

  • NMarkRobertsNMarkRoberts Posts: 455
    Timing

    This is my code to trigger a system cleanup at 4 in the morning:
    wait nOneMinuteTenths (* 60x10 *)
      {
      if (left_string(time,5) = '04:00')
        {
        MidnightHousekeeping()
        }
      }
    
  • HedbergHedberg Posts: 671
    There is a system variable "TIME". It "holds the current time as a string in 'hh:mm:ss'."

    Testing for time is something that I will normally put in the define_program section. Something like:

    if(time = '23:59:59')
    {
    do_something_at_midnight()
    }

    I can't really think of a better place to put something like this than in define_program.

    I don't know for certain that define_program will be executed during every distinct TIME value, but it seems to work.
  • NMarkRobertsNMarkRoberts Posts: 455
    Hedberg wrote:
    if(time = '23:59:59')
    {
    do_something_at_midnight()
    }

    That code could execute twice or many times.
  • HedbergHedberg Posts: 671
    Yes, it could. Generally, not a good idea.
  • dmurray14dmurray14 Posts: 80
    Wow, I have to say I'm surprised there's no real easy way to do this - I'd think this is something that's done a lot? Don't most homeowners have lights they want on "timers"?

    Dan
  • DHawthorneDHawthorne Posts: 4,584
    The simple answer is there is not built in timer functionality as such; one solution is to use the i!-Schedule package, but that's a little overblown for most applications. However, it will do exactly what you want.

    As for using the TIME variable, that suffices for simple timer operations. As mentioned, you have to be careful it won't trip multiple times. What I do is set a flag, and the first time the event fires, set the flag, then put a test in the event not to fire if the flag is set. Resetting the flag is simple; I use a persistent variable that I name sToday, and put this little routine in mainline:
    IF(sToday <> DATE)
    {
        sToday = DATE    // update variable; this will happen only once, daily at midnight
        bTimerFlag = FALSE    // reset timer flag, repeat as necessary with other flags
    }
    
    This makes use of the other built in variable DATE. The routine will only fire when it changes, at midnight. If the sToday variable is not persistent, it will also fire when your code is updated; there are times when this behavior may be desired, but mostly, it's not.
  • GSLogicGSLogic Posts: 562
    I setup a master TIMELINE that repeats ever minute and base every timed event off this TIMELINE.
  • viningvining Posts: 4,368
    if you create a local or global var to compare time and then set the time you can ensure it only executes once. The code below does this for me so I can track my own seconds, minutes, hours, etc.
           if(nMY_Second != TIME_TO_SECOND(TIME))              add to above global vars
    	  {
    	  nMY_Second = TIME_TO_SECOND(TIME) ;
    	  if(nMY_Minute != TIME_TO_MINUTE(TIME))
    	       {
    	       nMY_Minute = TIME_TO_MINUTE(TIME) ;
    	       if(nMY_Hour != TIME_TO_HOUR(TIME))
    		    {
    		    nMY_Hour = TIME_TO_HOUR(TIME) ;
    		    if (cMyDay != Day)
    			 {
    			 cMyDay = Day ;
    			 if (cMyDate != Date)
    			      {
    			      cMyDate = Date ;
    			      }
    			 }
    		    }
    	       }
    	  }
    

    This is sort of a long winded inside out version of what NMarkRoberts provided:
           if(time = '23:59:59')
    	  {
    	  local_var integer nRunOnce ;
    	  if(!nRunOnce)
    	       {
    	       do_something_at_midnight() ;
    	       nRunOnce = 1 ;
    	       wait 20 // an extra second for good measure
    		    {
    		    nRunOnce = 0 ;
    		    }
    	       }
    	  }
    
  • mpullinmpullin Posts: 949
    I use the same code NMarkRoberts does. It's very short and simple. One should keep in mind the task will not be done EXACTLY at the given time, but you will always be within 59 seconds (work it out :))
    DEFINE_PROGRAM
    wait 600{ // do every minute
       if(LEFT_STRING(TIME,5) == '00:00'){ // do at midnight
          // do something
       }
    }
    
  • NMarkRobertsNMarkRoberts Posts: 455
    dmurray14 wrote:
    Wow, I have to say I'm surprised there's no real easy way to do this - I'd think this is something that's done a lot?

    I don't see why you think it's not easy - it's only two lines of code, and the "wait one minute" line you probably already have in your mainline define_program for any number of other reasons.

    BTW a timeline is the "correct" way to do it but that's considerably more than one line of code.
    dmurray14 wrote:
    Don't most homeowners have lights they want on "timers"?

    Yep, the owners of homes and boardrooms and lecture theatres have lots of good ideas about things that they want to automagically happen at certain absolute or relative times, until the second or third time that happens post installation, and then they want you to take out that damn silly code right now please.

    eg (1) Projector timeout 60 minutes after last system activity:

    Have you heard the one about the CEO who droned on for 60 minutes at the company AGM and was a bit upset when it took 5 minutes to get his powerpoint back?

    And the one about the keen AV assistant who got the projector going 45 minutes before the meeting started, because no-one trusts their AV gear to start when the On button is pressed, and everyone knows CEOs can't press On buttons themselves?

    eg (2) Lights out 60 minutes after last PIR movement detection:

    Have you heard the one about running an examination with hundreds of students in a lecture theatre? Guess what, nobody moves for an hour!

    8^)
  • ericmedleyericmedley Posts: 4,177
    dmurray14 wrote:
    Wow, I have to say I'm surprised there's no real easy way to do this - I'd think this is something that's done a lot? Don't most homeowners have lights they want on "timers"?

    Dan

    It depends upon how you look at it. We do several flavors of automation /control systems around here.

    The 'in the box' do something at such-and-so time, check-box type control systems end up not being used much because you can't put much customization into them.

    With an open code system, you can pretty much dream up anything you want.

    A good example of this is sunrise/sunset. With the Netlinx programming model you can really tweak what happens. Other's it's pretty much "when the sun hits the horizon it's going to happen plus or minus some minutes."

    I typically use a timeline for timed events. However, here's a down-and-dirty way to trap the example from above so it'll only happen once.

    I'll avoid shorthand to keep it simple.
    DEFINE_VARIBALE
    
    INTEGER TIMED_EVENT_FLAG // WILL EQUAL ZERO AT STARTUP
    
    DEFINE_PROGRAM
    
    
    IF(TIMED_EVENT_FLAG=0 AND TIME=='23:59:59')
    {
    // DO SOMETHING AT MIDNIGHT
    WAIT 20
      {
      TIMED_EVENT_FLAG=0 // REESET THE FLAG IN 2 SECONDS
      }
    TIMED_EVENT_FLAG=1
    }
    
    
  • pdabrowskipdabrowski Posts: 184
    Hedberg wrote:
    if(time = '23:59:59')
    {
      do_something_at_midnight()
    }
    
    I do something more like:
    if(time = '23:59:59')
    {
      wait 100 'midnightwait'
      do_something_at_midnight()
    }
    
  • viningvining Posts: 4,368
    wait question?

    mpullin wrote:
    wait 600{ // do every minute
    if(LEFT_STRING(TIME,5) == '00:00'){ // do at midnight

    In the above example how the processor handle the wait? Being that it is not named does it create a seperate wait on each pass so that after the 1st 60 second delay in affect the code happens every pass of the processor with a 60 second delay. Basically creating a seperate wait 600 every pass of the program which would then evaluate the statement the number of waits held while the statement is true. Or is the processor smart enough to know that it's the same wait and ignore it. That doesn't seem likely.

    pdabrowski wrote:
    if(time = '23:59:59')
    {
    wait 100 'midnightwait'
    do_something_at_midnight()
    }
    What if it was named wait? If a named wait is pending is it ignored or re-written so that in the above example the last wait is the one executed while the if statement is true as opposed to the first wait when the statement first became true.

    Hope these questions make sense to someone!
  • NMarkRobertsNMarkRoberts Posts: 455
    vining wrote:
    mpullin wrote:


    In the above example how the processor handle the wait? Being that it is not named does it create a seperate wait on each pass so that after the 1st 60 second delay in affect the code happens every pass of the processor with a 60 second delay. Basically creating a seperate wait 600 every pass of the program which would then evaluate the statement the number of waits held while the statement is true. Or is the processor smart enough to know that it's the same wait and ignore it. That doesn't seem likely.

    pdabrowski wrote:

    What if it was named wait? If a named wait is pending is it ignored or re-written so that in the above example the last wait is the one executed while the if statement is true as opposed to the first wait when the statement first became true.

    Hope these questions make sense to someone!

    You already have a complete solution to the problems, if not to these questions, so why bother asking them again? What is the actual problem?
  • viningvining Posts: 4,368
    NMarkRoberts wrote:
    You already have a complete solution to the problems, if not to these questions, so why bother asking them again? What is the actual problem?
    No problem mate, just a simple question to which a simple answer could suffice. Maybe you should read the question again!
  • dchristodchristo Posts: 177
    vining wrote:
    mpullin wrote:


    In the above example how the processor handle the wait?

    See this post for more information on how waits are hanlded in this situation:

    http://www.amxforums.com/showthread.php?t=1592&highlight=mainline+wait

    --D
  • viningvining Posts: 4,368
    Well, I'm not sure that helped but as I gave it some more thought I would suspect that when a wait is initiated and put into the wait queue that the program appends some sort of marker or pointer to the that wait so that when it comes across the wait again, as it would multiple times when the wait is in DEFINE_PROGRAM, it can check to see if the wait is already in queue. If it is ignore the wait, if not, put it in queue. So in affect all waits are named by the program and some have additional names that we give them.

    Does this sound reasonable?
  • DHawthorneDHawthorne Posts: 4,584
    vining wrote:
    mpullin wrote:


    In the above example how the processor handle the wait? Being that it is not named does it create a seperate wait on each pass so that after the 1st 60 second delay in affect the code happens every pass of the processor with a 60 second delay. Basically creating a seperate wait 600 every pass of the program which would then evaluate the statement the number of waits held while the statement is true. Or is the processor smart enough to know that it's the same wait and ignore it. That doesn't seem likely.

    pdabrowski wrote:

    What if it was named wait? If a named wait is pending is it ignored or re-written so that in the above example the last wait is the one executed while the if statement is true as opposed to the first wait when the statement first became true.

    Hope these questions make sense to someone!

    My understanding of unnamed WAIT's is they act like a single named one with the same name. If there is already an unnamed wait active, a call for the next one is ignored until the first is done. So, if this is all that is going on, an unnamed and a named will act identically. It's only if there is another in the program that there may be an issue, and, of course, you can't cancel it if it's unnamed.
  • mpullinmpullin Posts: 949
    DHawthorne wrote:
    My understanding of unnamed WAIT's is they act like a single named one with the same name. If there is already an unnamed wait active, a call for the next one is ignored until the first is done. So, if this is all that is going on, an unnamed and a named will act identically. It's only if there is another in the program that there may be an issue, and, of course, you can't cancel it if it's unnamed.
    I disagree, I've done things like
    WAIT 0 SEND_COMMAND vdvADA, 'ROOM?1'
    WAIT 2 SEND_COMMAND vdvADA, 'ROOM?2'
    WAIT 4 SEND_COMMAND vdvADA, 'ROOM?3'
    WAIT 6 SEND_COMMAND vdvADA, 'ROOM?4'
    
    and these 4 commands will all be sent within 2 ticks of each other.

    My impression of unnamed waits is that the compiler assigns come kind of ID to each one at compile time, so an unnamed wait on a certain line cannot be started if an instance of the exact same wait is already going... which is the reason you can put WAIT 20 foo() in DEFINE_PROGRAM to do foo() every 2 seconds (poor man's timeline!)
  • viningvining Posts: 4,368
    mpullin wrote:
    My impression of unnamed waits is that the compiler assigns come kind of ID to each one at compile time, so an unnamed wait on a certain line cannot be started if an instance of the exact same wait is already going... which is the reason you can put WAIT 20 foo() in DEFINE_PROGRAM to do foo() every 2 seconds (poor man's timeline!)

    That's along the same lines as I was thinking to 3 post ago. Essentially if a wait isn't named by us it's named by the system and everytime the system comes across a wait it checks a table or queue to see if it's if the wait name is active or not.

    This seems reasonable!
  • NMarkRobertsNMarkRoberts Posts: 455
    DHawthorne wrote:
    My understanding of unnamed WAIT's is they act like a single named one with the same name. If there is already an unnamed wait active, a call for the next one is ignored until the first is done.

    Not quite - the same unnamed wait will be blocked, but a different unnamed wait isn't. They are implicitly named.
  • pdabrowskipdabrowski Posts: 184
    vining wrote:
    What if it was named wait? If a named wait is pending is it ignored or re-written so that in the above example the last wait is the one executed while the if statement is true as opposed to the first wait when the statement first became true.

    Hope these questions make sense to someone!
    the way I see it, naming wait's is to give them an unique identifier in the queue (very simple idea, I know but it's kept my code working!)

    For the action I proposed the use of a wait in that instance is to make the "if(time = '23:59:59')" a oneshot instead of being run potentially multiple times..

    for instance, I only want an after hours powerdown to be activated once in one second :D
  • DarksideDarkside Posts: 345
    pdabrowski wrote:
    For the action I proposed the use of a wait in that instance is to make the "if(time = '23:59:59')" a oneshot instead of being run potentially multiple times..
    The hot issue first!

    An unnamed wait is uniquely identified by the processor internally. The same wait will not be re-stacked until its current instance has expired.

    define_program
    wait 10 {do something}
    wait 10 {do something else}

    Neither of these waits will be re-stacked until each has expired. They are absolutely discrete and the wait value is unimportant.

    To return to the timer issue.....

    Placing a logic flag in the time expression would be the safest way to stop re-triggering as explained below. I haven't considered a timeline here...

    The following expression will potentially drive the system nuts. x is used to track the behavior.

    define_program

    if (time= '23:59:00')
    {
    x++
    // Your shutdown routine would go here
    }

    This will result in one whole second's worth of incrementing x at processor speed (on my ni3000, x = 1984). There is an obvious problem for running the shutdown routine here.

    whereas

    define_program

    if ((time= '23:59:00') && (!triggererd))
    {
    on[triggered]
    x++
    // Your shutdown routine would go here
    wait 50 'plenty of time' off[triggered]
    }

    x will increment only once. The shutdown routine will obviously only execute once only and the routine waits 5 seconds then reset the [triggered] flag to re-enable tomorrow nights shutdown procedure.

    Mathematically, the following should also produce a single increment, but most likely won't!

    On paper, 23:59:00 will not be true any longer by the time the wait 10 has expired, therefore there should be no re-entry

    if (time= '23:59:00')
    {
    wait 10
    {
    x++
    // Your shutdown routine would go here
    }
    }

    The basic maths here assumes no processing etc. and the reality is that x can indeed increment further - in my case with the ni3000 (and only with this routine running) x = 2.

    If you make the wait 10 into wait 11, all is well in this case, but it a large code would most likely make this value unworkable again!

    My two cents worth
  • Shutdown and Waits

    I vote for this one; works perfectly.
    DEFINE_PROGRAM
    if (time= '23:59:00')
    {
      wait 11
      {
        // Your shutdown routine would go here
      }
    }
    

    Just to verify, any and all unnamed waits are treated exactly like a named wait, except they can not be cancelled. Only one instance of a wait (named or unnamed) can be pending in the wait queue at a time. Unnamed waits are assigned explicit names.

    In the example above it does not matter how many times the program loops in one second. The shutdown routine only executes once.

    This code pulses 4 relays in one second intervals.
    BUTTON_EVENT[dvPANEL,1]
    {
      PUSH:
      {
        WAIT 10
          PULSE[dvRELAY,1]
        WAIT 20
          PULSE[dvRELAY,2]
        WAIT 30
          PULSE[dvRELAY,3]
        WAIT 40
          PULSE[dvRELAY,4]
      }
    }
    

    This code pulses relay 1 only because all waits have the same name.
    BUTTON_EVENT[dvPANEL,2]
    {
      PUSH:
      {
        WAIT 10 'Delay'
          PULSE[dvRELAY,1]
        WAIT 20 'Delay'
          PULSE[dvRELAY,2]
        WAIT 30 'Delay'
          PULSE[dvRELAY,3]
        WAIT 40 'Delay'
          PULSE[dvRELAY,4]
      }
    }
    

    This code pulses 4 relays in one second intervals becasue the named waits are nested.
    BUTTON_EVENT[dvPANEL,1]
    {
      PUSH:
      {
        WAIT 10 'Delay'
        {
          PULSE[dvRELAY,1]
          WAIT 10 'Delay'
          {
            PULSE[dvRELAY,2]
            WAIT 10 'Delay'
            {
            PULSE[dvRELAY,3]
            WAIT 10 'Delay'
              PULSE[dvRELAY,4]
            }
          }
        }
      }
    }
    
Sign In or Register to comment.