Execution order of OPC/Expression Tags and Transaction group


i have a little problem with the following scenario:

An UDT contains an OPC-Tag and an Expression Tag, both on a Driven ScanClass.
Every 15 minutes, the ScanClass executes and reads an OPC value.
The expression tag should then make a calculation, as soon as the new value arrives.
Now, the calculated value should be stored to a database table, using an id property, timestamp and the value from the UDT.
The above has to be done for a few hundred tags (from many different plcs) at the same time.

Everything works perfect if i forget the UDT and use a Standard Transaction group for every tag, but i don’t want to maintain some hundred groups, so the idea was to use one big block group, that references the UDT (block groups seem to have no support for expressions).

Is there a way to synchronize the execution, so that the tag is read, then it is calculated and when everything is calculated stored to the db? I can’t use data change events, because in rare cases the new values might be the same as the old ones.

I hope there is an easy way to do what i wan’t, that i just don’t realize by know.

Is the expression the same for each tag? From an execution point of view, the UDT tags should be fine (especially if the data mode on the scan class is set to “read”), but from a recording point of view, it’s tricky to make the block group know that the scan class has run. I can think of one or two ways, but they’re not too nice.

Would it maybe be possible to use the tag history system? Then you could just set the expression tag to store history. If you go into the history settings and turn off partitioning, you can query the data table fairly easily yourself, and its format isn’t much different than what you described.

If the expression is always the same, another option that comes to mind is to have the block group store the id and raw value, and set up a column for the calculated value, and a database trigger that contains the expression and runs it “on insert” of the raw value.

Just some thoughts, let me know what you think.


Hello Colby,

thank you for your suggestions.

The SQLTag History is not really an option. I need to have a bit more control on the table, there might be more columns necessary and i have to handle manual input in case of a system failure. Also automatic deletion of records based on age would be difficult to handle for the SQL-Server if i want to keep some other tags history. In addition, i have a null value logged on a gateway restart. Not a big problem, but distinguishing that value from a device error wouldn’t make queries easier to understand.

In the meantime i found out that a block transaction group is not usable at all, because it inserts a record when the group is restartet, even if the groups mode is set to ‘insert changed rows’. It also does not store a record when a ReadOnly block item changes, but the DB values are unchanged. The database trigger approach (which is a good idea) is therefore also pointless.

I was able to get a standard transaction group to do what i want, by adding an ‘update counter’ to the UDT and triggering the group with this counter. This got me back to my original intention of simplifying the handling.

I made a quick ‘proof of concept’ project with a custom script module, that exposes the Store&Forward functions to tag change events. I have to tweak this a bit more to have it insert blocks of data, instead of updating every value seperately and make sure it is stable when a lot of tags change at the same time, but that seems to be best solution so far. If someone has to add a tag later, an UDT instance can be simply added to the log by dragging my ‘update counter’ tag into the tag change script.

Once we get the execution issues mentioned in the other thread straightened out, the script approach is decent enough for now. However, I think my real goal would be to enable the block group to do what you want- we’ve had the idea for quite a while that you should be able to add an expression item to the block group that is executed for each row.

The other issues about logging on start up and triggering off of read-only columns should also be easy enough to handle.