feat(runtime): F11 — HistorianAdapterActor wired to IAlarmHistorianSink
Some checks failed
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been cancelled
v2-ci / build (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been cancelled
v2-ci / integration (push) Has been cancelled
Some checks failed
v2-ci / unit-tests (tests/Core/ZB.MOM.WW.OtOpcUa.Cluster.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.ControlPlane.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.OpcUaServer.Tests) (push) Has been cancelled
v2-ci / build (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Runtime.Tests) (push) Has been cancelled
v2-ci / unit-tests (tests/Server/ZB.MOM.WW.OtOpcUa.Security.Tests) (push) Has been cancelled
v2-ci / integration (push) Has been cancelled
Reshapes the placeholder buffered-counter actor into a thin fire-and-forget bridge over the existing IAlarmHistorianSink contract. Default sink is NullAlarmHistorianSink; production deployments override the DI binding to SqliteStoreAndForwardSink wrapping WonderwareHistorianClient (the v1 components in src/Drivers/ZB.MOM.WW.OtOpcUa.Driver.Historian.Wonderware* are reused verbatim — actor is just a mailbox-friendly entry point). - HistorianAdapterActor.Props(IAlarmHistorianSink?) — null defaults to NullAlarmHistorianSink - Receive<AlarmHistorianEvent>: fire-and-forget sink.EnqueueAsync - Receive<GetStatus>: returns sink.GetStatus() (queue depth + drain state) - ServiceCollectionExtensions.AddOtOpcUaRuntime registers the default sink - WithOtOpcUaRuntimeActors spawns the actor + registers HistorianAdapterActorKey - Program.cs calls AddOtOpcUaRuntime when hasDriver Tests: 2 new (forward-to-sink + GetStatus). Runtime suite 17 → 18.
This commit is contained in:
@@ -1,9 +1,12 @@
|
||||
using System.Collections.Concurrent;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using Akka.Actor;
|
||||
using Shouldly;
|
||||
using Xunit;
|
||||
using ZB.MOM.WW.OtOpcUa.Commons.Types;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.Abstractions;
|
||||
using ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian;
|
||||
using ZB.MOM.WW.OtOpcUa.Runtime.Health;
|
||||
using ZB.MOM.WW.OtOpcUa.Runtime.Historian;
|
||||
using ZB.MOM.WW.OtOpcUa.Runtime.Tests.Harness;
|
||||
@@ -61,14 +64,63 @@ public sealed class HealthProbeActorTests : RuntimeActorTestBase
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void HistorianAdapterActor_buffers_rows()
|
||||
public void HistorianAdapterActor_forwards_events_to_injected_sink()
|
||||
{
|
||||
var actor = Sys.ActorOf(HistorianAdapterActor.Props());
|
||||
for (var i = 0; i < 5; i++)
|
||||
actor.Tell(new HistorianAdapterActor.HistoryRow("driver-a", $"tag-{i}", i, DateTime.UtcNow));
|
||||
var sink = new RecordingSink();
|
||||
var actor = Sys.ActorOf(HistorianAdapterActor.Props(sink));
|
||||
|
||||
ExpectNoMsg(TimeSpan.FromMilliseconds(100));
|
||||
// No direct readback of the count from a sealed actor — assert by Ask of a self-probe later
|
||||
// when the engine wiring lands (F11). For now this asserts the actor accepts the contract.
|
||||
for (var i = 0; i < 5; i++)
|
||||
actor.Tell(new AlarmHistorianEvent(
|
||||
AlarmId: $"alm-{i}",
|
||||
EquipmentPath: "Plant/LineA",
|
||||
AlarmName: $"Alarm{i}",
|
||||
AlarmTypeName: "LimitAlarm",
|
||||
Severity: AlarmSeverity.High,
|
||||
EventKind: "Activated",
|
||||
Message: $"Test alarm {i}",
|
||||
User: "system",
|
||||
Comment: null,
|
||||
TimestampUtc: DateTime.UtcNow));
|
||||
|
||||
AwaitCondition(() => sink.Enqueued.Count == 5, TimeSpan.FromSeconds(2));
|
||||
sink.Enqueued.Select(e => e.AlarmId).OrderBy(s => s).ShouldBe(
|
||||
new[] { "alm-0", "alm-1", "alm-2", "alm-3", "alm-4" });
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HistorianAdapterActor_returns_sink_status_via_GetStatus()
|
||||
{
|
||||
var sink = new RecordingSink();
|
||||
var actor = Sys.ActorOf(HistorianAdapterActor.Props(sink));
|
||||
|
||||
actor.Tell(new AlarmHistorianEvent(
|
||||
"alm-x", "Plant/LineB", "OffNormal", "OffNormalAlarm",
|
||||
AlarmSeverity.Low, "Activated", "msg", "system", null, DateTime.UtcNow));
|
||||
AwaitCondition(() => sink.Enqueued.Count == 1, TimeSpan.FromSeconds(2));
|
||||
|
||||
var status = await actor.Ask<HistorianSinkStatus>(
|
||||
HistorianAdapterActor.GetStatus.Instance, TimeSpan.FromSeconds(2));
|
||||
|
||||
status.QueueDepth.ShouldBe(1);
|
||||
status.DrainState.ShouldBe(HistorianDrainState.Idle);
|
||||
}
|
||||
|
||||
private sealed class RecordingSink : IAlarmHistorianSink
|
||||
{
|
||||
public ConcurrentBag<AlarmHistorianEvent> Enqueued { get; } = [];
|
||||
|
||||
public Task EnqueueAsync(AlarmHistorianEvent evt, CancellationToken cancellationToken)
|
||||
{
|
||||
Enqueued.Add(evt);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public HistorianSinkStatus GetStatus() => new(
|
||||
QueueDepth: Enqueued.Count,
|
||||
DeadLetterDepth: 0,
|
||||
LastDrainUtc: null,
|
||||
LastSuccessUtc: null,
|
||||
LastError: null,
|
||||
DrainState: HistorianDrainState.Idle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,11 +46,14 @@ public sealed class ServiceCollectionExtensionsTests
|
||||
{
|
||||
var driverHost = host.Services.GetRequiredService<IRequiredActor<DriverHostActorKey>>();
|
||||
var dbHealth = host.Services.GetRequiredService<IRequiredActor<DbHealthProbeActorKey>>();
|
||||
var historian = host.Services.GetRequiredService<IRequiredActor<HistorianAdapterActorKey>>();
|
||||
|
||||
driverHost.ActorRef.ShouldNotBeNull();
|
||||
dbHealth.ActorRef.ShouldNotBeNull();
|
||||
historian.ActorRef.ShouldNotBeNull();
|
||||
driverHost.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.DriverHostActorName);
|
||||
dbHealth.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.DbHealthProbeActorName);
|
||||
historian.ActorRef.Path.Name.ShouldBe(ServiceCollectionExtensions.HistorianAdapterActorName);
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user