Hi all,
I'm troubleshooting an intermittent performance/communications issue on an Ignition 8.1 Vision gateway and I'm looking for some advice around Gateway Tag Change scripts, the Tag Event thread pool and blocking tag writes.
APPLICATION BACKGROUND
This is a fairly large SCADA application that was migrated from Wonderware InTouch 2012 to Ignition Vision 8.1.
A lot of the original InTouch process and database functionality has been replicated in Ignition. The application communicates with several Rockwell PLCs and makes extensive use of UDTs and Gateway Tag Change scripts.
We probably have a few hundred Tag Value Changed scripts across the application, many inherited through UDTs.
One of the main uses of these Tag Change events is process state/data logging to SQL.
Rather than continuously polling all of the process information, particular UDT members act as triggers. When a relevant process state, cycle, step, interlock or error changes, a Gateway Tag Change script runs.
The general flow is something like:
PLC process state changes
β
Ignition UDT member changes
β
Gateway Tag Change event fires
β
Script obtains the associated process context
β
Determines the relevant state/process information
β
Required information is assembled
β
Database query/update is executed
β
Process event/state is recorded in SQL
So this isn't simply standard tag history. We're recording the progression of the process through its states and associating that with the relevant process information in SQL.
There is similar event-driven functionality around interlocks and errors.
Other Tag Change scripts perform things such as:
- Process state logging to SQL
- Interlock/error logging
- Recipe/setpoint downloads to PLCs
- Batch/process handling
- PLC/HMI heartbeat handling
- Database connectivity monitoring
- Dataset generation/processing
- Various PLC reads and writes
Some of these scripts use system.tag.readBlocking(), system.tag.writeBlocking() and database queries.
This means that during a process transition several PLC values can change close together, potentially resulting in multiple Gateway Tag Change events firing around the same time.
For example:
PLC process transition
β
Several process values change
β
State logging events
Interlock/error events
Process sequence events
Recipe events
Other UDT events
β
Multiple Gateway Tag Event scripts execute
This is why I'm wondering whether bursts of Tag Change activity, combined with blocking operations in some scripts, could be relevant to the issues we're seeing.
ISSUE 1 - PLC/HMI HEARTBEAT TIMEOUT
We have a heartbeat/watchdog value changing approximately once per second.
A Gateway Tag Value Changed event writes the changing heartbeat value to an OPC value in the PLC. The PLC monitors this and generates a communications alarm if it doesn't see the value change within its timeout period.
The heartbeat script is essentially:
def valueChanged(tag, tagPath, previousValue, currentValue, initialChange, missedEvents):
if not initialChange:
system.tag.writeBlocking(
[destination],
[currentValue.value]
)
Under normal conditions this works without any problem.
However, intermittently the PLC doesn't receive a heartbeat change within its timeout period and generates a communications alarm.
As a test, we moved the actual heartbeat write into system.util.invokeAsynchronous() so that the writeBlocking() wouldn't occupy one of the Gateway Tag Event threads.
The heartbeat/comms alarm has still occurred since making that change, so the Tag Event thread pool may not be the sole cause.
ISSUE 2 - INTERMITTENTLY SLOW RECIPE/SETPOINT DOWNLOADS
We also have Gateway Tag Change events responsible for downloading recipe/setpoint data to the PLC.
When the relevant process sequence changes, the script essentially:
- Executes a Named Query to retrieve the required recipe setpoints.
- Processes the returned dataset.
- Builds the destination paths.
- Writes the required setpoints to the PLC.
- Checks the QualityCode returned from each write.
We are currently writing the setpoints using system.tag.writeBlocking() in chunks of four.
Under normal conditions the actual setpoint download is very quick. We generally see the complete write taking approximately 100-200 ms.
However, intermittently we have seen the operation take several seconds, occasionally around 8-10 seconds.
We have now added additional logging to the recipe download. For every setpoint we log the value being written and the QualityCode returned by writeBlocking(), as well as the overall execution time.
For example:
Wrote value: 15 - Quality: Good
Wrote value: 20 - Quality: Good
Wrote value: 120.0 - Quality: Good
Wrote value: 1 - Quality: Good
Recipe successfully written in chunks of 4. Total time: 120 ms.
The idea is to catch one of the 8-10 second occurrences and determine whether the PLC writes themselves are taking several seconds and/or returning anything other than Good quality.
GATEWAY TAG EVENT THREAD POOL
This led us to start looking at the Gateway Tag Event execution threads.
The Tag Event script thread pool was originally configured with the default 3 threads.
We increased this from 3 to 10.
So far, increasing it hasn't made an obvious difference to the intermittent heartbeat or recipe issue.
Looking at the Gateway thread page, we can see the Gateway Tag Event execution threads moving between states such as:
RUNNABLE
WAITING
TIMED_WAITING
During normal operation we might see, for example:
3 RUNNABLE
7 WAITING
When deliberately generating a lot of SCADA activity ("button bashing") or during periods with a lot of process activity, we have seen more of these threads active and have also seen TIMED_WAITING.
I realise the Java thread state itself doesn't necessarily prove that Tag Events are backed up, so I'm trying to understand the correct way to determine whether Tag Change events are actually sitting in a queue waiting for an available event thread.
WHAT WE'RE TRYING TO DETERMINE
One theory is that because we're quite heavily dependent on Tag Change scripts, some of which contain blocking operations, we could occasionally get something like:
Several process changes occur
β
Multiple Tag Change scripts start
β
Available Tag Event threads are occupied
β
Additional Tag Change events queue
β
Time-sensitive heartbeat/recipe event waits
β
Script eventually executes
If this is happening, it could potentially explain why something that normally executes very quickly occasionally appears to be delayed by several seconds.
However, the alternative is that the Tag Event pool isn't actually the problem and the script starts immediately, but the delay is occurring further downstream:
Tag Change event occurs
β
Script starts immediately
β
Script reaches writeBlocking()
β
Ignition/OPC/device communication stalls
β
writeBlocking() eventually returns
β
Script takes several seconds to complete
The fact that increasing the Tag Event pool from 3 to 10 hasn't made an obvious difference, and that we've still seen the heartbeat alarm after moving its write into invokeAsynchronous(), makes me particularly interested in distinguishing between these two scenarios.
We're therefore trying to determine whether our occasional 8-10 second delay is:
EVENT OCCURS
β
[Several seconds waiting in Tag Event queue]
β
SCRIPT STARTS
β
PLC writes complete quickly
OR:
EVENT OCCURS
β
SCRIPT STARTS immediately
β
[writeBlocking()/OPC operation takes several seconds]
β
SCRIPT FINISHES
QUESTIONS
I'm hoping someone with more experience of the Gateway internals can give some guidance on the following:
-
With a few hundred Tag Change scripts, is increasing the Gateway Tag Event thread pool from 3 to 10 reasonable, or are we potentially just masking an architectural issue?
-
Is there any benefit in going higher than 10, or could that potentially make things worse by allowing more simultaneous OPC/database operations?
-
Is there a good way to see the actual Tag Event queue depth/wait time rather than trying to infer it from the Java thread states?
-
Are RUNNABLE, WAITING and TIMED_WAITING on the Gateway Tag Event execution threads actually useful for diagnosing this, or would capturing thread dumps/stack traces during the issue be more useful?
-
Is using system.tag.writeBlocking() inside Gateway Tag Change events generally discouraged, particularly when writing to OPC-backed PLC values?
-
Would system.tag.writeAsync() be preferable for operations where we don't need to wait for the write result?
-
For something like a recipe download where ordering, completion and returned QualityCodes matter, what would be the recommended architecture? Would you still use writeBlocking(), but move the overall operation away from the Tag Event execution pool?
-
Is there a recommended way of determining the time between the actual value change occurring and the Python valueChanged script beginning execution? This would allow us to distinguish an event that has been queued from a script that starts immediately but subsequently runs slowly.
-
Are there particular Gateway loggers/metrics that would be useful to enable temporarily to determine whether the delay is happening in:
- Tag Event execution
- Ignition's tag system
- OPC writes
- Device driver/PLC communications
- Database operations
- More generally, for an application migrated from InTouch 2012 that has ended up with several hundred Tag Change scripts, should we be looking at reducing the amount of logic performed directly inside Tag Events and moving heavier/event-driven operations elsewhere?
At this point I'm mainly trying to get enough diagnostics in place so that when the issue happens again we can establish exactly where those missing seconds are occurring.
Any suggestions on what diagnostics/loggers/thread information would be useful to capture when the issue occurs would be appreciated.
Thanks.