String Parsing

Please follow-up with whether you were using the ARL or ARD serial port instructions on an older CompactLogix with a built-in Channel 0 serial port, or if you were using the 1769-ASCII module, which has delimiter configurations in its port setup.

I think you were almost there in a previous post where you had <CR> (the default) as the terminator, but you needed <LF> as the terminator because both characters came at the end of every line.

For fun, you can experiment with the FIND instruction, which will locate a string inside another string. Make a search string that is just a comma, then see what position it tells you the comma is in.

When I use FIND and MID, I usually have to test to be sure I get the offsets versus the indexes correct.

The first character of a string is TagName.Data[0], but it is considered "position" 1.




Ken, As I can see strings are easy for you.



How one can extract several different numbers to array of int with find_int and MID_int?


If we have original string like this +00123.+00345.+00457.+00563 ... +00743


We want extract all number at once to integer.


so array should be like this
123
345
457
563
...
...
743


Find_int (.) finds first character, but for looking other "." Find_int should run several times. But for finding next ones, first finded should be deleted or replaced with different characters or first one is finded allways.

I think that left_int and right int is needed and several diffrent loops or PLC cycles, but I can't get it easily to work. o_O
 
Last edited:
@Lare, rather than repeatedly use find and mid to walk through the string finding sub-strings to pass to stod, perhaps an approach that looks at the input string one character at a time and finds sequences of [sign + digits] might be simpler; see below.


Whether this is done in a loop in one scan or an implied loop over several scans, one scan per character, is a design choice that probably depends on how frequently a new string comes in.


Summary:

  • [inp] an array of SINTs that contains the the string
  • [LENGTH] is the number of characters in the string
  • [str_off] is an offset into SINT array/string [inp] that starts at 0 and is incremented until it is LENGTH i.e. past the end of the string
  • [arr] is an array of integers to receive the numbers parsed from the [inp] string
  • [arr_offset] is an offset into [arr] that starts at -1 and is incremented at the start of each number
  • we are only interested in 12 characters
    • two signs (+ or minus = ASCII 43 and 45)
    • 10 decimal digits ('0' to '9' = ASCII 48 to 57).
  • if we assume all numbers start with a sign, then finding a sign character (+ or -) can be the trigger to initiate the next integer by
    • incrementing [arr_off] by 1
    • putting a 0 in [arr[arr_off]]
  • There should be a string of digits after a sign character
    • each new digit increases the base multiplier of the previous digits by ten i.e.
      • arr[arr_off] = 10 * arr[arr_off] + new_digit
  • The decimal point character [.] can be ignored
Screenshot_2021-05-21_08-13-59.png


Code:
   _______     _______
--[MOV    ]---[MOV    ]--
  [0      ]   [-1     ]
  [str_off]   [arr_off]
   -------     -------

 TheLoop       ____________    ____        _______    ____________
--[LBL]----+--[EQU         ]--[MOV ]--+---[ADD    ]--[MOV         ]--
           |  [inp[str_off]]  [-10 ]  |   [arr_off]  [0           ]
           |  [43          ]  [mult]  |   [1      ]  [arr[arr_off]]
           |   ------------    ----   |   [arr_off]   ------------
           |   ____________    ____   |    -------
           +--[EQU         ]--[MOV ]--+
              [inp[str_off]]  [10  ]
              [45          ]  [mult]
               ------------    ----
   _______    ____________    ____________    ____________    ____________    ____________
--[GRT    ]--[GEQ         ]--[LEQ         ]--[MUL         ]--[ADD         ]--[SUB         ]--
  [arr_off]  [inp[str_off]]  [inp[str_off]]  [arr[arr_off]]  [arr[arr_off]]  [arr[arr_off]]
  [-1     ]  [48          ]  [57          ]  [10          ]  [inp[str_off]]  [48          ]
   -------    ------------    ------------   [arr[arr_off]]  [arr[arr_off]]  [arr[arr_off]]
                                              ------------    ------------    ------------
   ____________    ____________    _______
--[ADD         ]--[LES         ]--[JMP    ]--
  [str[str_off]]  [str[str_off]]  [TheLoop]
  [1           ]  [LENGTH      ]   -------
  [str[str_off]]   ------------
   ------------
 
Last edited:
STOR or STOD seems a lot easier to me, what am I missing?


Oh I agree that is the case for the parsing a single string once it is extracted with MID.

But for a sequence of ASCII numbers with signs and numbers, and especially other characters, just two rungs triggered by only two types of characters (sign or digit), and only ever incrementing offsets by 1, might be simpler than managing start and stop indices with FIND and MID.


Although if each sub-string is the same length, as in @Lare's example, so the start offset is incrementd by 6 each time and the ASCII nubmer's length is always 5, then yes ADD/MID/STOD in a loop is the way to go.
 
Last edited:
@Lare, rather than repeatedly use find and mid to walk through the string finding sub-strings to pass to stod, perhaps an approach that looks at the input string one character at a time and finds sequences of [sign + digits] might be simpler; see below.


Whether this is done in a loop in one scan or an implied loop over several scans, one scan per character, is a design choice that probably depends on how frequently a new string comes in.


Summary:

  • [inp] an array of SINTs that contains the the string
  • [LENGTH] is the number of characters in the string
  • [str_off] is an offset into SINT array/string [inp] that starts at 0 and is incremented until it is LENGTH i.e. past the end of the string
  • [arr] is an array of integers to receive the numbers parsed from the [inp] string
  • [arr_offset] is an offset into [arr] that starts at -1 and is incremented at the start of each number
  • we are only interested in 12 characters
    • two signs (+ or minus = ASCII 43 and 45)
    • 10 decimal digits ('0' to '9' = ASCII 48 to 57).
  • if we assume all numbers start with a sign, then finding a sign character (+ or -) can be the trigger to initiate the next integer by
    • incrementing [arr_off] by 1
    • putting a 0 in [arr[arr_off]]
  • There should be a string of digits after a sign character
    • each new digit increases the base multiplier of the previous digits by ten i.e.
      • arr[arr_off] = 10 * arr[arr_off] + new_digit
  • The decimal point character [.] can be ignored
View attachment 58373


Code:
   _______     _______
--[MOV    ]---[MOV    ]--
  [0      ]   [-1     ]
  [str_off]   [arr_off]
   -------     -------

 TheLoop       ____________    ____        _______    ____________
--[LBL]----+--[EQU         ]--[MOV ]--+---[ADD    ]--[MOV         ]--
           |  [inp[str_off]]  [-10 ]  |   [arr_off]  [0           ]
           |  [43          ]  [mult]  |   [1      ]  [arr[arr_off]]
           |   ------------    ----   |   [arr_off]   ------------
           |   ____________    ____   |    -------
           +--[EQU         ]--[MOV ]--+
              [inp[str_off]]  [10  ]
              [45          ]  [mult]
               ------------    ----
   _______    ____________    ____________    ____________    ____________    ____________
--[GRT    ]--[GEQ         ]--[LEQ         ]--[MUL         ]--[ADD         ]--[SUB         ]--
  [arr_off]  [inp[str_off]]  [inp[str_off]]  [arr[arr_off]]  [arr[arr_off]]  [arr[arr_off]]
  [-1     ]  [48          ]  [57          ]  [10          ]  [inp[str_off]]  [48          ]
   -------    ------------    ------------   [arr[arr_off]]  [arr[arr_off]]  [arr[arr_off]]
                                              ------------    ------------    ------------
   ____________    ____________    _______
--[ADD         ]--[LES         ]--[JMP    ]--
  [str[str_off]]  [str[str_off]]  [TheLoop]
  [1           ]  [LENGTH      ]   -------
  [str[str_off]]   ------------
   ------------


Why there is need of subract number 48 (string 0) from array?
 
Why there is need of subract number 48 (string 0) from array?


Short answer

It's a quick and dirty way to convert the SINT values (ASCII codes 48 through 57) for characters '0' though '9' to the values they represet (0 through 9).

Long answer


Characters '0' and '9' are ASCII codes (SINTs) 48 and 57; character '1' is ASCII code 49; character '2' is code 50; etc.

The code wants to add a integer values of 0 or 9 for the characters '0' or '9' respectively, but the SINT values in the [inp] string are 48 or 57 respectively.

So subtracting 48 converts the ASCII codes (SINTs) for the characters '0' through '9' to the numbers, 0 to 9, that they represent.

That "trick" and the multiply by 10 for each decimal digit replace the STOD instruction, which likely does essentially the same thing.

It's a good thing we are not using EBCDIC for the strings.
 
Last edited:
...
If we have original string like this +00123.+00345.+00457.+00563 ... +00743

We want extract all number at once to integer.
N.B. this has not been tested.

STRING [str] contains input data 'snnnnn.snnnnn.+nnnnn.snn...'

  • where the n's are decimal digits, and the s's are + or -
Set array length to 0
Code:
   _______
--[MOV    ]--
  [0      ]
  [arr_off]
   -------
Run the loop as long as

  • There are at least 7 characters in [str ]
  • AND
  • The seventh charater is a decimal point (ASCII code 46)
On each pass of the loop, do the following

  • Use MID to extract the first six characters to STRING [dst]
    • Assumed to be snnnnnn e.g. +00123
  • Parse those six characters in [dst] to the current position [arr_off] in integer array arr
  • Delete the first 7 characters of [str], place result back in [str]
  • Increment the array length/position [arr_off]
  • Repeat the loop
Code:
 TheLoop  _______    ___________       ___    ____________
--[LBL]--[GRT    ]--[EQU        ]--+--[MID]--[STOD        ]--+--
         [str.LEN]  [str.DATA[6]]  |  [str]  [dst         ]  |
         [6      ]  [46         ]  |  [6  ]  [arr[arr_off]]  |
          -------    -----------   |  [0  ]   ------------   |
                                   |  [dst]                  |
                                   |   ---                   |
                                   |   ______     _______    |
                                   +--[DELETE]---[ADD    ]---+
                                   |  [str   ]   [arr_off]   |
                                   |  [7     ]   [1      ]   |
                                   |  [0     ]   [arr_off]   |
                                   |  [str   ]    -------    |
                                   |   ------                |
                                   |   _______               |
                                   +--[JMP    ]--------------+
                                      [TneLoop]
                                       -------
 
Last edited:
N.B. this has not been tested.

STRING [str] contains input data 'snnnnn.snnnnn.+nnnnn.snn...'

  • where the n's are decimal digits, and the s's are + or -
Set array length to 0
Code:
   _______
--[MOV    ]--
  [0      ]
  [arr_off]
   -------
Run the loop as long as

  • There are at least 7 characters in [str ]
  • AND
  • The seventh charater is a decimal point (ASCII code 46)
On each pass of the loop, do the following

  • Use MID to extract the first six characters to STRING [dst]
    • Assumed to be snnnnnn e.g. +00123
  • Parse those six characters in [dst] to the current position [arr_off] in integer array arr
  • Delete the first 7 characters of [str], place result back in [str]
  • Increment the array length/position [arr_off]
  • Repeat the loop
Code:
 TheLoop  _______    ___________       ___    ____________
--[LBL]--[GRT    ]--[EQU        ]--+--[MID]--[STOD        ]--+--
         [str.LEN]  [str.DATA[6]]  |  [str]  [dst         ]  |
         [6      ]  [46         ]  |  [6  ]  [arr[arr_off]]  |
          -------    -----------   |  [0  ]   ------------   |
                                   |  [dst]                  |
                                   |   ---                   |
                                   |   ______     _______    |
                                   +--[DELETE]---[ADD    ]---+
                                   |  [str   ]   [arr_off]   |
                                   |  [7     ]   [1      ]   |
                                   |  [0     ]   [arr_off]   |
                                   |  [str   ]    -------    |
                                   |   ------                |
                                   |   _______               |
                                   +--[JMP    ]--------------+
                                      [TneLoop]
                                       -------




Got it working whit find, mid_int and replace blocks.
For some reason deleting gived sometimes errors, and program finded also on next scan string which should have be deleted allready from original. (One scan cycle issues, I think.)


Another problem was different PLC platform. It won't accept string to number, if there was non numerical character on string. ;)


String converting was also time comsuming. I can only make 2-8 string to int conversions / PLC cycle. Otherwise scan time goes pretty big.

String conversion even halted PLC couple times as cycle time goed over 255ms if there was over 50 conversions / loop.


I didn't tryed 48 subtract, So not sure if it would make scan time much faster.

For 48 subtract I need move variable with indirect commands. This PLC don't like different variable type moves or copies by default.
 
Last edited:
String converting was also time comsuming. I can only make 2-8 string to int conversions / PLC cycle. Otherwise scan time goes pretty big.

String conversion even halted PLC couple times as cycle time goed over 255ms if there was over 50 conversions / loop.
Instead of a loop, do one conversion per scan. That will take some extra code to determine state: when new data arrive (assign 0 to array offset); when to parse and advance offset; when to do nothing.

If sign (+ or -) cause string-to-number parse to fail, then MID instruction Length and Start should be 5 and 1 instead of 6 and 0.

Moving the string is not necessary and is definitely slower; simply initializing an integer pointer to 1 and incrementing it by 7 each time, using that pointer as the Start parameter by the MID instruction, stopping when the pointer is less than [str.LEN - 5], and dropping the DELETE would work as well.
 

Similar Topics

Hi folks, I'm trying to parse a binary string on a Red Lion DA30D using a Raw UDP/IP input port. I've done this before with ASCII strings so I do...
Replies
38
Views
984
Hi all, I am reading in serial data from a Cognex Dataman 2D matrix reader into a SLC 5/04 serial port (channel 0 - user). I have successfully...
Replies
3
Views
3,852
Hello, I am using studio 5000 pro and am trying to figure out the structured text. Here's my scenario: An operator scans a barcode, the barcode...
Replies
15
Views
238
I am creating a global object in FTView SE 13. I want to have a string read from the PLC and display (in this case the tagname). So lets say...
Replies
4
Views
171
So I had an odd request from a customer for the above. I have written the logic and tested it all in one PLC with only using 7 outputs and 7...
Replies
15
Views
428
Back
Top Bottom