driver-galaxy: EventPump dispatches OnAlarmTransition family (PR B.1)
Second PR of the alarms-over-gateway epic (docs/plans/alarms-over-gateway.md). Depends on PR A.1 in mxaccessgw (merged) which added the OnAlarmTransitionEvent body + family. No runtime impact yet — the gateway doesn't emit the new family until A.3 ships; this PR just stops dropping it on the floor. EventPump.Dispatch becomes a switch on MxEventFamily. The new DispatchAlarmTransition decodes the proto event, runs the raw severity through MxAccessSeverityMapper (the same four-bucket ladder v1 used — 250/500/750/1000 boundaries per docs/v1/AlarmTracking.md), and fires an internal OnAlarmTransition event with a GalaxyAlarmTransition record carrying the full payload. Body absent or transition-kind unspecified → counted via galaxy.alarm_transitions.decoding_failures and dropped. Gateway version skew or worker malformed event therefore degrades to "fall back to the sub-attribute path" rather than crashing the pump. GalaxyDriver consumes the internal event in PR B.2 (next), wrapping it onto IAlarmSource.OnAlarmEvent. The richer fields (operator user + comment, original raise time, category) become visible on the OPC UA Part 9 condition once AlarmEventArgs gets extended in E.7. Tests: - MxAccessSeverityMapperTests — full bucket ladder + clamp behaviour for negative + out-of-range inputs. - EventPumpAlarmTests — raise/ack/clear sequence dispatches in order with operator metadata + original-raise preserved; unspecified kind drops; missing body drops; mixed data-change + alarm streams dispatch independently; OnWriteComplete / OperationComplete filtered out. Full Driver.Galaxy.Tests suite: 196 passed (was 191 — 5 new tests). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,43 @@
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Runtime;
|
||||
|
||||
namespace ZB.MOM.WW.OtOpcUa.Driver.Galaxy.Tests.Runtime;
|
||||
|
||||
/// <summary>
|
||||
/// Pins the four-bucket MxAccess severity → (AlarmSeverity, OPC UA numeric) ladder.
|
||||
/// Customers see no surprise re-classification when the v2 path takes over from
|
||||
/// v1's sub-attribute synthesis: the bucket boundaries match v1's
|
||||
/// <c>GalaxyAlarmTracker</c> per <c>docs/v1/AlarmTracking.md</c>.
|
||||
/// </summary>
|
||||
public sealed class MxAccessSeverityMapperTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData(0, AlarmSeverity.Low, MxAccessSeverityMapper.OpcUaSeverityLow)]
|
||||
[InlineData(1, AlarmSeverity.Low, MxAccessSeverityMapper.OpcUaSeverityLow)]
|
||||
[InlineData(249, AlarmSeverity.Low, MxAccessSeverityMapper.OpcUaSeverityLow)]
|
||||
[InlineData(250, AlarmSeverity.Medium, MxAccessSeverityMapper.OpcUaSeverityMedium)]
|
||||
[InlineData(499, AlarmSeverity.Medium, MxAccessSeverityMapper.OpcUaSeverityMedium)]
|
||||
[InlineData(500, AlarmSeverity.High, MxAccessSeverityMapper.OpcUaSeverityHigh)]
|
||||
[InlineData(749, AlarmSeverity.High, MxAccessSeverityMapper.OpcUaSeverityHigh)]
|
||||
[InlineData(750, AlarmSeverity.Critical, MxAccessSeverityMapper.OpcUaSeverityCritical)]
|
||||
[InlineData(999, AlarmSeverity.Critical, MxAccessSeverityMapper.OpcUaSeverityCritical)]
|
||||
[InlineData(int.MaxValue, AlarmSeverity.Critical, MxAccessSeverityMapper.OpcUaSeverityCritical)]
|
||||
public void Map_assigns_expected_bucket(int rawMxAccessSeverity, AlarmSeverity expectedBucket, int expectedOpcUaSeverity)
|
||||
{
|
||||
var (bucket, opcUa) = MxAccessSeverityMapper.Map(rawMxAccessSeverity);
|
||||
|
||||
bucket.ShouldBe(expectedBucket);
|
||||
opcUa.ShouldBe(expectedOpcUaSeverity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Map_clamps_negative_severities_into_low_bucket()
|
||||
{
|
||||
var (bucket, opcUa) = MxAccessSeverityMapper.Map(-100);
|
||||
|
||||
bucket.ShouldBe(AlarmSeverity.Low);
|
||||
opcUa.ShouldBe(MxAccessSeverityMapper.OpcUaSeverityLow);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user