c7411700dc
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.
72 lines
3.1 KiB
C#
72 lines
3.1 KiB
C#
using ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Adapters;
|
|
|
|
namespace ZB.MOM.WW.ScadaBridge.DataConnectionLayer.Tests.Adapters;
|
|
|
|
/// <summary>
|
|
/// In-memory fake <see cref="IMxGatewayClient"/> for adapter unit tests. Lets tests
|
|
/// drive the event loop (push updates / fault the stream) and stub read/write/browse.
|
|
/// </summary>
|
|
public sealed class FakeMxGatewayClient : IMxGatewayClient, IMxGatewayClientFactory
|
|
{
|
|
public MxGatewayConnectionOptions? ConnectedWith;
|
|
public readonly List<string> Subscribed = new();
|
|
public readonly List<string> Unsubscribed = new();
|
|
public readonly TaskCompletionSource EventLoopGate = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
public Action<MxValueUpdate>? OnUpdate;
|
|
public Func<IReadOnlyList<string>, IReadOnlyList<MxReadOutcome>>? ReadHandler;
|
|
public Func<IReadOnlyList<(string TagPath, object? Value)>, IReadOnlyList<MxWriteOutcome>>? WriteHandler;
|
|
public Func<string?, (IReadOnlyList<MxBrowseChild>, bool)>? BrowseHandler;
|
|
private int _nextHandle;
|
|
|
|
public IMxGatewayClient Create() => this;
|
|
|
|
public Task ConnectAsync(MxGatewayConnectionOptions o, CancellationToken ct = default)
|
|
{
|
|
ConnectedWith = o;
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task DisconnectAsync(CancellationToken ct = default) => Task.CompletedTask;
|
|
|
|
public Task<string> SubscribeAsync(string tag, CancellationToken ct = default)
|
|
{
|
|
var id = (++_nextHandle).ToString();
|
|
Subscribed.Add(tag);
|
|
return Task.FromResult(id);
|
|
}
|
|
|
|
public Task UnsubscribeAsync(string id, CancellationToken ct = default)
|
|
{
|
|
Unsubscribed.Add(id);
|
|
return Task.CompletedTask;
|
|
}
|
|
|
|
public Task<IReadOnlyList<MxReadOutcome>> ReadAsync(IReadOnlyList<string> tags, CancellationToken ct = default)
|
|
=> Task.FromResult(ReadHandler!(tags));
|
|
|
|
public Task<IReadOnlyList<MxWriteOutcome>> WriteAsync(IReadOnlyList<(string TagPath, object? Value)> w, CancellationToken ct = default)
|
|
=> Task.FromResult(WriteHandler!(w));
|
|
|
|
public Task<(IReadOnlyList<MxBrowseChild> Children, bool Truncated)> BrowseChildrenAsync(string? p, CancellationToken ct = default)
|
|
=> Task.FromResult(BrowseHandler!(p));
|
|
|
|
public async Task RunEventLoopAsync(Action<MxValueUpdate> onUpdate, CancellationToken ct = default)
|
|
{
|
|
OnUpdate = onUpdate;
|
|
using var reg = ct.Register(() => EventLoopGate.TrySetResult());
|
|
await EventLoopGate.Task; // test completes this to end the loop…
|
|
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>
|
|
public void FaultEventLoop() => EventLoopGate.TrySetException(new Exception("stream broke"));
|
|
}
|