Are OPC writes atomic? Conclusion: No

When using system.opc.writeValues to write to a PLC through the 'Allen-Bradley Logix Driver,' are multiple writes synchronous atomic within the PLC? The two values in this case are sequential elements (but not every element) of an array. An example is:

# plc_array_tag has type DINT[5,2]
# This does not write to all elements, but they are sequential elements.
system.opc.writeValues(
    'OPC_SERVER_NAME'
    ['plc_array_tag[0,0]', 'plc_array_tag[0,1]'],
    [5,10]
)

I'm seeing behavior that makes me think the PLC (sometimes) evaluates the elements after one has been changed but not the other. Is this possible? Is it likely?

The script runs in the gateway scope from a gateway timer event.

Ignition Version: 8.1.17
Allen Bradley CAT: 5069-L320ER
Allen Bradley Firmware: 30.011

Edit 1: @Kevin.Herron pointed out I used the wrong function in my code snippet.
Edit 2: Learned a new word that better describes my question. Question edited for future searchability.

This is code does a read, so I'm not sure what you're trying to ask.

Both the system.opc.readValues and system.opc.writeValues calls are synchronous though. When the calls return you will have values or quality codes representing the result of the operation.

I corrected it. The question stands, I just typed it wrong with my brain in automatic mode this morning.

I think what you may be trying to ask is if this write operation to 2 separate elements is atomic, and that's something that I can't answer, and will be entirely up to the PLC implementation of whatever PLC you're writing to.

In this case with it being a Logix PLC we do send a single "Multiple Service Packet" Service request, inside of which will be 2 Write Tag Service requests.

What the PLC does with these 2 requests, and whether it's possible for them to be evaluated in separate scans somehow, is not something I'm aware of being documented. @pturmel sometimes knows these kinds of implementation details.

1 Like

Atomic is what I'm curious about, but I didn't know the right word to describe it. I learned something new today.

You'd probably be better off assuming it's not atomic.

Generally, if you want to load some set of values into a PLC for it to operate on as a whole, you need to write those values first, then in a separate operation write to a trigger tag that tells the PLC it can start operating on those values.

1 Like

That's what I'll do. Thanks for your help with this.

Just for fun, I did some testing. I used this script in the Ignition Script console:

import time

for i in range(1000):
	print i
	_ = system.opc.writeValues(
		'Ignition OPC-UA Server',
		[
			'ns=1;s=[TestPLC]zTestDint[0]',
			'ns=1;s=[TestPLC]zTestDint[1]'
		],
		[i,i]
	)
	time.sleep(.1)

and this logic rung in the PLC:

Conclusion: @Kevin.Herron's assumption is correct; OPC writes are not atomic within this type of PLC.

1 Like

The CIP Specification requires that the requests within the Multiple Service Packet be executed serially, with no parallelism. But it says nothing about the PLC's own parallelism for target objects, which effectively makes the restriction useless. I have seen no evidence that any PLC brand follows that part of the CIP spec, because of the loophole, and the fact that it would crush messaging performance.

Single requests for an array tag or for the entire content of a UDT will be cooperatively atomic where the target is handled in the PLC only with CPS instructions.

Can you clarify what this means? Google results were less than helpful, mostly pointing to research papers in the atomic physics realm.

Dedicate a PLC structure type instance for data that travels one-way. For data that is going from Ignition to the PLC, write every element in the UDT (even if repeating) when writing anything (in a single system.tag.writeBlocking() or system.opc.writeValues() call) so that the driver can optimize into a single write request for the entire UDT. In the PLC, use a CPS instruction to copy from that tag to another instance. Use that copy for all other logic in the PLC--that copied structure will be reliably unfragmented.

Similarly, going from PLC to Ignition, use CPS to load a snapshot from one UDT into the UDT read by Ignition. Use system.opc.readValues() to get everything in that UDT atomically. Subscribe to a trigger tag that is always updated by the PLC after the snapshot, and fire the readValues() call in its tag change event.

6 Likes