Detect change in any element of UDT

ASF

Lifetime Supporting Member
Join Date
Jun 2012
Location
Australia
Posts
3,921
Hi all,

Just wondering if anyone has any ideas to easily detect a change in any of the elements of a UDT, other than brute force?

Looking at a recipe system, and I want to achieve the following:

- User selects recipe, and the recipe parameters are copied from a database into a local UDT which controls line parameters
- User wants to change one parameter just for now, but not actually edit the recipe
- User changes that one parameter. UDT is changed, but the change is not uploaded to the database
- PLC detects that there has been a change in one of the UDT elements and puts an alert onto the SCADA that they are currently running a modified recipe

Any ideas? I was hoping it would be as simple as an EQU and a LastScan tag, but EQU doesn't work on UDT's. Brute force (checking every element individually) will be a pain because the UDT's are quite large, so I'm hoping for a more creative solution.

Thanks!
 
Hi all,

Just wondering if anyone has any ideas to easily detect a change in any of the elements of a UDT, other than brute force?

Looking at a recipe system, and I want to achieve the following:

- User selects recipe, and the recipe parameters are copied from a database into a local UDT which controls line parameters
- User wants to change one parameter just for now, but not actually edit the recipe
- User changes that one parameter. UDT is changed, but the change is not uploaded to the database
- PLC detects that there has been a change in one of the UDT elements and puts an alert onto the SCADA that they are currently running a modified recipe

Any ideas? I was hoping it would be as simple as an EQU and a LastScan tag, but EQU doesn't work on UDT's. Brute force (checking every element individually) will be a pain because the UDT's are quite large, so I'm hoping for a more creative solution.

Thanks!

I'm doing the exact same thing for a customer right now. I did the detection with brute force. There are mixes of arrays and uniquely named tags so where there are arrays, I used For/Loop's.

Now, one way I was just thinking of may be simpler. Let's say that you have a UDT that is 1000 bytes. You create an array of DINT's that is also 1000 bytes. Copy the currently selected parameters into one array of 1000 bytes, then copy the stored UDT into another array of 1000 bytes and compare those two arrays using a For/Loop. If any individual array element is different, then someone made changes. This wouldn't directly identify where the changes were made (at least not easily) but it might be an easy way to tell if there were changes.

Doing brute force might make it easier to tell exactly what was changed, if that is important.
 
I use a type for HMI data entry fields. Then use the EnterKey (PanelView, and custom built Wonderware/FTView/RSView keypad) to detect the change of data.

Name STRING Setpoint Name
Desc STRING Setpoint Description
Unit STRING Setpoint Unit of Measure
Value DINT Decimal From PanelView Double Integer Type
Enter BOOL Decimal From PanelView Enter Key Pressed
Minimum DINT Decimal To PanelView Data Entry Minimum
Maximum DINT Decimal To PanelView Data Entry Maximum
 
This wouldn't directly identify where the changes were made (at least not easily) but it might be an easy way to tell if there were changes.

Doing brute force might make it easier to tell exactly what was changed, if that is important.

Knowing WHAT has changed isn't important really - at the end of the day I just want an alert to say that you're not running the recipe "normally".

I use a type for HMI data entry fields. Then use the EnterKey (PanelView, and custom built Wonderware/FTView/RSView keypad) to detect the change of data.

Name STRING Setpoint Name
Desc STRING Setpoint Description
Unit STRING Setpoint Unit of Measure
Value DINT Decimal From PanelView Double Integer Type
Enter BOOL Decimal From PanelView Enter Key Pressed
Minimum DINT Decimal To PanelView Data Entry Minimum
Maximum DINT Decimal To PanelView Data Entry Maximum

Hmm, that could work...I mean it still means attaching that tag to each and every entry field (and there are a lot of them), but I'm yet to create them, so if I do it as I create them it wouldn't be too much extra hassle...
 
Well if just knowing that something has changed is the goal, then copying your data to some arrays to compare should work. I bet you could do it in a few lines of structured text (seems to be a curse word) a little ladder if you prefer.
 
Last edited:
Well if just knowing that something has changed is the goal, then copying your data to some arrays to compare should work. I bet you could do it in a few lines of structured text (seems to be a curse word) a little ladder if you prefer.

True - although in about the same amount of effort I could just copy the UDT to an identical UDT each scan and then do an EQU on each element. Either way, it's still brute force :)
 
True - although in about the same amount of effort I could just copy the UDT to an identical UDT each scan and then do an EQU on each element. Either way, it's still brute force :)

copy each scan? instead of comparing and copying if different?
 
True - although in about the same amount of effort I could just copy the UDT to an identical UDT each scan and then do an EQU on each element. Either way, it's still brute force :)

Not really. If you copy it to an array, A for/loop can handle the whole thing with a couple of lines of code. If you use brute force, you will have to write out each element of your UDT.

Code:
copy CURRENT to ARRAY1
copy DATABASE to ARRAY2

FOR i in 0 to LENGTH_OF_ARRAY:
  IF ARRAY1[i] <> ARRAY2[i]:
    CHANGE = True
LOOP
Or
Code:
if Current_Widget <> Database_Widget: CHANGE = True
if Current_Thing <> Database_Thing: CHANGE = True
if Current_Stuff <> Database_Stuff: CHANGE = True
... and so on for every element
They are not the same as far as the amount of work you have to do to do the compare. Of course that doesn't match any programming language in particular but I'm at home and lazy. The first would do what you are after in about that many lines. Brute force would require to write a condition for each piece of your UDT.
 
I do this right now, and put an asterix next to the recipe name on the menu bar when a param is modified and the recipe isn't saved.

Currently the asterix gets messed up when I do software updates. Working on this now.
 
Not really. If you copy it to an array, A for/loop can handle the whole thing with a couple of lines of code. If you use brute force, you will have to write out each element of your UDT.

Yep - but copying the data into the arrays so that I can use the for loop will require the same amount of brute force as checking each one individually. The UDT doesn't have any arrays, it's all individual DINT's, REAL's and STRING's. So I'd have to copy them one at a time into a DINT/REAL/STRING array, and then I can do the checking - but at that point I might as well have just run an EQU on them one at a time, instead of running a COP one at a time.

Or am I missing some part of your cunning plot? ;)
 
Yep - but copying the data into the arrays so that I can use the for loop will require the same amount of brute force as checking each one individually. The UDT doesn't have any arrays, it's all individual DINT's, REAL's and STRING's. So I'd have to copy them one at a time into a DINT/REAL/STRING array, and then I can do the checking - but at that point I might as well have just run an EQU on them one at a time, instead of running a COP one at a time.

Or am I missing some part of your cunning plot? ;)

Your UDT has a size (number of bytes). If your array of words (SINT/INT/DINT, whatever gives you an equal number of bytes) is the same size (number of bytes), you can use one copy command to copy your entire UDT to the array. It just does a byte for byte copy. The data in the array won't mean much. But by comparing the array containing your current copy of parameters with your array of your database copy, you can tell if there is a change.

So a single copy box will dump your UDT into an array of equal number of bytes. Then a simple For/Loop comparing your arrays will tell you if there is a difference.
 
Aaaaah, yes, you said that way back in post #3. Duh! Missed it somehow :) it all makes sense now, quite a valid solution!
 
Aaaaah, yes, you said that way back in post #3. Duh! Missed it somehow :) it all makes sense now, quite a valid solution!

Something like this works for me:

Code:
// make sure the size of the array matches the number
// of bytes for an instance of the UDT

// get the size of the array so we know many times
//  to loop
SIZE(CurrentProductArray, 0, ArraySize);

// Copy current running product values and the saved values to
//  arrays so that we can check for any changes
COP (CurrentProduct, CurrentProductArray[0], ArraySize);
COP (DatabaseBackup, BackupProductArray[0], ArraySize);

// Set our changes made flag to False
ChangesDetected := False;

// loop and compare both arrays, if there are any differences, set
//  our changes detected flag true
For i:=0 To ArraySize-1 Do
	If CurrentProductArray[i] <> BackupProductArray[i] Then ChangesDetected := True; End_If;
End_For;

With the above code, if you ever modify your database parameters, you should just have to change your array sizes to match. Someone may find a flaw, I threw it together quickly, but the idea should work

edit: Well I may have to do it a little different, it's not detecting the changes sometimes. Maybe the copy is not complete while the loop is running. I'll have to investigate.
 
Last edited:
If it's using Logix5000, you can use a FSC to look for differences. Works really simply.

First, copy the UDTs into DINT arrays with the proper length. I usually oversize the DINT arrays to have easier room for growth (and make sure that I zero them out first).

Once you have the two DINT arrays, use the FSC to go through them looking for differences.

Comments:
Code:
Checks to see if the active recipe is different from its saved version on an interval.
Use the interval to conserve PLC scan time.

The SIZE and FLL commands are used to make sure that the DINT arrays that are being used are empty before the comparison process is started. This is to prevent an issue if the UDT size is changed.
The DINT arrays are intentionally sized a little bit larger than the UDTs in order for it to be easier to add several UDT elements at a later date, if needed.

The active recipe UDT and its saved version UDT are copied into the DINT arrays. The length is determined by taking the size of the UDT (from the top-right corner of its configuration screen) and dividing the byte count by four.
This converts the bytes into DINT sized groups (DINTS are 32 bits - 4 bytes long each).
For example, UDT_Rcp UDT has a size of 344 bytes. 344 / 4 = 86.

The FSC searches through the DINT arrays and compares each element to see if they are not equal to each other. If it detects a difference, the found (.FD) bit is set.
The inhibit (.IN) bit is automatically cleared in order to allow the FSC to function again.

Untitled.png
 

Similar Topics

I was wondering if anyone has any ideas on how to detect change in an integer in a SLC 5/04. We have a machine that runs 3 different parts. Each...
Replies
21
Views
7,502
Hi guys....first post here. It's been about 6 years since I worked heavily with PLC's and automation, but I'm getting back into it. I was hoping...
Replies
20
Views
3,563
Hi every one I' using Unity Pro software. Is there a function for detecting instantaneous change in a register value and display whether it is...
Replies
1
Views
2,065
Hello all, I've written a routine that scrolls a bright green oval (positioned behind existing 'goto screen' buttons to create a 'halo' effect)...
Replies
6
Views
4,820
Hello everyone, I have a problem to detect speed change in my program. I use a sensor to detect a feeder roller speed change. When some strap...
Replies
5
Views
6,132
Back
Top Bottom