Not all members of UDT instance updating before gateway event script execution

I am working several systems with CompactLogix 5380 PLCs that fill a UDT with data as a part is being made, latch a boolean to trigger a driven tag group to read the UDT, use a gateway event script on tag change (looking at an element in the UDT) to verify valid data, then write the data to a SQL table using a transaction group. On the first system that I setup, this is working without any issues. On the second system, it seems like there is a race condition; after I trigger the driven tag group, the gateway event script executes but not all of the data in the UDT is updated. Is there a better/more robust way to know when the UDT has updated/tag group has finished updating values?

Additional info on current setup:

partUDT definition:

bPassPart (Boolean)
bFailPart (Boolean)
iPartQuantity (Integer)
iDataID (Integer)
iTransactionID (Integer)
dtTimestampPLC (DateTime)
FailureData (UDT, different on each machine)
PartData (UDT, different on each machine)

Once a part is completed, data is loaded into a FIFO with the type partUDT. The FIFO is unloaded to the PLC tag PartDataToLog (controller scoped). Then PartDataToLog.iTransactionID is incremented. Then the boolean TriggerPartLog is latched. Once PLC gets status that the transaction group execution completed, it will unload from the FIFO (or wait for the FIFO to have more data).

On the Ignition side, TriggerPartLog (in a direct tag group) is used as the driving expression for a driven, one-shot tag group. The tag [default]machineA/PartDataToLog is in this driven tag group. There is a gateway event script which runs on tag change for [default]machineA/PartDataToLog/iTransactionID which does the following:

logger = system.util.getLogger("MachineA_LogDebug")

if not initialChange:
	dataToLog = system.tag.readBlocking('[default]machineA/PartDataToLog',500)[0].getValue().toDict()
	
	logger.info('bPassPart:' + str(dataToLog['bPassPart']))
	logger.info('bFailPart:' + str(dataToLog['bFailPart']))
	logger.info('bScrapOnly:' + str(dataToLog['bScrapOnly']))
	logger.info('iPartQuantity:' + str(dataToLog['iPartQuantity']))

	if (dataToLog['bPassPart'] ^ dataToLog['bFailPart']) and dataToLog['iPartQuantity'] > 0:
		logger.info('Latching Trigger Bit')
		system.tag.writeBlocking('[default]machineA/PartTransactionTrigger', 1)
	else:
		logger.info('Latching Error Bit')
		system.tag.writeBlocking('[default]machineA/PartLogIgnitionError', 1)

On the first system that I setup, this works without issue. On the second system it does not work. If the current and previous parts both have bPassPart=True and bFailPart=False, it will work fine. When the previous and current part are not both pass or both fail, the log will show bPassPart=False and bFailPart=False. On the PLC side, PartDataToLog.bPassPart XOR PartDataToLog.bFailPart is never true.

The only way to get atomicity is to either:

  • use SQL Bridge transaction group, subscribe to trigger, direct OPC items in READ mode
  • subscribe to only the trigger tag and then issue a system.opc.readValues for the rest, setting the values onto memory tags or just using them directly in the script

edit: actually the transaction group probably doesn't help since ultimately you want to run a script with all these values, so probably ignore that one.

1 Like

So if I have a driven one-shot tag group and the only tag in that group is a UDT instance. When I trigger the tag group, there is no way to know that all of the elements of that UDT instance have updated? As I type this, I am realizing that I could check the timestamp for every element in the UDT instance but that seems a bit ridiculous.

This is situation.

1 Like

Do you mean situational? Is there any additional information or documentation on this? My understanding is that the Logix driver would optimize all of the UDT elements into one OPC read for the UDT, but I don’t really know much about how the ignition tags themselves get updated.

The Logix driver will do that, but it's not really relevant.

When your one shot group triggers all the OPC tags are going to get read.

Then all the values will be set on the tags. You can imagine it as simply a for-loop:

for (tag, value) in tags_and_values:
  tag.set_value(value)

As values are being set, tag change scripts, among lots of other things, will be triggered/evaluated/whatever. Maybe you're done iterating that list, maybe your not.

In your case, it seems not - so your script executes and some tag or another hasn't seen its value updated yet.

1 Like

Is there a good way to get feedback that tag group execution has completed? Is there a way to predict or control the order that tags within a tag group get updated when the tag group executes?

No, that's why I'm telling you what the alternatives to accomplish this are.

Use a regular subscribed tag as your trigger, then read all the tags that update atomically and you need to see updated atomically via system.opc.readValues and do something with those values.

1 Like

@Kevin.Herron, thank you for the help. I’ll look at switching the transaction groups to use OPC items instead of tag references, then try to get my DBMS to enforce the checks I was doing in the script.