DCL OPC UA: MapStatusCode maps every non-zero Good status (GoodLocalOverride, info bits) to Uncertain #117

Closed
opened 2026-11-10 04:38:19 -05:00 by dohertj2 · 1 comment
Owner

Found while fixing #112 (code reading, not reproduced against a server).

OpcUaDataConnection.MapStatusCode (src/ZB.MOM.WW.ScadaBridge.DataConnectionLayer/Adapters/OpcUaDataConnection.cs):

if (statusCode == 0) return QualityCode.Good;
if ((statusCode & 0x80000000) != 0) return QualityCode.Bad;
return QualityCode.Uncertain;

OPC UA severity is the top two bits (00 Good, 01 Uncertain, 10 Bad). Any Good status with a non-zero sub-code or info bits — e.g. GoodLocalOverride (0x00960000), GoodClamped (0x00300000), a Good DataValue carrying the overflow / limit info bits — therefore reaches the Instance Actor as Uncertain. A quality-gated WaitAsync, alarm logic or the Debug View would treat a healthy value as Uncertain.

Proposed fix: (statusCode & 0xC0000000) switch { 0 => Good, 0x40000000 => Uncertain, _ => Bad }, with a test over a few real codes (Good sub-codes, UncertainLastUsableValue 0x40900000, Bad codes). Check the read path (ReadAsync/ReadBatchAsync) uses the same mapping, and whether any MxGateway mapping has the same shape.

Found while fixing #112 (code reading, not reproduced against a server). `OpcUaDataConnection.MapStatusCode` (`src/ZB.MOM.WW.ScadaBridge.DataConnectionLayer/Adapters/OpcUaDataConnection.cs`): ```csharp if (statusCode == 0) return QualityCode.Good; if ((statusCode & 0x80000000) != 0) return QualityCode.Bad; return QualityCode.Uncertain; ``` OPC UA severity is the top two bits (`00` Good, `01` Uncertain, `10` Bad). Any Good status with a non-zero sub-code or info bits — e.g. `GoodLocalOverride` (0x00960000), `GoodClamped` (0x00300000), a Good DataValue carrying the overflow / limit info bits — therefore reaches the Instance Actor as **Uncertain**. A quality-gated `WaitAsync`, alarm logic or the Debug View would treat a healthy value as Uncertain. Proposed fix: `(statusCode & 0xC0000000) switch { 0 => Good, 0x40000000 => Uncertain, _ => Bad }`, with a test over a few real codes (Good sub-codes, UncertainLastUsableValue 0x40900000, Bad codes). Check the read path (`ReadAsync`/`ReadBatchAsync`) uses the same mapping, and whether any MxGateway mapping has the same shape.
dohertj2 added the type:bugpriority:p3 labels 2026-11-10 04:38:19 -05:00
Author
Owner

Fixed in fb04b54d (main). OpcUaDataConnection.MapStatusCode now maps by severity bits (00 Good, 01 Uncertain, 10/11 Bad), so GoodLocalOverride, GoodClamped and info-bit Good variants are Good; BadResourceUnavailable stays Bad (#112 untouched). Note: the SDK StatusCode.IsGood/IsUncertain(uint) helpers failed the subcode tests in the pinned SDK, so an explicit 0xC0000000 mask is used. MxGateway mapper uses the OPC-DA quality byte (>=192 Good) and is not affected. Tests: OpcUaStatusCodeMappingTests (11 cases), mutation-proved (old == 0 check fails 4). Solution builds 0 warnings; DCL 494 passed/4 skipped, SiteRuntime 1016 passed.

Fixed in fb04b54d (main). OpcUaDataConnection.MapStatusCode now maps by severity bits (00 Good, 01 Uncertain, 10/11 Bad), so GoodLocalOverride, GoodClamped and info-bit Good variants are Good; BadResourceUnavailable stays Bad (#112 untouched). Note: the SDK StatusCode.IsGood/IsUncertain(uint) helpers failed the subcode tests in the pinned SDK, so an explicit 0xC0000000 mask is used. MxGateway mapper uses the OPC-DA quality byte (>=192 Good) and is not affected. Tests: OpcUaStatusCodeMappingTests (11 cases), mutation-proved (old == 0 check fails 4). Solution builds 0 warnings; DCL 494 passed/4 skipped, SiteRuntime 1016 passed.
Sign in to join this conversation.