diff --git a/src/ZB.MOM.WW.ScadaBridge.DataConnectionLayer/Adapters/OpcUaDataConnection.cs b/src/ZB.MOM.WW.ScadaBridge.DataConnectionLayer/Adapters/OpcUaDataConnection.cs
index 4252a900..3f927c84 100644
--- a/src/ZB.MOM.WW.ScadaBridge.DataConnectionLayer/Adapters/OpcUaDataConnection.cs
+++ b/src/ZB.MOM.WW.ScadaBridge.DataConnectionLayer/Adapters/OpcUaDataConnection.cs
@@ -528,13 +528,20 @@ public class OpcUaDataConnection
}
///
- /// 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).
///
- 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,
+ };
}
}
diff --git a/tests/ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests/OpcUaStatusCodeMappingTests.cs b/tests/ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests/OpcUaStatusCodeMappingTests.cs
new file mode 100644
index 00000000..f669c0b5
--- /dev/null
+++ b/tests/ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests/OpcUaStatusCodeMappingTests.cs
@@ -0,0 +1,24 @@
+using Opc.Ua;
+using ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Protocol;
+using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;
+
+namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests;
+
+/// ScadaBridge#117: quality follows the StatusCode severity bits, not "== 0".
+public class OpcUaStatusCodeMappingTests
+{
+ [Theory]
+ [InlineData(StatusCodes.Good, QualityCode.Good)]
+ [InlineData(StatusCodes.GoodLocalOverride, QualityCode.Good)]
+ [InlineData(StatusCodes.GoodClamped, QualityCode.Good)]
+ [InlineData(StatusCodes.Good | 0x0480u, QualityCode.Good)] // info bits set
+ [InlineData(StatusCodes.Uncertain, QualityCode.Uncertain)]
+ [InlineData(StatusCodes.UncertainLastUsableValue, QualityCode.Uncertain)]
+ [InlineData(StatusCodes.UncertainSensorNotAccurate, QualityCode.Uncertain)]
+ [InlineData(StatusCodes.Bad, QualityCode.Bad)]
+ [InlineData(StatusCodes.BadNodeIdUnknown, QualityCode.Bad)]
+ [InlineData(StatusCodes.BadResourceUnavailable, QualityCode.Bad)]
+ [InlineData(0xC0000000u, QualityCode.Bad)]
+ public void MapStatusCode_UsesSeverityBits(uint code, QualityCode expected) =>
+ Assert.Equal(expected, OpcUaDataConnection.MapStatusCode(code));
+}