Home AMX User Forum NetLinx Studio

Comparing strings

Hello all,

what I'm trying to do is compare a string received from a UPS (after parsing it) with TIME on my NI4100. I don't want only the current time to be checked against the string, but within a 3 minute window.

So if TIME == 08:59:00 and string from UPS contains: 09:01:00, do something.

I'm just wondering what approach people have taken previously, in a channel event's STRING, I have:
ACTIVE (FIND_STRING (UPS1_BUFFER,'UPS: On battery power',1)):
{
REMOVE_STRING(UPS1_BUFFER,"LEFT_STRING(UPS1_BUFFER,FIND_STRING(UPS1_BUFFER,'UPS: On battery power',1)-18)",1)
IF (FIND_STRING(UPS1_BUFFER,DATE,1))
{
//CODE REQUIRED!
}
}

I was thinking COMPARE_STRING, but I can't use TIME to compare the string to because of my 2 minute gap requirement. The only way I can think to do this is to extract the TIME_TO_SECOND, MINUTE and HOUR and store the values in variables, then use this to compare to the string.

By the way, after my REMOVE_STRING, the buffer will look something like:

06/21/2011 09:05:15 UPS: On Batter power in response to an input power problem.

I know there will be some more tweaks to do, all I'm looking for at the moment is an efficient way to compare the time from a string to my master's time.

Thanks in advance,
Nick

Comments

  • PhreaKPhreaK Posts: 966
    Forget string comparison, what you need is math :)

    First off you'll need to put both the time returned from the device and the current time into a single comparable format - I'd recommend using true's unix time library to create a couple of timestamps. From there just check the absolute value of their difference and see if it lies within your bounds.
  • nhightonnhighton Posts: 18
    Thanks,

    yeah I can see how that'd work but it seems a bit long-winded for what I need. How about extracting hour, minute and second from TIME, storing them in variables before concatenating them back together but with wild cards where appropriate.

    I haven't written anything comprehensive yet with it being Monday, but I was thinking pretty much a time parsing function that takes into account the 20 second difference between 50 seconds and 10 seconds. Is that unix timestamp code capable of this?

    My reasoning here being that an offset in accuracy down to about 20 seconds is no big deal.

    Thanks,
    Nick
  • nhightonnhighton Posts: 18
    Come to think of it, with the accuracy being not so much of an issue, I could just use:

    IF(COMPARE_STRING(UPS1_BUFFER,nTIME))
    {
    DO STUFF
    }

    Where nTIME is the time reconcatenated (is that a word?) but with a wildcard in place of seconds. This way, if it misses one instance where the above IF should evaulate to true, it will catch the next.

    This event is regularly scheduled anyway. An AMX SNMP module/APC Link Language would make this so much easier.
  • ericmedleyericmedley Posts: 4,177
    I use a similar method as True's but it's my own. It goes in all my programs since I find I'm always needing to do something that requires a huge headache in timing and converting from the ancient 12 hour/AM/PM clock.

    However, in your case you could probably make a quick version that'll get the gig done by taking the TIME_TO_HOUR and TIME_TO_MINUTE commands and making an integer of the minutes since 12AM midnight.

    n_decimal_Time=(Time_To_Hour(time)*60) + TIME_TO_MINUTE(time)

    This will give you the minutes in a day since midnight and it's easy math at that point to get the plus or minus 3 minute window.

    Believe me, getting the same reliable functionality by using the text is nightmarish. You end up with so many conditionals it'll make your head hurt.

    The only problem with my bandade solution is that it won't work right around midnight since your time will be going from 1439 to 0. So, you'll have to do a little conditional logic around that time to deal with the jump.
  • viningvining Posts: 4,368
    Aslo keep in mind that your code can execute numerous times depending on the time resolution. If your resolution is in seconds the code will evaluate as true for the entire second and could fire a 100 times if in define program. If your res is a minute it will fire 60 times that so you need a flag with a wait or something to block the code from repeating.
  • PhreaKPhreaK Posts: 966
    vining wrote: »
    Aslo keep in mind that your code can execute numerous times depending on the time resolution. If your resolution is in seconds the code will evaluate as true for the entire second and could fire a 100 times if in define program. If your res is a minute it will fire 60 times that so you need a flag with a wait or something to block the code from repeating.
    It looks like the OP's firing this off based on feedback from the device rather than within a timer loop / mainlaine so that shouldn't be an issue.

    @ericmedley
    Modulus should help with the wrap around.

    @nhighton
    Unix time stamps are the number of seconds elapsed since midnight January 1, 1970 (UTC) so any math involved in comparing them is really straight forward.
  • nhightonnhighton Posts: 18
    Thanks for the responses,

    yes it is based on device feedback, but I have to 'poke' the device to make it speak - based on a schedule. I've got it so that the event only triggers once so it's just the time string comparison I needed some advice about.

    IF ((TIME_TO_SECONDS(TIME) % 14 == 0) AND nFLAG == 0)
    {
    WAIT 6
    nFLAG = 1
    //CODE BLOCK
    }

    ...So the code runs at 14 28 42 56 seconds each minute.

    Eric Medley, that system seems good to me. As Phreak said, not as much logic to mess around with apart from the start & end of the 1439 minutes.

    I think that might be about it, once again thanks AMX Forums

    Nick
  • nhightonnhighton Posts: 18
    Using Eric's time count approach - only with seconds as opposed to minutes I have:
    UPS_SECOND_COUNTER = (TIME_TO_HOUR(TIME) * 3600) + (TIME_TO_MINUTE(TIME) * 60) + TIME_TO_SECOND(TIME)
    
    IF ((UPS_SECOND_COUNTER >= 32400) && (UPS_SECOND_COUNTER <= 77400) && (UPS_SECOND_COUNTER % 20 == 0) && (UPS1_DIRTY == 0) && (UPS1_LOGGEDIN == 1) && (UPS2_LOGGEDIN == 1) && (UPS3_LOGGEDIN == 1) && (UPS4_LOGGEDIN == 1))
    {
        UPS1_DIRTY = 1
        WAIT 6
        SEND_STRING UPS,"'',$0D"
        UPS1_DIRTY = 0
    }
    

    This suddenly seems like a taxing IF - yet contains all the checks required before the queries are sent. The rest of the code on the master isn't too heavy ~1900 lines. 4 UPSs that need to be checked between 09:00 and 21:30 every 20 seconds = 9,000 queries and parsing feedback per day. The particular use of AMX here is purely for AV show control, there's an array of 4 Christie 16K projectors (1 UPS for each).

    Is this too taxing to put in define_program based on my brief description?

    Thanks again,
    Nick
  • ericmedleyericmedley Posts: 4,177
    I kinda went all the way around the Define_Program issue. I now write all my code without a single line in D_P. Does code running in DP bog down a system? Yes, but only insomuch as how that code interacts during runtime. I don't want to degrade this discussion to another long-winded treatise on "...to DP or not to DP..."

    In the application you're describing I personally would use a timeline. I'd make a polling TL and check the for the time slots you describe and then do them as events.

    But, that's just me...
  • viningvining Posts: 4,368
    nhighton wrote: »
    Using Eric's time count approach - only with seconds as opposed to minutes I have:
    UPS_SECOND_COUNTER = (TIME_TO_HOUR(TIME) * 3600) + (TIME_TO_MINUTE(TIME) * 60) + TIME_TO_SECOND(TIME)
    
    IF ((UPS_SECOND_COUNTER >= 32400) && (UPS_SECOND_COUNTER <= 77400) && (UPS_SECOND_COUNTER % 20 == 0) && (UPS1_DIRTY == 0) && (UPS1_LOGGEDIN == 1) && (UPS2_LOGGEDIN == 1) && (UPS3_LOGGEDIN == 1) && (UPS4_LOGGEDIN == 1))
    {
        UPS1_DIRTY = 1
        WAIT 6
        SEND_STRING UPS,"'',$0D"
        UPS1_DIRTY = 0
    }
    

    This suddenly seems like a taxing IF - yet contains all the checks required before the queries are sent. The rest of the code on the master isn't too heavy ~1900 lines. 4 UPSs that need to be checked between 09:00 and 21:30 every 20 seconds = 9,000 queries and parsing feedback per day. The particular use of AMX here is purely for AV show control, there's an array of 4 Christie 16K projectors (1 UPS for each).

    Is this too taxing to put in define_program based on my brief description?

    Thanks again,
    Nick
    Given your needs I would create a timeline that starts at 9:30 and is killed at 21:30 which repeats when running every 20 seconds to query the UPS's. Then just keep your IF in the data_event or parsing function that it may call. You could step your IF's so it doesn't have to check all the conditions on every pass of the function especially if your have alot of conditions to check in a single if.

    Here's an example of what I mean by stepping. Basically check 1 condition and if that passes check another. If you're looking for a specfic hour for instance first check for the hour match and then if the hours matches then check for a minute match but don't check for both on every pass. If your checking for seconds in the day match then check for that match before any further conditions are tested.

    If I recall we tested to see if the IF's exit comparsions on the first false evaluation and I think we determined it didn't. So "if(nHour == 12 && nMinute == 15)" doesn't exit if the hour isn't 12 and it will still check the minutes even though the first condition has already evaluated as false. I think spire_jeff ran that test and it would be worth a search to confirm my recollection.

    So in this function which is triggered by a timeline running every 500ms I step my conditions or qualify one condition at a time in an attempt to make it as effecient as possible. In the code below I first check my seconds and if that passes I'll check my minute and then hours and so on.
    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)) ;
    	       if(sMyTime.nTime >= sMyTime.nSunriseTime && sMyTime.nTime < sMyTime.nSunsetTime)
    		    {
    		    sMyTime.nDaytime = 1 ;
    		    }
    	       else
    		    {
    		    sMyTime.nDaytime = 0 ;
    		    }			 
    	       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)" ;
    		    }
    	       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 = ((((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 ;
         }
    
  • a_riot42a_riot42 Posts: 1,624
    Why don't you just compare the strings with operators? IE:

    if((time > '10:00:00' && time < '10:03:00'))
    {
    // do sumpun
    }

    Paul
  • nhightonnhighton Posts: 18
    Because my schedule is every 20 seconds within a given time period. To calculate total seconds accumulatively in a day is easier to schedule every 20 seconds.
Sign In or Register to comment.