← Articles
OPC UA/6 min read/ views

Good Quality, Stale Value: Reading OPC UA StatusCode and Timestamps

Using StatusCode severity bits, SourceTimestamp and ServerTimestamp to catch frozen data, cached gateway values and clock drift a value-only HMI hides.

OPC UASCADATagsTroubleshootingProject Notes

The tank level reads 3.42 m on the HMI. It has read 3.42 m for forty minutes. The operator thinks the pump is off; the pump has actually tripped and the level is climbing, but the OPC UA server behind the gateway is handing out its last cached DataValue with a Good status and a SourceTimestamp from forty minutes ago. Nobody sees the timestamp because the HMI tag was mapped to the value and nothing else.

That is the failure this whole topic exists to prevent. Every OPC UA read or subscription notification is a DataValue: a value, a StatusCode, a SourceTimestamp, and a ServerTimestamp (IEC 62541-4). Map only the value into the tag and you throw away the three fields that tell you whether the value is trustworthy.

The StatusCode is 32 bits, and only the top two matter first

An OPC UA StatusCode is a 32-bit number. The most significant two bits are the severity, and that is what you branch on before anything else:

  • 00Good (0x00000000 and up)
  • 01Uncertain (0x40000000 range)
  • 10Bad (0x80000000 range)

The rest of the word is a sub-code that names why. A driver that collapses everything to "bad quality" is throwing that away, and it costs you at the worst possible time. Bad_NoCommunication (0x80310000) is a cable, a switch, or a dead PLC — go to the field. Bad_NodeIdUnknown (0x80340000) means the address is gone; almost always someone did a PLC download and the NodeId shifted, and no amount of walking the plant will fix it. Two tags both painted red on the HMI, two completely different jobs.

The handful worth keeping distinct during commissioning:

Symbolic nameSeverityWhat it actually means
GoodGoodValue is live and usable
UncertainLastUsableValueUncertainServer is warning you the value may be stale — do not treat as Good
Bad_NoCommunicationBadComms down to PLC / remote I/O / gateway
Bad_NodeIdUnknownBadAddress mapping broke, usually after a program download
Bad_OutOfServiceBadItem deliberately taken out of scan
Bad_UserAccessDeniedBadAccess level, role, or security policy, not comms

UncertainLastUsableValue is the one people skip. It is the server being honest that it is showing you the last value it had. If your HMI renders Uncertain the same as Good, you have re-introduced the stale-value bug the server was trying to warn you about.

Read the two timestamps against each other, not alone

SourceTimestamp is when the source believes the value was sampled or changed. ServerTimestamp is when the OPC UA server processed it. Both are UTC (encoded as 100 ns ticks since 1601 — Windows FILETIME), so if your client shows local time, that conversion is one more place a "wrong timestamp" is really a display setting.

The diagnosis lives in the gap between them:

  • ServerTimestamp fresh, SourceTimestamp old → the server is alive but the upstream value is not moving. Frozen source, or a cache holding a value. This is the tank-level case above.
  • ServerTimestamp old too → the problem is on the server side: subscription starved, session dropped, publishing stalled.
  • SourceTimestamp ahead of ServerTimestamp → a clock mismatch between source and server, not a data problem.

One caveat before you call an old SourceTimestamp a fault: it depends on the tag. A batch step number legitimately holds the same SourceTimestamp for six minutes because the step has not changed. A 1-second analog that stops advancing is dead. You need to know which class the tag is in — write it down per tag at commissioning:

  • Analog process value: advances every publish or on significant change (server-dependent)
  • Discrete status: advances only on transition
  • Counter / totalizer: advances on increment or on sample
  • Recipe / batch state: can sit unchanged for a whole step — old is normal
  • Heartbeat: must advance on a fixed interval; this is your stale detector

That last row is the cheap insurance. A heartbeat tag that increments server-side every second gives you one thing you can trust to always move. When it stops, the path is down — no per-tag guessing.

The disconnect test is the one people skip at FAT

For every critical tag, the commissioning test is six steps, and the value of it is entirely in the last two:

  1. Force a controlled change at the source.
  2. Confirm the displayed value changes.
  3. Confirm SourceTimestamp advances when you expect.
  4. Confirm ServerTimestamp and client receive time are close enough for the process.
  5. Pull the source path and confirm quality goes Bad instead of freezing Good.
  6. Reconnect and confirm quality and timestamps recover with no HMI restart.

Steps 1–4 pass on almost any system. Steps 5 and 6 are where you find the gateway that keeps serving its last value as Good, the reconnect logic that never re-subscribes, and the HMI that shows a stale number with no visible flag. If you only test that a value can change, you have tested the easy half.

Fix the clock layer before you debug the logic

OPC UA values, alarms, historian samples, and MES events all get lined up by timestamp downstream. Let the PLC, OPC UA server, SCADA server, and historian disagree by five minutes and every one of those comparisons lies. That same five-minute offset shows up as a historian backfill glitch on one screen, an out-of-order alarm sequence on another, and a broken lot trace in the MES — three "bugs" from one wrong clock.

Before you touch application logic, check the boring layer:

  • NTP / plant time source on the OPC UA server host — is it actually syncing, or configured and silently failing?
  • Server time vs SCADA server time, measured, before you start alarm or historian testing.
  • Time zone and DST config, not just the wall-clock value.
  • How the client renders UTC vs local — a lot of "wrong time" reports are pure display.
  • The time source of any embedded gateway that has no normal OS time service; note it explicitly.

Keep the reference reading

For each critical interface, park a few lines in the project file: endpoint URL and security policy, subscription and sampling intervals, whether quality and timestamps get historized, the stale timeout per tag class, and the SourceTimestamp/ServerTimestamp you observed during the controlled-change and disconnect tests. Next time someone says the HMI is "slow," you compare against a known-good reading instead of arguing about it. IEC 62541-8 (Data Access) is the part to cite when a vendor claims their status handling is "per spec" — it defines what these codes are supposed to mean.