fix(driver-galaxy): resolve Medium code-review finding (Driver.Galaxy-004)
Add StatusCodeMap.ToQualityCategoryByte(uint) so the StatusCode → quality-byte mapping lives in one place next to its inverse (FromQualityByte). GalaxyDriver OnPumpDataChange now delegates to the helper instead of duplicating the shift+switch inline; a future edit to the OPC UA bit layout cannot silently desync the probe-health decode. Unit tests in StatusCodeMapTests pin all three category buckets and the round-trip invariant. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1010,15 +1010,11 @@ public sealed class GalaxyDriver
|
||||
if (_probeWatcher is not null
|
||||
&& args.FullReference.EndsWith(PerPlatformProbeWatcher.ProbeSuffix, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
// The probe decoder takes a raw quality byte; recover it from the StatusCode
|
||||
// top byte (Good=0x00 → byte 192, Uncertain=0x40 → byte 64, Bad=0x80 → byte 0).
|
||||
var qualityByte = (byte)((args.Snapshot.StatusCode >> 30) & 0x3) switch
|
||||
{
|
||||
0 => 192,
|
||||
1 => 64,
|
||||
_ => 0,
|
||||
};
|
||||
_probeWatcher.OnProbeValueChanged(args.FullReference, args.Snapshot.Value, (byte)qualityByte);
|
||||
// The probe decoder takes a raw quality byte. Recover it via the canonical
|
||||
// StatusCodeMap.ToQualityCategoryByte helper so the mapping lives in one
|
||||
// place next to its inverse (FromQualityByte) and cannot desync silently.
|
||||
var qualityByte = StatusCodeMap.ToQualityCategoryByte(args.Snapshot.StatusCode);
|
||||
_probeWatcher.OnProbeValueChanged(args.FullReference, args.Snapshot.Value, qualityByte);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -101,6 +101,25 @@ internal static class StatusCodeMap
|
||||
return Bad;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Convert an OPC UA <see cref="StatusCode"/> uint back to the OPC DA quality category
|
||||
/// byte — Good=192, Uncertain=64, Bad=0 — by extracting the top-two bits of the
|
||||
/// high word. This is the inverse of the category-bucket arm of
|
||||
/// <see cref="FromQualityByte"/>. It is intentionally lossy (substatus bits are not
|
||||
/// round-tripped) because the sole consumer
|
||||
/// (<see cref="ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Health.PerPlatformProbeWatcher"/>)
|
||||
/// only tests <c>qualityByte < 192</c> to distinguish Running from Stopped. Keeping
|
||||
/// the round-trip in one place means a future change to the OPC UA bit layout cannot
|
||||
/// silently desync the probe-health decode.
|
||||
/// </summary>
|
||||
public static byte ToQualityCategoryByte(uint statusCode) =>
|
||||
(byte)(((statusCode >> 30) & 0x3u) switch
|
||||
{
|
||||
0u => 192u, // Good — top two bits 00b → OPC DA 0xC0
|
||||
1u => 64u, // Uncertain — top two bits 01b → OPC DA 0x40
|
||||
_ => 0u, // Bad — top two bits 10b/11b → OPC DA 0x00
|
||||
});
|
||||
|
||||
private static uint Categorize(byte q, ILogger? logger)
|
||||
{
|
||||
if (q >= 192) { Log(logger, q, "Good"); return Good; }
|
||||
|
||||
Reference in New Issue
Block a user