@page "/alarms-historian" @* Live status of the local node's IAlarmHistorianSink (queue depth, drain state) via the HistorianAdapterActor.GetStatus query landed in F11. *@ @attribute [Microsoft.AspNetCore.Authorization.Authorize] @rendermode RenderMode.InteractiveServer @using Akka.Actor @using Akka.Hosting @using ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian @using ZB.MOM.WW.OtOpcUa.Runtime @using ZB.MOM.WW.OtOpcUa.Runtime.Historian @inject IRequiredActor HistorianActor @implements IDisposable

Alarms historian sink

Snapshot from the local node's HistorianAdapterActor. Default sink is a no-op (NullAlarmHistorianSink); production wires SqliteStoreAndForwardSink with the Wonderware historian sidecar behind it. Polling every @PollSeconds s.
@if (_status is null) {

Loading…

} else {
Queue
Depth@_status.QueueDepth
Dead-lettered@_status.DeadLetterDepth
Evicted (lifetime)@_status.EvictedCount
Drain state
State@_status.DrainState
Last drain@(_status.LastDrainUtc?.ToString("u") ?? "—")
Last success@(_status.LastSuccessUtc?.ToString("u") ?? "—")
@if (!string.IsNullOrWhiteSpace(_status.LastError)) {
Last error@_status.LastError
}
} @code { private const int PollSeconds = 5; private HistorianSinkStatus? _status; private Timer? _timer; protected override async Task OnInitializedAsync() { await RefreshAsync(); _timer = new Timer(_ => _ = InvokeAsync(RefreshAsync), null, TimeSpan.FromSeconds(PollSeconds), TimeSpan.FromSeconds(PollSeconds)); } private async Task RefreshAsync() { try { _status = await HistorianActor.ActorRef.Ask( HistorianAdapterActor.GetStatus.Instance, TimeSpan.FromSeconds(2)); StateHasChanged(); } catch { // Actor unavailable (admin-only node, not driver-role) — leave _status null and let // the page show "Loading…". A dedicated "this role doesn't run a historian" message // would be nicer; lands when we add role gating to the UI. } } private static string StateChipClass(HistorianDrainState state) => state switch { HistorianDrainState.Disabled => "chip chip-idle", HistorianDrainState.Idle => "chip chip-idle", HistorianDrainState.Draining => "chip chip-ok", HistorianDrainState.BackingOff => "chip chip-caution", _ => "chip chip-idle", }; public void Dispose() => _timer?.Dispose(); }