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:
@@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
/// <summary>ScadaBridge#117: quality follows the StatusCode severity bits, not "== 0".</summary>
|
||||
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));
|
||||
}
|
||||
Reference in New Issue
Block a user