Wrong OPC UA status-code constants in three existing drivers (FF-2) #497

Closed
opened 2026-07-24 21:00:09 -04:00 by dohertj2 · 1 comment
Owner

Found while implementing SqlPollReader (verified by reflecting over Opc.Ua.StatusCodes against the pinned SDK, not by copying siblings). All PRE-EXISTING, unrelated to the Sql branch, and client-visible (OPC UA clients branch on status): (1) src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Mapping/SampleMapper.cs names 0x800E0000 'BadNoData' — that value is BadServerHalted; BadNoData is 0x809B0000. (2) GalaxyDriver.cs:878 names 0x800B0000 'BadTimeout' — that is BadServiceUnsupported; BadTimeout is 0x800A0000. (3) TwinCATStatusMapper BadTypeMismatch = 0x80730000; real value is 0x80740000. Each needs its own fix + test; do not fold into the Sql work.

Found while implementing SqlPollReader (verified by reflecting over Opc.Ua.StatusCodes against the pinned SDK, not by copying siblings). All PRE-EXISTING, unrelated to the Sql branch, and client-visible (OPC UA clients branch on status): (1) src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Gateway/Mapping/SampleMapper.cs names 0x800E0000 'BadNoData' — that value is BadServerHalted; BadNoData is 0x809B0000. (2) GalaxyDriver.cs:878 names 0x800B0000 'BadTimeout' — that is BadServiceUnsupported; BadTimeout is 0x800A0000. (3) TwinCATStatusMapper BadTypeMismatch = 0x80730000; real value is 0x80740000. Each needs its own fix + test; do not fold into the Sql work.
Author
Owner

Fixed on fix/sql-driver-followups (b95b99ba).

Scope grew from 3 constants to 16. All values verified by reflecting over Opc.Ua.StatusCodes in the pinned SDK (1.5.378.106), not by copying siblings.

The three named here were confirmed exactly as reported. Two additions:

  1. BadTypeMismatch = 0x80730000 was in four drivers, not one — TwinCAT (named here) plus FOCAS, AbLegacy and AbCip. Every call site is a FormatException/InvalidCastException conversion failure, so the name was right and the value was wrong in all four. 0x80730000 is BadWriteNotSupported.

  2. A reflection parity guard found nine more. Rather than six point fixes I added StatusCodeParityTests to Core.Abstractions.Tests (which already project-references every driver): it reflects over every const uint in the deployed ZB.MOM.WW.OtOpcUa.Driver.*.dll whose name reads as a status code and asserts it equals Opc.Ua.StatusCodes.<name>. It went red on nine defects nobody had reported:

driver constant was is actually now
Galaxy GoodLocalOverride 0x00D80000 not a UA code 0x00960000
Galaxy UncertainLastUsableValue 0x40A40000 UncertainDataSubNormal 0x40900000
Galaxy UncertainSensorNotAccurate 0x408D0000 not a UA code 0x40930000
Galaxy UncertainEngineeringUnitsExceeded 0x408E0000 not a UA code 0x40940000
Galaxy UncertainSubNormal 0x408F0000 UncertainNoCommunicationLastUsableValue 0x40950000
TwinCAT BadOutOfService 0x80BE0000 BadProtocolVersionUnsupported 0x808D0000
TwinCAT BadInvalidState 0x80350000 BadAttributeIdInvalid 0x80AF0000
AbCip + AbLegacy GoodMoreData 0x00A70000 GoodCommunicationEvent 0x00A60000
Historian.Gateway GatewayQualityMapper Good_LocalOverride 0x00D80000 not a UA code 0x00960000

Galaxy’s whole Uncertain block reads as though the OPC DA quality byte could be shifted into the UA substatus position — it cannot; the substatuses are an unrelated enumeration. GatewayQualityMapper already held the correct table, which is what the Galaxy values were reconciled against.

Two inline literals were hoisted into named constants so the guard can see them at all (reflection cannot see a literal at a call site — which is precisely how GalaxyDriver’s 0x800B0000u, // BadTimeout survived): Galaxy’s into StatusCodeMap, and GatewayQualityMapper’s 15-entry table into a new HistorianStatusCodes.

Also corrected: tests that pinned the wrong values (StatusCodeMapTests, SampleMapperTests, GatewayQualityMapperTests — all written from the same bad source), and stale constants in two design docs, one of them the unexecuted MTConnect driver design, which would have propagated BadNoData’s wrong value into a brand-new driver.

The CLI’s SnapshotFormatter value→name table was checked against the SDK and is correct; no change needed.

The drivers stay SDK-free by design — only the test project references Opc.Ua.Core, as the oracle. Guard now checks 109 constants green; full solution builds; all 41 unit-test projects pass.

Fixed on `fix/sql-driver-followups` (`b95b99ba`). **Scope grew from 3 constants to 16.** All values verified by reflecting over `Opc.Ua.StatusCodes` in the pinned SDK (1.5.378.106), not by copying siblings. The three named here were confirmed exactly as reported. Two additions: 1. **`BadTypeMismatch = 0x80730000` was in four drivers, not one** — TwinCAT (named here) plus **FOCAS, AbLegacy and AbCip**. Every call site is a `FormatException`/`InvalidCastException` conversion failure, so the *name* was right and the *value* was wrong in all four. `0x80730000` is `BadWriteNotSupported`. 2. **A reflection parity guard found nine more.** Rather than six point fixes I added `StatusCodeParityTests` to `Core.Abstractions.Tests` (which already project-references every driver): it reflects over every `const uint` in the deployed `ZB.MOM.WW.OtOpcUa.Driver.*.dll` whose name reads as a status code and asserts it equals `Opc.Ua.StatusCodes.<name>`. It went **red on nine defects nobody had reported**: | driver | constant | was | is actually | now | |---|---|---|---|---| | Galaxy | `GoodLocalOverride` | `0x00D80000` | *not a UA code* | `0x00960000` | | Galaxy | `UncertainLastUsableValue` | `0x40A40000` | `UncertainDataSubNormal` | `0x40900000` | | Galaxy | `UncertainSensorNotAccurate` | `0x408D0000` | *not a UA code* | `0x40930000` | | Galaxy | `UncertainEngineeringUnitsExceeded` | `0x408E0000` | *not a UA code* | `0x40940000` | | Galaxy | `UncertainSubNormal` | `0x408F0000` | `UncertainNoCommunicationLastUsableValue` | `0x40950000` | | TwinCAT | `BadOutOfService` | `0x80BE0000` | `BadProtocolVersionUnsupported` | `0x808D0000` | | TwinCAT | `BadInvalidState` | `0x80350000` | `BadAttributeIdInvalid` | `0x80AF0000` | | AbCip + AbLegacy | `GoodMoreData` | `0x00A70000` | `GoodCommunicationEvent` | `0x00A60000` | | Historian.Gateway | `GatewayQualityMapper` `Good_LocalOverride` | `0x00D80000` | *not a UA code* | `0x00960000` | Galaxy’s whole Uncertain block reads as though the OPC DA quality byte could be shifted into the UA substatus position — it cannot; the substatuses are an unrelated enumeration. `GatewayQualityMapper` already held the **correct** table, which is what the Galaxy values were reconciled against. **Two inline literals were hoisted into named constants** so the guard can see them at all (reflection cannot see a literal at a call site — which is precisely how `GalaxyDriver`’s `0x800B0000u, // BadTimeout` survived): Galaxy’s into `StatusCodeMap`, and `GatewayQualityMapper`’s 15-entry table into a new `HistorianStatusCodes`. Also corrected: tests that pinned the wrong values (`StatusCodeMapTests`, `SampleMapperTests`, `GatewayQualityMapperTests` — all written from the same bad source), and stale constants in two design docs, **one of them the unexecuted MTConnect driver design**, which would have propagated `BadNoData`’s wrong value into a brand-new driver. The CLI’s `SnapshotFormatter` value→name table was checked against the SDK and is correct; no change needed. The drivers stay SDK-free by design — only the test project references `Opc.Ua.Core`, as the oracle. Guard now checks 109 constants green; full solution builds; all 41 unit-test projects pass.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: dohertj2/lmxopcua#497