A Do-more PLC pulls the ID out of a variable width ASCII transaction by anchoring on text markers. Find Complete with STRFIND, add its 9 characters to the found position, search forward for the next space, and extract the span with STRGETB. Publish the converted number only when both markers and the digit checks pass inside one bounded record.
A transaction feed is dumping ASCII events into the controller's serial port. The event type and the ID both change width, so no column position holds, and the job is to publish the ID to nine peer controllers as a binary number, but only for Complete events on unit 32. The parse that holds is anchor based: bound one record, find Complete and the unit token inside it with STRFIND, add the marker's 9 characters to its position to get the ID start, search forward for the next space, and lift the characters in between with STRGETB. Validate the span, convert it with STR2INT, and let PEERLINK publish. The steps below carry the arithmetic and the checks that catch the off-by-one and framing errors that break this parse.
The answer: anchor on the markers, not the columns
Fixed columns assume every field keeps its width. On this feed the event type runs from Load at 4 characters up to Complete at 8, the ID runs from 1 to 5 characters, names carry spaces, and the unit number moves across 1 to 64. Every offset moves with them.
Text anchors stay put. The feed puts a stable label in front of the field you need, so the parse has three moves: bound one record, find two markers inside that same record, and take the characters between the end of the Complete marker and the next space. The ID start is the marker position plus the marker length. The ID end is the next space after that start. The length is ID end minus ID start. STRGETB lifts the span, the checks qualify it, STR2INT converts it, and PEERLINK publishes it.
What you need
- A byte level capture of the feed with control characters visible. The printed samples hide line endings and tidy the spacing, so the capture is the authority for the record terminator and for the exact spaces between fields. One sample event even carries a mangled time field, 21 07:32, worth remembering before anyone anchors a parse on the date and time text.
- The controller port matched to the sender's baud, data bits, parity and stop bits, the same fixed-settings discipline as tying an HMI to a PLC port with fixed serial settings.
- ASCII receive mode on the port, so incoming bytes collect in the input buffer while the scan keeps running; the port is full duplex and can receive and send at the same time. A stream input move transfers the bytes into a string element, and parsing Do-more strings with STREAMIN, STRFIND and STRPRINT walks through that move.
- Three instructions from the Do-more Designer instruction list: STRFIND finds one string inside another, STRGETB gets bytes out of a string, STR2INT converts a numeric string to an integer.
- The marker strings exactly as the capture shows them:
Completeat 9 characters with its trailing space, and the unit tokenUnit 32at 7 characters. - PEERLINK, already in place, to publish to the other nine controllers.
Bound one record before you search
A serial port hands the program a byte stream with no record edges of its own. One event can arrive in two reads, and two events can be sitting in the input buffer after a single read. Searching that stream before the edges are known is exactly what matches Complete in one event against Unit 32 in the next and publishes a number for an event that never happened.
The terminator sets the edges. Most line oriented ASCII feeds end each event with a carriage return, a line feed, or the pair; the capture shows which byte it is, and that byte is the boundary. Accumulate into a record buffer until the terminator arrives, treat everything up to it as one record, and park the bytes behind it for the next pass. That boundary-first habit is the same discipline as parsing RS-232 data as frames, not digit runs.
Two cases need their own handling:
- An oversize record. If the bytes between two terminators run past the record buffer, enter a discard state and drop bytes until the next terminator arrives. While the discard state holds, publish nothing, and never let the overflowed tail become the start of an ID field. The next terminator restores the record boundary, and the first complete record after it parses normally.
- An empty read during recovery. A scan that finds no new bytes while the buffer is recovering changes nothing: the discard state holds and accumulation resumes on the next read.
Publication stays blocked until one complete record bounded by the terminator has passed through after any discard. That is what keeps a broken line from leaving a fragment that gets published as a number.
The parse, step by step
Run these on one completed record per scan.
- Take the bytes from the previous terminator up to the next one. Nothing below runs on a partial record.
- STRFIND the record for
Complete, 9 characters including the trailing space. No match ends the scan with nothing published: the event is a Load or something else. - STRFIND the same record for
Unit 32, 7 characters, and check that the character after the token is a space or the record end, so the match only counts when it ends at a field boundary. If the target unit changes from job to job, build the token from the number instead of hard codingUnit 32. - Compute the ID start: the
Completeposition plus 9. - Search again from the ID start for the next space. That position is the ID end, and the ID length is ID end minus ID start. A length of 0 or more than 5 characters rejects the record.
- STRGETB lifts the span: start at the ID start and take Length characters. Check that the first and the last extracted character are digits, ASCII 48 to 57. The check also traps an off-by-one: a leading space means the start is one low, a trailing space means it is one high.
- STR2INT converts the digit span to a binary integer. Confirm the value sits inside 1 to 99,999.
- Enable PEERLINK publication for this record only when steps 2 through 7 all passed.
The manual prints whether string positions count from 0 or from 1. The method is identical under either convention, and the digit check in step 6 catches the wrong one.
Leave the parser as the only writer of the published register, and keep the two C-More screens and the nine peers on the read side. A second writer on that register is the usual reason a value changes back right after it is set, and find what owns the tag shows how to run the cause down.
If a screen also needs the raw event text, packing that ASCII into memory words for a tag is its own job, separate from this parse.
Worked example: extract 110 from the 21:05:17 Complete record
Inputs. The record is 2012-07-25 21:05:17 Complete 110 Mary Jane Unit 32, 50 characters with one space between fields. The event text comes from the feed's sample records, and the separator count comes from your own capture; this example assumes single spaces. Marker 1 is Complete at 9 characters, marker 2 is Unit 32 at 7 characters, the ID limit is 1 to 5 characters and 1 to 99,999, and the digit codes run 48 to 57.
Rule. ID start equals the Complete position plus 9. ID end equals the next space after that start. Length equals ID end minus ID start. STRGETB takes Length characters at the ID start.
Arithmetic, counting positions from 1:
- STRFIND for
Completereturns 21: the C is character 21 and the marker's trailing space is character 29. - ID start = 21 + 9 = 30, which lands on the first digit of the ID.
- The space search from character 30 finds a space at 33.
- Length = 33 - 30 = 3.
- STRGETB with start 30 and length 3 returns
110. - STRFIND for
Unit 32returns 44, inside the same record, and the record ends at character 50, so the token ends at a field boundary. - The three extracted bytes are 49, 49 and 48 in ASCII, all inside 48 to 57. STR2INT gives 110, inside 1 to 99,999.
Result. PEERLINK publishes 110 as a binary integer to the nine peers, and nothing else about the record travels.
Decision. The publish gate is: Complete found, Unit 32 found in the same record, length 1 to 5, every byte a digit code, value 1 to 99,999. The feed's first sample, a Load of 2724 on unit 32, fails at step 2 and publishes nothing. The third sample, a Load of 42467 on unit 2, fails both the marker and the unit token. A Complete event for a different unit fails only the unit token, which is what keeps its transactions off the peers when the target is unit 32.
How to check it worked
Put these records through the parse and compare against the expected publish:
- The feed's three sample events: Load 2724 unit 32, Complete 110 unit 32, Load 42467 unit 2. Expected publishes: none, 110, none.
- A Complete record with a 1 character ID, then one with a 5 character ID such as 99999. Both publish, which proves both ends of the 1 to 5 limit.
- A Complete record with two spaces between Complete and the name, so the ID span would be empty. Nothing publishes; the length check rejects 0.
- One event split across two reads by the terminator timing. Nothing publishes until the terminator lands and both markers sit inside one bounded record.
- A record longer than the buffer, with no terminator inside it. Nothing publishes, the discard state runs to the next terminator, and the following good record publishes with no fragment of the broken one inside its fields.
Watch the extract on every pass. A digit at both ends of the span, with no spaces, means the offsets are right.
Recommendation: Keep PEERLINK as the distribution path and gate it on the full chain: one bounded record, Complete found, Unit 32 found in the same record, an extracted span of 1 to 5 characters, every byte an ASCII digit, and a converted value inside 1 to 99,999. Build the record boundary from the terminator in your own capture before any of that runs, because a marker match across two events is the failure mode this feed sets up. Parse on the controller that owns the serial port, and let the screens and the peers stay readers.
Questions
What changes if the capture shows two spaces after Complete?
Make the marker match the captured bytes. Either put both spaces into the search string, which makes it 10 characters and shifts the ID start arithmetic by one, or search Complete alone and step over the leading spaces before the first digit. Either way the ID start comes from the counted marker length, and the digit check on the extract catches a miscount: a leading space means the start is one low, a trailing space means it is one high.
How big should the record buffer be?
Size it to the longest record in your capture, plus headroom. The sample Complete record with single space separators runs 50 characters, date through unit number. A buffer of a few hundred characters holds any sample event several times over. Anything longer goes through the discard rule: bytes between two terminators that overflow the buffer are dropped, and parsing resumes at the next terminator.
What happens when two Complete events arrive close together?
Each event ends at its own terminator, so each one is bounded and parsed on its own pass, and each publishes its own ID. The second publication overwrites the first in a single shared register, so a value can be replaced before the peers or the screens read it. If every event must reach the peers, publish into successive words of a small indexed table instead of one register.
Planning information, not legal or engineering advice. Rules differ by place: check codes and permits with your local authority and your licensed trades.