feat(dcl): MxGateway StreamAlarms adapter (snapshot + live transitions, reconnecting)
Adds IAlarmSubscribableConnection to MxGatewayDataConnection (shared session-less feed, ref-counted), IMxGatewayClient.RunAlarmStreamAsync over the package StreamAlarmsAsync with internal reconnect, and MxGatewayAlarmMapper (AlarmFeedMessage/OnAlarmTransitionEvent -> NativeAlarmTransition). Behavior verified against a live gateway in Task 28; mapper unit-tested.
This commit is contained in:
@@ -58,6 +58,12 @@ public sealed class FakeMxGatewayClient : IMxGatewayClient, IMxGatewayClientFact
|
||||
ct.ThrowIfCancellationRequested(); // …or FaultEventLoop() faults it to simulate a stream break
|
||||
}
|
||||
|
||||
public Task RunAlarmStreamAsync(
|
||||
string? alarmFilterPrefix,
|
||||
Action<ZB.MOM.WW.ScadaBridge.Commons.Types.Alarms.NativeAlarmTransition> onTransition,
|
||||
CancellationToken ct = default)
|
||||
=> Task.CompletedTask; // no alarm feed in the fake
|
||||
|
||||
public ValueTask DisposeAsync() => ValueTask.CompletedTask;
|
||||
|
||||
/// <summary>Simulate a stream break so the adapter raises Disconnected.</summary>
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
using ZB.MOM.WW.MxGateway.Contracts.Proto;
|
||||
using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;
|
||||
using CommonsTransitionKind = ZB.MOM.WW.ScadaBridge.Commons.Types.Enums.AlarmTransitionKind;
|
||||
using ProtoConditionState = ZB.MOM.WW.MxGateway.Contracts.Proto.AlarmConditionState;
|
||||
using ProtoTransitionKind = ZB.MOM.WW.MxGateway.Contracts.Proto.AlarmTransitionKind;
|
||||
|
||||
namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests;
|
||||
|
||||
/// <summary>Task-12: pure MxGateway alarm-feed proto → NativeAlarmTransition mapping.</summary>
|
||||
public class MxGatewayAlarmMapperTests
|
||||
{
|
||||
[Fact]
|
||||
public void MapTransition_AckTransition_IsAcknowledgedWithOperator()
|
||||
{
|
||||
var ev = new OnAlarmTransitionEvent
|
||||
{
|
||||
AlarmFullReference = "Tank01.Level.HiHi",
|
||||
SourceObjectReference = "Tank01",
|
||||
AlarmTypeName = "AnalogLimitAlarm.HiHi",
|
||||
TransitionKind = ProtoTransitionKind.Acknowledge,
|
||||
Severity = 600,
|
||||
OperatorUser = "operator1",
|
||||
OperatorComment = "ack",
|
||||
Category = "Process",
|
||||
Description = "hi"
|
||||
};
|
||||
|
||||
var t = MxGatewayAlarmMapper.MapTransition(ev);
|
||||
|
||||
Assert.Equal(CommonsTransitionKind.Acknowledge, t.Kind);
|
||||
Assert.True(t.Condition.Active);
|
||||
Assert.True(t.Condition.Acknowledged);
|
||||
Assert.Equal(600, t.Condition.Severity);
|
||||
Assert.Equal("operator1", t.OperatorUser);
|
||||
Assert.Equal("Tank01", t.SourceObjectReference);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapConditionState_ActiveAcked_To_ActiveTrue_AckTrue()
|
||||
{
|
||||
var c = MxGatewayAlarmMapper.MapConditionState(ProtoConditionState.ActiveAcked, severity: 600);
|
||||
Assert.True(c.Active);
|
||||
Assert.True(c.Acknowledged);
|
||||
Assert.Equal(600, c.Severity);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapSnapshot_ActiveUnacked_IsSnapshotKind()
|
||||
{
|
||||
var snap = new ActiveAlarmSnapshot
|
||||
{
|
||||
AlarmFullReference = "Tank01.Level.Hi",
|
||||
SourceObjectReference = "Tank01",
|
||||
AlarmTypeName = "AnalogLimitAlarm.Hi",
|
||||
CurrentState = ProtoConditionState.Active,
|
||||
Severity = 1500 // out of range — must clamp
|
||||
};
|
||||
|
||||
var t = MxGatewayAlarmMapper.MapSnapshot(snap);
|
||||
|
||||
Assert.Equal(CommonsTransitionKind.Snapshot, t.Kind);
|
||||
Assert.True(t.Condition.Active);
|
||||
Assert.False(t.Condition.Acknowledged);
|
||||
Assert.Equal(1000, t.Condition.Severity);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user