fix(dcl): map OPC UA status by severity bits so all Good subcodes are Good (ScadaBridge#117)

MapStatusCode returned Uncertain for any non-zero non-Bad code, so GoodLocalOverride,
GoodClamped and info-bit Good variants degraded to Uncertain. Now top two bits decide:
00 Good, 01 Uncertain, 10/11 Bad (BadResourceUnavailable stays Bad, #112 untouched).
The SDK IsGood/IsUncertain(uint) helpers were tried first and failed the subcode tests,
so an explicit mask is used. MxGateway mapper uses the OPC-DA quality byte (>=192 Good)
and is not affected.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
Joseph Doherty
2026-11-10 04:48:33 -05:00
parent 456c6659e7
commit fb04b54d62
2 changed files with 37 additions and 6 deletions
@@ -528,13 +528,20 @@ public class OpcUaDataConnection
}
/// <summary>
/// Maps OPC UA StatusCode to QualityCode.
/// StatusCode 0 = Good, high bit set = Bad, otherwise Uncertain.
/// Maps an OPC UA StatusCode to QualityCode by its severity bits (top two bits: 00 Good,
/// 01 Uncertain, 10/11 Bad), so every Good subcode (GoodLocalOverride, GoodClamped, info-bit
/// variants, ...) is Good (ScadaBridge#117). BadResourceUnavailable is a plain Bad here
/// (ScadaBridge#112 handles it separately and never as a connection fault).
/// </summary>
private static QualityCode MapStatusCode(uint statusCode)
internal static QualityCode MapStatusCode(uint statusCode)
{
if (statusCode == 0) return QualityCode.Good;
if ((statusCode & 0x80000000) != 0) return QualityCode.Bad;
return QualityCode.Uncertain;
// Explicit severity mask: the SDK's StatusCode.IsGood/IsUncertain(uint) helpers did not
// treat Good subcodes / info-bit variants as Good in the pinned SDK (tests prove it).
return (statusCode & 0xC0000000u) switch
{
0x00000000u => QualityCode.Good,
0x40000000u => QualityCode.Uncertain,
_ => QualityCode.Bad,
};
}
}