Handle OPC UA BadResourceUnavailable (0x80040000) from OtOpcUa's out-of-process script runner #112
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
OtOpcUa is adding an out-of-process, pre-emptible script runner (dohertj2/lmxopcua#780, design
docs/plans/2026-11-09-out-of-process-script-runner-design.mdin lmxopcua, owner decision §14 #3). Once a node runs withScripting:Runner=OutOfProcess, OtOpcUa publishes a new OPC UA status code on script-computed nodes:0x80040000BadResourceUnavailablescript worker restarted0x80040000BadResourceUnavailableScripting:Worker:UnavailableGrace(default 5 s), or the worker cannot start at all (fail closed)script runner unavailableWhat does not change:
BadInternalError(0x80020000).Scripting:Runner=InProcess, which never produces 0x80040000. Nothing changes on any deployed box until a node is explicitly flipped (design P3: QA first, Calculation then virtual tags; scripted alarms last and not yet covered).Affected nodes: virtual tags (UNS namespace) and Calculation tags (raw namespace + every UNS reference to them). Scripted-alarm predicates are not affected yet (that seam stays in process in this phase; when it moves, a runner failure freezes the condition with a Bad quality annotation, it never clears the alarm).
Ask for ScadaBridge's OPC UA adapter (DCL): make sure 0x80040000 is handled as a plain Bad quality on a monitored item — "the source node is temporarily unable to compute this value", not a configuration, security or session error — so it maps to Bad quality on the instance attribute and does not trigger a reconnect, a subscription rebuild or a static-tag false-bad path. If the adapter classifies status codes by name or sub-code anywhere (quality mapping, alarm quality, flow-debugger display), add
BadResourceUnavailablethere. A test against a node that alternates Good / 0x80040000 / Good would pin it.Must be in place before any OtOpcUa node that ScadaBridge reads is flipped to
OutOfProcess(lmxopcua#780 P3).Cross-link: dohertj2/lmxopcua#780.
OtOpcUa side landed on lmxopcua master @ 69a6e0b4 (opt-in; default InProcess never emits 0x80040000). Tracking: dohertj2/lmxopcua#780.
Fixed on main @
08fe5acf.OpcUaDataConnection):BadResourceUnavailable(code part 0x80040000, info bits ignored) maps to plain Bad tag quality as before; it never reachesRaiseDisconnected, and it is not inDataConnectionActor.IsConnectionLevelStatus, so no reconnect and no subscription rebuild. It is now logged on its own: one Information line per tag on entry ("source node is temporarily unable to compute this value…") and one on recovery, not one per notification. Read errors name the symbolic status (0x80040000 BadResourceUnavailable (source node temporarily unable to compute the value)). Every other Bad read now carries its symbolic name too.InstanceActorused to overwrite the scalar attribute withnulland re-notify scripts and alarms. Bad with no value is now a quality-only transition: the last-known value is kept, quality goes Bad, the update is published to the stream, and scripts, alarms and waiters are not re-evaluated. This matches the existing connection-drop rule (DCL design, "Connection drop detection"). A Bad update that does carry a value still stores it. This applies to every source, not only OtOpcUa, and also covers the DCL's own tag-resolution Bad+null push. Doc: Component-DataConnectionLayer.md.OpcUaResourceUnavailableTests(Good, then 0x80040000, then 0x80040000, then Good: quality goes Good/Bad/Bad/Good, no Disconnected, no unsubscribe, exactly one entry log and one recovery log; the read names the status; 0x80040000 is not connection-level, with BadSessionIdInvalid as a positive control), andInstanceActor_BadQualityWithNullValue_KeepsLastKnownValue_AndRecoversOnGoodplus a guard for Bad-with-value. Mutation-proved: with the InstanceActor branch disabled, and separately with the adapter tracking and naming reverted, 3 of the new tests fail.Not covered: alarm quality and the flow-debugger display carry no OPC UA status code, only Good/Bad/Uncertain, so they need no change. Adjacent bug filed as #117 (non-zero Good codes map to Uncertain).
Evidence:
dotnet build ZB.MOM.WW.ScadaBridge.slnx0 warnings / 0 errors; DataConnectionLayer.Tests 483 passed (4 skipped), SiteRuntime.Tests 1016, Transport.Tests 232, Transport.IntegrationTests 151, ScriptAnalysis.Tests 103 — all green. Not deployed anywhere.