using Akka.Actor;
using Akka.Event;
using ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian;
namespace ZB.MOM.WW.OtOpcUa.Runtime.Historian;
///
/// Thin actor wrapper around . Engine code (ScriptedAlarmActor,
/// Galaxy native alarm bridge, AB CIP ALMD reader) tells s to this
/// actor; the actor enqueues them on the sink fire-and-forget. Production deployments register
/// against IAlarmHistorianSink; the sink owns the
/// durable queue + drain-to-Wonderware-pipe loop. The actor here owns nothing operational beyond
/// the message contract — its job is to keep the engine actors on Akka's mailbox without blocking
/// them on disk I/O or pipe handshakes.
///
/// Query queue depth + drain health via .
///
public sealed class HistorianAdapterActor : ReceiveActor
{
public sealed record GetStatus
{
public static readonly GetStatus Instance = new();
}
private readonly IAlarmHistorianSink _sink;
private readonly ILoggingAdapter _log = Context.GetLogger();
/// Creates the props for a HistorianAdapterActor instance.
/// The alarm historian sink implementation, or null to use a null sink.
/// Props configured for creating a HistorianAdapterActor.
public static Props Props(IAlarmHistorianSink? sink = null) =>
Akka.Actor.Props.Create(() => new HistorianAdapterActor(sink ?? NullAlarmHistorianSink.Instance));
/// Initializes a new instance of the HistorianAdapterActor class.
/// The alarm historian sink to forward enqueued events to.
public HistorianAdapterActor(IAlarmHistorianSink sink)
{
_sink = sink;
Receive(evt =>
{
// Fire-and-forget: SqliteStoreAndForwardSink persists to local SQLite synchronously
// inside EnqueueAsync (it returns once the row is committed), so we don't block on
// network/pipe latency. Failures are surfaced via GetStatus's LastError + drain state.
_ = EnqueueAsync(evt);
});
Receive(_ => Sender.Tell(_sink.GetStatus()));
}
private async Task EnqueueAsync(AlarmHistorianEvent evt)
{
try
{
await _sink.EnqueueAsync(evt, CancellationToken.None);
}
catch (Exception ex)
{
_log.Error(ex, "Historian sink rejected event for {AlarmId} at {Ts}",
evt.AlarmId, evt.TimestampUtc);
}
}
}