I was wondering if anyone could help me setup some trap logic that will compare incoming strings to all the strings received in the last 15 seconds. I want to count every instance a string is received that is a duplicate within the last 15 seconds worth of strings received. Thanks in advance, love this community!

Serial communications? Sockets? Are they sent as a string, or do you need to organise them when you receive it?

If this is by exemple a barcode reader connected to a serial communication card, you can easily store each received barcode to an array. But you would need to create your array with a big enough size and know the frequency of data being received.

You could add a second array of time to store the time and then check it it's more or less than 15 seconds to manage it.

This will be almost impossible to do on a PLC and a PLC's CPU power shouldn't be wasted doing this.
If the messages or strings have a CRC, I would simply compare the two CRCs. If the string doesn't have a CRC I would create one because it will be easier, more efficient, to compare CRCs with one compare than to do multiple compares for long strings. If you KNOW that most of the strings will be different in the first 2 characters you may try the brute force method. The worst case situation is multiple long strings that are the same for the first 10 or more characters.

__________________
"Living is easy with eyes closed, misunderstanding all you see...." Strawberry Fields Forever, John Lennon

Are the strings coming in a regular, repeatable, consistent intervals? Or could you have ten strings in a five-second period, and then only two strings in the next five second period?

If they're regular, it's a bit simpler, but I'm going to guess they're probably not - and even if they are, there's likely no way of guaranteeing that it will always be that way. So it makes sense to assume the worst and program accordingly.

As Ken said, we'll need to know what PLC/platform you're using to do this, but here's how I'd approach it in a Logix PLC. For another platform, the same basic premise should hold true, but the instructions and terminology will be a little different.

First things first, you'll need an array of strings to store your incoming strings. Work out the absolute maximum number of strings you can possibly receive in any 15-second period. Then double it, and make an array that size. If your strings are a fixed length, you can make a custom string data type with a shorter length to save memory (a string by default reserves space for 82 characters).

Then you'd use an FFL and FFU to load them in and out of the array. Adding them in is easy: each time you see a new string, you use an FSC to compare it to the existing array strings, then an FFL to add it into the array.

Unloading from the array is a bit trickier. Normally when you're doing things like this, you're checking against the last 15 strings, not all the strings in the last 15 seconds. If you were doing the former, it's a piece of cake - each time you get a new string, you do a compare, unload the last (15th) string from the array, and add the new one in. But here, you're going to have to have some way of timestamping the values, and periodically checking to see if any have exceeded the 15-second "lifetime" and thus need removing.

There are several ways you could do this. You could use the T_clock and T_add add-on instructions from the RA sample library, or you could use an array of timers to match your array of strings. You could streamline somewhat by creating a UDT consisting of a string and a timer, and then creating an array of that UDT. In any case, indirect addressing and loops are your friend here. Remember, you don't need to check every single string in your array to see if it's "expired" - start your check with the oldest one in the array, and as soon as you find one that hasn't expired, you know that none of the remaining ones will have either.

The string is a tote ID from a cognex camera and shouldn't be more than 10 characters.

I was thinking this is a two part configuration. The first part would be to store the strings for 15-20 seconds using an array. Then at the end of that time frame start overwriting the previously stored data in the array. This way I have a 15-20 second rolling storage of strings. But doing this is what I am needing the most help with. Is this something that would be good to use FIFO with? I've tried using FIFO before without much luck it was tricky for me. The strings are received fairly consistent, but dependent on human production. Shouldn't exceed 20 strings in 15 seconds though.

The second part of the programming would be the comparison of the last received string to the array of strings. If there was a match the instance would be counted. The comparison would be the next part I am needing help with.

Is there an easier way to do this or am I on the right track? ASF I just read your post after I typed all this and I think we are on the same page. Anyone know an easier/better way? Thanks all!!!

...Unloading from the array is a bit trickier. Normally when you're doing things like this, you're checking against the last 15 strings, not all the strings in the last 15 seconds. If you were doing the former, it's a piece of cake - each time you get a new string, you do a compare, unload the last (15th) string from the array, and add the new one in. But here, you're going to have to have some way of timestamping the values, and periodically checking to see if any have exceeded the 15-second "lifetime" and thus need removing.

If I am only keeping 15 seconds worth of strings in the array, then I don't need to timestamp them do I? Can't I just compare to the entire array? Can I reset the FFL back to the top of the stack/array every 15 seconds to accomplish this?

The FSC instruction exists to de exactly the sort of thing you're doing. It does string comparisons; I use it for that sort of thing all the time.

Quote:

Originally Posted by Nebul0us

If I am only keeping 15 seconds worth of strings in the array, then I don't need to timestamp them do I? Can't I just compare to the entire array? Can I reset the FFL back to the top of the stack/array every 15 seconds to accomplish this

Well, it depends on exactly what you're trying to achieve, but if you want to compare each new string against all strings received in the 15 seconds preceding the one you just received, then no. Imagine you start your timer at 12:00:00am. At 12:00:01am you receive the string "ABC". Then you receive several more, then at 12:00:14am you receive the string "DEF". At 12:00:16am you again receive "DEF". Now, if you were clearing your array every 15 seconds, you'd have cleared it at 12:00:15, and not detected that you received "DEF" twice within the space of two seconds.

No, the only way to do what you need to do is to timestamp each string, and then once a second (or however often), check to see if the oldest string in your array is older than 15 seconds. If so, unload it and check the next. Repeat until the oldest string in your array is younger than 15 seconds, and then pause until the next one-second check.

Of course, if you're only interested in duplicates within completely discrete 15-second periods, then yes. That's much easier, and works exactly as you describe. But I doubt that's what you're after - if you need a rolling "last 15 seconds" duplicate check, then you have to have a timestamp of some kind.

What's the ultimate goal of this? What are you trying to detect/prevent? There may be an easier way again to do it.

I agree with the FSC but not a timestamp to compare or FIFO for the Array. The FAL or COP instruction is better suited for this type of task.

If the objective is compare over a 15 second period then I would use a pulse to advance my array positions for data entry. Use a TON to generate a 1 second pulse by using a XIO timer done bit from the timer to enable the timer. Then also use the valid results bit from the camera to (RES) the timer. This way if you get valid results in less than 1 second the timer will reset or if you get timer done shift your data array. This would allow for multiple parts in under a second or no parts for a second or longer.

The FAL will automatically roll over when the control length is reached and restart at the 0 position.

The COP instruction could also be used by setting it to copy Array[1] to Array[0] for length. This would require the entry data to be placed in bottom of the Array and shift it up toward [0].

To use the FSC you will be required to (U) unlatch the FSC control.IN (inhibit) bit to search the entire array if a match is FSC control.FD (found), if you need to find more than one match. If only one match is required to generate your condition then you can use the (RES) instruction on the FSC to reset all bits and control position.

The only need I see for using a timestamp would be to capture the duplicate information for reporting. If this is necessary then you could use indirect addressing to pull the timestamp out of its array using the FSC control.POS(position) when a duplicate is FSC control.FD (found).

Building on the ideas suggested by cwal61, one approach could use the following design/implementation strategy:

. Create a user-defined-type (UDT) consisting of three elements: barcode string, time remaining, duplicate count. Let's say they are of types STRING, DINT, and DINT, respectively, where the time remaining is in milliseconds.
. Create an array of these UDTs large enough to hold the maximum number of unique strings (e.g., 20 elements) that could be read within the time frame of interest (e.g., 15 seconds).
. Create a recurring (pulse) timer at the resolution to purge stale barcodes from the tracking array (e.g., every one second).
. Create a single instance of the barcode data UDT as a working unit.
. When a new barcode arrives, first search for it in the UDT array using the FSC instruction. The comparison will look something like 'arrBarcodeData[bcFSCctrl.POS].strBarcode = newBarcode'. For efficiency, set the FSC length equal to the FIFO array position, and RES prior to executing the FSC.
. >> If the barcode is found (i.e. bcFSCctrl.POS.FD is set), increment the duplicate count for that array element. No other action is required.
. >> If the barcode is not found, then copy the newBarcode string into the working UDT instance, and initialize its duplicate count to zero, and time remaining to the time frame of interest (e.g., 15000). Then load this working instance into the FIFO using FFL with the UDT array and the working instance as the source.
. Every time the pulse timer expires, execute a FAL on the UDT array to decrement all the remaining time of all active elements by the pulse timer preset (e.g., 1000). This will look something like 'arrBarcodeData[bcFALctr.POS].RemainTime' as destination with expression 'arrBarcodeData[bcFALctr.POS].RemainTime - 1000'. Again, set the FAL length to the FIFO position for efficiency.
. Also, after the FAL, purge all of the stale barcodes from the UDT array using the FFU to remove the oldest elements whose remaining time has expired. This would be done by testing the remaining time of zero element of the UDT array as less than or equal to zero. If so, execute FFU on the UDT array. The unloaded UDT will indicate how many times it experienced a duplicate read.
. >> Since there could be multiple expired elements, it may be necessary to do more than one FFU after the pulse timer expires. One way is to latch a "purge stale" bit after the FAL. Then unlatch this bit when the remaining time of the zero element is greater than zero or the FIFO is empty. Execute the FFU on every scan while this bit is set.
. Be sure to use the FIFO control.EM bit (i.e., not empty) to precondition the FAL, FSC, and FFU in order to avoid operating on an empty UDT array.

If you want to count active duplicates at any time, you could use an FAL to sum up the duplicate counters in the FIFO. Or if you want to keep track of duplicates over a long period of time, you could sum the duplicate counts of the elements as they are unloaded from the FIFO.

After coding some of this, I did start to wonder it might be easier to read and maintain using loops and routines instead of FAL and FSC. Although the code is quite compact with the traditional FAL/FSC/FFL/FFU approach.

__________________
"The greatest enemy of knowledge is not ignorance, it is the illusion of knowledge." -Stephen Hawking

I still stand by my suggestion of FFL/FFU for storing the data, but Mispeld's suggestion of a "TimeRemaining" DINT is a great one. Having the time remaining update on a simple one second timer, independently of the rest of the loading/unloading/duplicate checking is definitely the way to go. Simple and effective! The FAL instruction, as Mispeld suggests, is a good fit for the task.

I agree with Peter. It is brutally inefficient to compare the same full size strings over and over again. Calculating a CRC of each received string and only storing and comparing that makes the solution much more efficient.