The Auto tag is updated either by a timer script (every 2 seconds) or by another tag through tag change scripts.
The Manual tag stores operator-entered values for overriding the automatic value.
The IsManual tag indicates whether manual override is enabled and determines which value gets written to the Final tag.
The Final tag reflects the actual value sent to the PLC based on the override status.
My Goal:
I need a mechanism that ensures the correct value (either from Auto or Manual) is consistently written to the Final tag depending on the state of IsManual.
I've explored several options:
Tag valueChanged scripts on Auto, Manual, and IsManual
Gateway tag change scripts
A timer script that runs at a fixed interval and writes the appropriate value to Final
The most convenient approach seems to be using valueChanged scripts directly on the tags. However, I'm concerned about performance implications — particularly the need for blocking reads (e.g., checking IsManual) in each script to decide whether to propagate the value.
Does anyone have recommendations or best practices for this kind of tag-based control logic in Ignition? I'm especially interested in performance considerations since I'll have thousands of tags of this form updating pretty frequently.
I would build a UDT for these tags, get a list of all UDT instances, and issue a single read for all UDTs (where all tag values of the UDT may be accessed like you would a dictionary), then iterate over the response (while building a list of tags to write), and write the output to all tags - in a (periodic) gateway script.
Final could be an expression tag that derives the final value from the other 3 tags. Then you could put a single tag event script on this that writes to a Final_OPC tag.
Is it an option to update your Auto tag to an expression tag, where all of the calculation for its value is done there? If so,, the @Kevin.Herron method gets my vote as well.
Reading this bullet:
...starting to feel like a lot of links in the chain. If there is already a script that is required for reading, calculating, & writing values, I'm hopeful that all of this logic could be moved to the initial script, where bulk reads/writes are possible.
I firmly believe that if you are trying to have PLC-like operating logic in Ignition, you should use a gateway timer event, emulating a PLC task. Put all related logic in the project library function called from that event.
I’m thinking of structuring the script so that all tag reads happen at the top, all tag writes happen at the bottom (batched), and the logic for determining the Final values lives in the middle. One quick question: should the write command be writeBlocking or WriteAsync?
That said, I do think emulating a PLC-style task could increase OPC traffic, since the script would write on every timer tick—even if the value hasn’t changed. Do you think it would be worthwhile to implement a compare_and_set mechanism? It would only write if the value has actually changed. To do that, I’d need to read the current Final values up front, which would increase the number of reads but could significantly reduce the number of writes. Thoughts?
If the writes are occurring at the end of the script and there is no processing following in which you are dependent on knowing the success or failure of the write, there is no reason you can not use WriteAsync(). Since you're planning to have the script in a Timer Event, this would even be my recomendation, no reason to wait on a return you wont be using, and it gets you out of the execution as quickly as possible.
The reads, however, should be blocking.
If by this you mean, save the orignial values at the start of the script, and then loop through both sets and compare at the end of the script, I think you're adding complexity without a lot of significant value gained. Remember, you are not writing directly to the device here. The gateway driver is already doing the work of optimizing reads and writes to the device.
I would not use writeAsync in this kind of "task" timer event. If there are comms problems, a write can take a long time to timeout, and if you aren't blocking, your timer event could pile up a bunch of stuck writes, with potentially unwanted behavior.
Great point! Comms problems do happen with my devices on a regular basis.
What if I use Optimistic Writes? Could I use writeAsync() with that or would I still be piling up a bunch of stuck writes in case of a comms issue?
Would you also recomend that the Timer script in this instance be set up for Fixed Rate and Shared Threading? I'm assuming that Fixed Rate with Shared Threading most closely emmulates a PLC's scan, however, it's entirely possible that I'm misunderstanding something else.