Home AMX User Forum AMX General Discussion

String Arrays

When parsing a lot of data and the strings (E.G. from a security system) are very similar, can you do this...

DEFINE_CONSTANT
CHAR strZONE_ID [xxx] [9]	=	{
				'[C01:X01]',  '[C01:X02]',  '[C01:X03]' .... etc
				}



and then when parsing the buffer from the device...
....
{
STACK_VAR CHAR BufferReply
BufferReply = REMOVE_STRING ( DeviceBuffer, "13", 1 )
STACK_VAR INTEGER nZoneIndex
nZoneIndex = GET_LAST (strZONE_ID)
	{
	SELECT
		{
		ACTIVE FIND_STRING ( BufferReply, strZONE_ID [ nZoneIndex ], 1 ) :
			{
			SEND_COMMAND dvTP_PORT_x, " '^TXT-', nZoneIndex, ',0, THIS ZONE IS IN ALARM, EVACUATE IMMEDIATELY!' "
			}
		}
	}
} ...

Or do I really need to start using structures?

Comments

  • viningvining Posts: 4,368
    Nope that's not going to work.

    GET_LAST:
    This function returns the index of the array element that most recently caused an event handler to be triggered.

    If you define an event (in this case a button event) you create an event table based on a button array (channel numbers) and a TP or array of TPs. So when this button event is triggered the get_last will only return the index of the TP used or button used. Even if you have a single TP I like to use a TP_array with a single TP in it just so I can use get_last. Also makes adding TPs to existing code alot easier.

    In your code the use of get_last has no way of figuring out what you're asking since the array you're using isn't part of the event table.
  • Thought there'd be something. The Data_Event is looking for the string strZONE_ID [whatever one it is] and I thought the GET_LAST might return an integer based on which string it was (i.e. the index number) to give me a channel number.

    What I'm trying to achieve is not coding 150-odd SELECT...ACTIVEs (in the DATA_EVENT for the device buffer) based on individual strings that are the same length but cant return me a unique integer (the string could also be something like B02:Z01, B01:Z15, C01:Z15 etc etc) to allow me to easily give some kind of feedback on a panel (i.e. a button relating to an arbitrary zone will flash when in alarm or tamper).

    Maybe I could tie the string indexes to a channel event? I use G_L all the time for channel, button and level events and just thought it might be extended into something like this. A data_event is still an event after all.


    PS
    I just tried to code out a SWITCH...CASE but failed dismally x-$

    Think I need to sleep on it.....
  • a_riot42a_riot42 Posts: 1,624
    What I'm trying to achieve is not coding 150-odd SELECT...ACTIVEs (in the DATA_EVENT for the device buffer) based on individual strings that are the same length but cant return me a unique integer (the string could also be something like B02:Z01, B01:Z15, C01:Z15 etc etc) to allow me to easily give some kind of feedback on a panel (i.e. a button relating to an arbitrary zone will flash when in alarm or tamper).

    That's what a hash is for.
    Paul
  • OK, at the risk of sounding durrr....


    What's a hash?

    :-)
  • HedbergHedberg Posts: 671
    When parsing a lot of data and the strings (E.G. from a security system) are very similar, can you do this...

    DEFINE_CONSTANT
    CHAR strZONE_ID [xxx] [9]	=	{
    				'[C01:X01]',  '[C01:X02]',  '[C01:X03]' .... etc
    				}
    



    and then when parsing the buffer from the device...
    ....
    {
    STACK_VAR CHAR BufferReply
    BufferReply = REMOVE_STRING ( DeviceBuffer, "13", 1 )
    STACK_VAR INTEGER nZoneIndex
    nZoneIndex = GET_LAST (strZONE_ID)
    	{
    	SELECT
    		{
    		ACTIVE FIND_STRING ( BufferReply, strZONE_ID [ nZoneIndex ], 1 ) :
    			{
    			SEND_COMMAND dvTP_PORT_x, " '^TXT-', nZoneIndex, ',0, THIS ZONE IS IN ALARM, EVACUATE IMMEDIATELY!' "
    			}
    		}
    	}
    } ...
    

    Or do I really need to start using structures?


    If I understand what you are doing:

    You are receiving a response from a device and you are placing that response in a string variable named BufferReply. You want to find the index number of the row in your string array (strZONE_ID) that matches the string in BufferReply and use that index number to identify the zone.

    You can use a for loop to do this:
    nZoneIndex = 0
    for(i = 1; i < xxx + 1, i++)
    {
      if( find( BufferReply, strZONE_ID [i], 1 ) )
         nZoneIndex = i
    }
    if(nZoneIndex)
      SEND_COMMAND dvTP_PORT_x, " '^TXT-', nZoneIndex, ',0, THIS ZONE IS IN ALARM, EVACUATE IMMEDIATELY!' "
    
    

    I think the above will do what you want, I don't see the need for the select:active.

    "hash" is a word used by computer science professionals to demonstrate that they know more than engineers. I think that in the current case a "hash" would be an array of index numbers that would indicate how the strings in your array would map to the zones in the system. You are obviating the need for this by putting your string names in your array in zone order: i.e. the index numbers match the zone numbers. Of course, I'm an engineer and not a computer science professional so I can't be sure that this explanation of "hash" was what was intended.

    Google "hash table" and you'll find better explanations.
  • a_riot42a_riot42 Posts: 1,624
    OK, at the risk of sounding durrr....


    What's a hash?

    :-)

    Google would be the place to turn since the subject is so huge, but for instance you could sum all the characters in the incoming command to get a unique or almost unique integer that would tell you which command came in and respond accordingly. This saves the use of loops with text finding functions in them that are typically slow.
    Paul
  • Spire_JeffSpire_Jeff Posts: 1,917
    If you are parsing security data and there is a halfway decent protocol in use by the security panel manufacturer, you should be able to break the message apart logically and get the zone with code. If you share the protocol you are dealing with, I can try to suggest a method for doing this.

    This way, instead of doing 150 select..actives for each message type, you do a switch..case based on the message type and handle it appropriately using the zone number you parsed out of the message.

    A quick example:
    data_event[dvSecurity]{
    string:{
        stack_var integer nZone;
        stack_var char sMessage[100];
        stack_var char sAlarmCode[25];
        while(find_string(sIncData,END_OF_MESSAGE,1)){
          sMessage = remove_string(sIncData,END_OF_MESSAGE,1)
          remove_string(sMessage,START_OF_MESSAGE,1);
          sAlarmCode = remove_string(sMessage,':',1);
          nZone = atoi(sMessage);
          switch(sAlarmCode){
             case '[C01:':{
               SEND_COMMAND dvTP_PORT_x, " '^TXT-', nZone, ',0, THIS ZONE IS IN ALARM, EVACUATE IMMEDIATELY!' "
             }
             case '[D01:':{
               SEND_COMMAND dvTP_PORT_x, " '^TXT-', nZone, ',0, THIS ZONE IS FINE!!!!!' "
             }
          }
        }
    }
    }
    

    Jeff
  • a_riot42a_riot42 Posts: 1,624
    Hedberg wrote: »
    nZoneIndex = 0
    for(i = 1; i < xxx + 1, i++)
    {
      if( find( BufferReply, strZONE_ID [i], 1 ) )
         nZoneIndex = i
    }
    if(nZoneIndex)
      SEND_COMMAND dvTP_PORT_x, " '^TXT-', nZoneIndex, ',0, THIS ZONE IS IN ALARM, EVACUATE IMMEDIATELY!' "
    
    

    I think here you might want a 'break' in the for loop once the string has been found. No point in continuing to search once you have found what you are looking for.
    Paul
  • HedbergHedberg Posts: 671
    a_riot42 wrote: »
    I think here you might want a 'break' in the for loop once the string has been found. No point in continuing to search once you have found what you are looking for.
    Paul

    True enough, though it won't save much time unless the array is very large and then you would probably want to do something other than search an array with a for loop.
  • a_riot42a_riot42 Posts: 1,624
    Hedberg wrote: »
    True enough, though it won't save much time unless the array is very large ....

    or the code gets executed often. Not sure which security system the OP is using but the ones I end up working with constantly spew so much feedback that an efficient parsing routine is necessary. Calling find_string 150 times for every string that the security system returns would likely slow down panel feedback I would think. YMMV.
    Paul
  • Its a Concept 4000 with about 150 inputs (doors/PIRs) and since they've stopped doing the module for it it's down to muggins 'ere to write something that makes it work (for MONITORING only, mind).

    The For loop did occur to me last night but it was far too late for me to elaborate on it.

    The format concept gives is something like "preamble, date, time, input ID that triggered the last string (in e.g. C01:Z08 format - which means Controller Module 01, Zone input 8 - the first letter can change according to the module it is and the second letter according to the TYPE of input (e.g. standard, auxiliary, others) ) and then On or Off, and when its in Alarm the "Alarm" comes BEFORE the ID. There are two modes of responses, the first is Full Text which says literally "East Corridor 14 Alarm" or "John Smith opened the Fire Exit Door 12 at such and such a time" which is actually difficult to parse because of such variance in string length, not to mention the client could easily change the name of Zones. The second is ID only, which is what I'm using and is a lot more consistent.

    When the client confirms the order I will try these different methods out (I've obviously already got comms) and use a bit of trial and error to find the one which is most efficient.

    Thanks everyone for the responses!
  • viningvining Posts: 4,368
    I would go the route that Spire_Jeff suggested and parse the returned string and break it down into its seperate componants and then handle it based on module type, module number and zone.

    If you think about what the find_string command has to do to find a match it's a very laborious process. Short strings are obviously less laborious than long strings so when using find string it's best to use the shortest string possible that will return what you want.

    If you can avoid putting find_string in a loop which it this case isn't even needed why do it.
Sign In or Register to comment.