Home AMX User Forum AMX General Discussion

NX masters do something at a specific time

Dear All,
since AMX suggests the DEFINE_PROGRAM section to not be used any more, how should we make the code to do something at a specific time ?

For example:

//Older master NI

DEFINE_PROGRAM

if ( TIME = '19:00:00')
//do something

How can this be implemented on the new NX master ?

Thanks,
George

Comments

  • Create a timeline that repeats every second, and check for time in the timeline_event.
  • John NagyJohn Nagy Posts: 1,734
    Note also that
    if ( TIME = '19:00:00')
    //do something
    is likely to occasionally fail no matter where you put it. You are asking to check the time for a 100'th of a second match. If you don't happen to be checking at that hundreth, that is, if you are busy at that precise moment, you miss it.
  • I have tried using a timeline but there are cases that I have missed the execution especially, if master is busy when run a lot of code
    To avoid missing anything, is it possible to check a range of time ? For example from '19:00:00' to '19:00:02'
  • Grabbed this out of working code. It resets the system @ 2am and then powers things back up 10 minutes later. You can see how to use it to adjust to your needs. I'm pretty sure I grabbed this method(TIME_TO_HOUR... etc) from this forum...
    DEFINE_VARIABLE
    
        PERSISTENT INTEGER nEndofDayFlag    = 0
    
    TIMELINE_EVENT[POLLING_TIMELINE]{
    
    IF(TIME_TO_HOUR(TIME) == 2 && TIME_TO_MINUTE(TIME) == 0 && !nEndofDayFlag){
        nEndOfDayFlag = 1
        fnShutDownWatchouts()
        fnBuildingMode(0)
        //Lights Out
        toDev(dvLighting,"'P1L0T',ITOA(LX_FADE_TIME)")
        toDev(dvLighting,"'P2L0T',ITOA(LX_FADE_TIME)")
        toDev(dvLighting,"'P3L0T',ITOA(LX_FADE_TIME)")
        //Camera Power
        toDev(vdvCam1,'POWER-OFF')
        WAIT 50{
            fnRoomPower(6,0)
        }
        WAIT 200{
            REBOOT(0)
        }
    }
    IF(TIME_TO_HOUR(TIME) == 2 && TIME_TO_MINUTE(TIME) == 10 && nEndofDayFlag){
        nEndOfDayFlag = 0
        toDev(vdvCam1,'POWER-ON')
        fnWakeUp()
        WAIT 2200{
            toDev(vdvWatchout,'RELOAD-')
            fnRoomPreset(6,61)
            fnRoomPower(6,1)
            toDev(vdvCam1,'CAMERAPRESET-7')
        }
    }
    
    }
    
  • ericmedleyericmedley Posts: 4,177
    I have tried using a timeline but there are cases that I have missed the execution especially, if master is busy when run a lot of code
    To avoid missing anything, is it possible to check a range of time ? For example from '19:00:00' to '19:00:02'

    I feel the timeline suggestion is the best idea to handle this. However, I think you'll find things work better if you convert the time to an integer. the way I do this for events firing with 1 minute accuracy is to convert the time to X minutes past midnight. You use the following formula. nDigital_Time= ((abs_value(time_to_hour(time))*60) + abs_value(time_to_minute(time)) )

    While this may seem a bit of overkill - If you do it it allows you to now use all kinds of math operators to be pretty clever in choosing times and/or time ranges. For example:
    If I want to turn on my Christmas lights at 5:30PM and the off at 12:00AM I can create a range of time from 1050 minutes up to midnight (which is zero)

    For example: 5:30PM (17:30 military) is (17 * 60 or 1020) plus 30 minutes past midnight. (1050)

    I can use a simple "If time is >= 1050" turn the lights on if not already on" routine. The nice thing about this method is not only does it work to make the timed event but also will update the system right after a reboot or if something happens to the light that shuts them off by accident. I've gotten where I can do the math pretty easily in my head but a simple calculator is hand for more oddball times.

    There are cases also where I also bring the accuracy down to the second. However, this results in a number too big for a standard integer and have to flop over to using a LONG. But the principle is the same. I've even written a little function that allows me to send in the time in human readable form and it converts it to a decimal-past-midnight value for me.

    Like I say - at first glance it seems like a lot of work. But, having the availability of simple math operators to handle time is so clean and powerful it's worth the trouble.

    I also use this method for more complex systems. I did a system for a hotel that had several audio zones and several background music sources that they wanted to schedule when a zone went on or off, what music source to play, and at what volume to come on at and also the abiltiy to just raise/lower the volume at a given time. To make matters more complex, they also wanted all this to be editable on the fly.

    So, I just made the whole mess into a minutes-past-midnight system and made an unordered event list of 'stuff to happen' The entire thing is nothing more than equals/greater-than/less-than math operators built into the logic. They can have as many events as they want doing whatever they want without dealing with the crazy logic and string chopping of using human-readable time.

    My timeline runs once a minute to create the digital time value and at each fire then checks for timed events.
  • hi ericmedley, in case of converting the current time in minutes ( time_to_hour(time)*60 + time_to_minute(time) ) and then compared with my defined time ( I have to do with an integer from 0 - 1440 ) a timeline running every minute will fire ONLY once. It is not possible to fire more times on that minute. Is that correct ? This would be what I like to do...

    timeline_event[TL] //monitor event x every 1 minute
    {
    stack_var integer currentTime

    currentTime = time_to_hour(time)*60 + time_to_minute(time)

    switch ( timeline.sequence ) //every minute
    {
    case 1:
    {
    if ( eventTime = currentTime )
    //do something
    }
    }
    }


    George
  • ericmedleyericmedley Posts: 4,177
    hi ericmedley, in case of converting the current time in minutes ( time_to_hour(time)*60 + time_to_minute(time) ) and then compared with my defined time ( I have to do with an integer from 0 - 1440 ) a timeline running every minute will fire ONLY once. It is not possible to fire more times on that minute. Is that correct ? This would be what I like to do...

    timeline_event[TL] //monitor event x every 1 minute
    {
    stack_var integer currentTime

    currentTime = time_to_hour(time)*60 + time_to_minute(time)

    switch ( timeline.sequence ) //every minute
    {
    case 1:
    {
    if ( eventTime = currentTime )
    //do something
    }
    }
    }


    George


    Well sure there is, but what exactly do you want to do - how often and how many times?

    Let's say, for example, you want an event routine to start at 19:00 hours. At that time you want something to fire 5 times every 15 seconds. I would in this case create another timeline that runs the routine as desired (5 events at 15 second intervals) and simply launch that timeline routine from my timed events timeline.

    Another method of getting there might be to go ahead and make your 1 minute timeline actually run more than once per minute and just trap events you only need to happen once but let the ticking of the timeline time out an event like you describe.

    Since the thing your describe is simply an event with multiple steps I would think making it its own separate routine that is easily called whenever you want to run the whole thing would make more logical sense. so using your code example:
    timeline_event[TL] //monitor event x every 1 minute
    {
    stack_var integer currentTime
    
    currentTime = time_to_hour(time)*60 + time_to_minute(time)
    
    switch ( timeline.sequence ) //every minute
    {
    case 1:
    {
    if ( eventTime = currentTime )
    // here is where I'd start my 5-event timeline
    }
    }
    }
    
    timeline_event[TL_My_5_Step_Process]{
      switch(timeline.sequence){
        case 1:{ 
             // do step one
           } // case
        case 2:{ 
             // do step two
           } // case
        case 3:{ 
             // do step three
           ​​​​​​​} // case
        case 4:{ 
             // do step four
           ​​​​​​​} // case
        case 5:{ 
             // do step five
           ​​​​​​​} // case
        }// switch
      } // timeline_event
    
  • John NagyJohn Nagy Posts: 1,734
    Another way... without the minutes to midnight conversion:

    Set a 1 minute recurring timeline, In it call your event check.
    In the event check, if actual time is less than a minute AFTER a trigger time, trigger.
    Stack and dynamically manage as many triggers in your event check as you need.

    You have a full minute to get to the timeline and still catch your event. You don't need a DONE flag, because you won't be back to check again within the accepted time window.
  • a_riot42a_riot42 Posts: 1,624
    You can still use define_program if needed, you just don't want to set variables inside it, or call functions that set variables. Unfortunately calling TIME is setting a variable since the time has to be retrieved and then set to the TIME variable inside of define_program, so it will recurse. AMX recommends using a timeline with a 500 ms time to reproduce the same functionality as define_program but without the infinite recursion. So if you need "to the second" timing, I would go that route. If you just need "to the minute" timing, John's suggestion will work.
    Paul
  • John NagyJohn Nagy Posts: 1,734
    Keep in mind even if you go as granular as 500ms (half second) on your time check, you still risk missing your trigger if you insist on a time MATCH to a tenth of a second. Point being, if you are in your loop a few milliseconds late for a task with a specific time, you won't fire. And if you get there just a bit early, not time yet, but a half second later when you try again, same as above, you don't match, no trigger.

    So you must either trigger on a RANGE of time, or more simply as I suggest, "Is it AFTER the Trigger time now (and within a guard time that assures you won't trigger twice)?
  • I wasn't happy with my results using a timeline to shutdown a room at 11pm every night. I ended up creating some code that calculates the seconds to 11pm and then creates a wait with the specified seconds.
    define_function CreateAutoShutDownCheck(){
        local_var char eleven[10]
        local_var slong SecToNextRun
        local_var slong twentyfour
        twentyfour = 86400
        eleven = '23:00:00'
    
        SecToNextRun = sec_dif(time,eleven)
    
    
       send_string 0,"'createwait: ',time,' SecToNextRun: ',itoa(SecToNextRun)"
        if(SecToNextRun <= 0){
            SecToNextRun = twentyfour + SecToNextRun
        }
        send_string 0,"'createwait: ',time,' SecToNextRun: ',itoa(SecToNextRun)"
        SYSLOG("'createwait: ',time,' SecToNextRun: ',itoa(SecToNextRun)")
    
        wait (SecToNextRun + 1) * 10 {
            AutoShutdown()
        }
    }
    
    define_function slong sec_dif(char time1[], char time2[]){
        local_var slong time1hour,time2hour,time1min,time2min,time1sec,time2sec
        local_var slong time1seconds,time2seconds
        time1hour = time_to_hour(time1)
        time2hour = time_to_hour(time2)
        time1min = time_to_minute(time1)
        time2min = time_to_minute(time2)
        time1sec = time_to_second(time1)
        time2sec = time_to_second(time2)
        time1seconds = (time1hour * 60 * 60) + (time1min * 60) + time1sec
        time2seconds = (time2hour * 60 * 60) + (time2min * 60) + time2sec
        send_string 0,"'time1: ',time1,' sec: ',itoa(time1seconds),' time2: ',time2,' sec: ',itoa(time2seconds)"
        return (time2seconds - time1seconds)
    }
    
    define_function AutoShutDown(){
        if(projector_is_on){
            syslog("'AUTOSHUTDOWN=1 PROJ=',itoa(projector_is_on)")
            DO_PUSH(dvTP,90) //shut room down
        }
        else{
            syslog("'AUTOSHUTDOWN=0'")
        }
        CreateAutoShutDownCheck()
    }
    
    define_start
    
    wait 600 CreateAutoShutDownCheck()
    
    
  • dchristodchristo Posts: 177
    John Nagy wrote: »
    Note also that
    if ( TIME = '19:00:00')
    //do something
    is likely to occasionally fail no matter where you put it. You are asking to check the time for a 100'th of a second match. If you don't happen to be checking at that hundreth, that is, if you are busy at that precise moment, you miss it.

    Keep in mind even if you go as granular as 500ms (half second) on your time check, you still risk missing your trigger if you insist on a time MATCH to a tenth of a second. Point being, if you are in your loop a few milliseconds late for a task with a specific time, you won't fire. And if you get there just a bit early, not time yet, but a half second later when you try again, same as above, you don't match, no trigger.

    John,

    I'm confused as to what you're saying here. "19:00:00" is 7pm, no minutes, and no seconds; therefore, isn't the statement "TIME='19:00:00'" true for full second?

    --D
  • John NagyJohn Nagy Posts: 1,734
    You aren't confused, it's me. Yes, a full second, I was seeing that as tenths.
    Same caveat however, be sure you are not going to miss the precise match you seek. If, due to system activity, you don't get around to checking at the exact moment, you don't get what you expect.
  • viningvining Posts: 4,368
    I have one function in my date & time include that is triggered by a 250ms timeline that in turn triggers anything based on seconds, minutes, hours, day of the week, day of the month, etc.
    DEFINE_FUNCTION fnUpdate_Time()
    
         {
         if(sMyTime.nSecond != TYPE_CAST(TIME_TO_SECOND(TIME)) || nTimeWait_Force)
          {
          sMyTime.nSecond = TYPE_CAST(TIME_TO_SECOND(TIME));
          sMyTime.c24Time = TIME;
          if(sMyTime.nMinute != TYPE_CAST(TIME_TO_MINUTE(TIME)) || nTimeWait_Force)
               {
               sMyTime.nMinute = TYPE_CAST(TIME_TO_MINUTE(TIME));
               //only 1 minute resolution for sunrise/set, could check every second but why, not that critical.....
               if(sMyTime.nTime_Seconds >= sMyTime.nSunriseTime && sMyTime.nTime_Seconds < sMyTime.nSunsetTime)
                {
                if(!sMyTime.nDaytime)
                 {
                 sMyTime.nDaytime = 1;
                 fnRun_SunRise_n_Set(sMyTime.nDaytime);
                 }
                }
               else 
                {
                if(sMyTime.nDaytime)
                 {
                 sMyTime.nDaytime = 0;
                 fnRun_SunRise_n_Set(sMyTime.nDaytime);
                 }
                }             
               if(sMyTime.n24Hour != TYPE_CAST(TIME_TO_HOUR(TIME)) || nTimeWait_Force)
                {
                sMyTime.n24Hour = TYPE_CAST(TIME_TO_HOUR(TIME));
                if(sMyTime.n24Hour >= 12)
                 {
                 sMyTime.cAMPM = 'PM';
                 sMyTime.nTimePM_flag = 1;
                 if(sMyTime.n24Hour > 12)
                      {
                      sMyTime.n12Hour = sMyTime.n24Hour - 12;
                      }
                 else
                      {
                      sMyTime.n12Hour = sMyTime.n24Hour;
                      }
                 }
                else
                 {
                 sMyTime.cAMPM = 'AM';
                 sMyTime.nTimePM_flag = 0;
                 sMyTime.n12Hour = sMyTime.n24Hour;
                 }
                sMyTime.c12Time = "itoa(sMyTime.n12Hour),':',RIGHT_STRING("'0',itoa(sMyTime.nMinute)",2),':',RIGHT_STRING("'0',itoa(sMyTime.nSecond)",2)";
                if(sMyTime.nDayOfMonth != TYPE_CAST(DATE_TO_DAY(LDATE)) || nTimeWait_Force)
                 {
                 sMyTime.nDayOfMonth = TYPE_CAST(DATE_TO_DAY(LDATE));
                 sMyTime.nDayOfWeek = TYPE_CAST(DAY_OF_WEEK(LDATE));
                 if(sMyTime.nMonth != TYPE_CAST(DATE_TO_MONTH(LDATE)))//no force here.  would erase stored values.
                      {
                      sMyTime.nMonth = TYPE_CAST(DATE_TO_MONTH(LDATE));
                      if(sMyTime.nYear != TYPE_CAST(DATE_TO_YEAR(LDATE)))
                       {
                       sMyTime.nYear = TYPE_CAST(DATE_TO_YEAR(LDATE));
                       }
                      }
                 if(sMyTime.cDay != Day || nTimeWait_Force)
                      {
                      sMyTime.cDay = Day;
                      if (sMyTime.cDate != Date)
                       {
                       sMyTime.cDate = Date;
                       }
                      }
                 fnGet_DayOccurrences();
                 fnRun_D_O_W_Events(sMyTime.nDayOfWeek);
                 fnRun_D_O_M_Events(sMyTime.nDayOfMonth);
                 fnRun_DailyEvents();
                 }
                fnRun_HourEvents(sMyTime.n24Hour);
                }
               else
                {
                sMyTime.c12Time = "itoa(sMyTime.n12Hour),':',RIGHT_STRING("'0',itoa(sMyTime.nMinute)",2),':',RIGHT_STRING("'0',itoa(sMyTime.nSecond)",2)";
                }
               sMyTime.nTime_Minutes = ((sMyTime.n24Hour * 60) + sMyTime.nMinute);
               fnRun_MinuteEvents(sMyTime.nMinute);
               }
          else
               {
               sMyTime.c12Time = "itoa(sMyTime.n12Hour),':',RIGHT_STRING("'0',itoa(sMyTime.nMinute)",2),':',RIGHT_STRING("'0',itoa(sMyTime.nSecond)",2)";
               }
          sMyTime.nTime_Seconds = ((((sMyTime.n24Hour * 60) + sMyTime.nMinute) * 60) + sMyTime.nSecond);
          fnRun_SecondEvents(sMyTime.nSecond);
          fnFB_Time(UI_UPDATE_ACTIVE,FB_TIME_TIME); //IF ANYTHING NEEDS TIME UPDATES
          }
         nTimeWait_Force  = 0;
         }
    
    The functions called are then further routed to other parts of my code:
    DEFINE_FUNCTION fnRun_SecondEvents(INTEGER iSecond)      
    
         {
         STACK_VAR INTEGER nCopySecond;
    
         nCopySecond = iSecond;
    
         SELECT//add as needed
          {
          ACTIVE(sMyTime.n24Hour == 23):
               {
               if(sMyTime.nMinute == 59)
                {
                if(nCopySecond == 59)
                 {//used for log entry
                 SEND_STRING 0,"'TIME-[ ',TIME,' ].  End day and begin a new!'";
                 }
                }
               }
          }
         fnGet_WebDATE_TIME();
         fnMain_EverySecond(nCopySecond);
         }
    
    This function call is in my date & time include and I have others for minutes, hours, etc and in each I call functions in my main code file that I put job specific code and calls. Using this method I can eliminate a lot of timelines. Whether I want to control TP FB that is not event driven or any sort of timing events, day events, holiday events etc.
Sign In or Register to comment.