Files
lmxopcua/src/Server/ZB.MOM.WW.OtOpcUa.Admin/Components/Pages/AlarmsHistorian.razor
Joseph Doherty 43291d7fdd fix(admin): add InteractiveServer render mode to all interactive Blazor pages; fix wrong hub URLs
Eight pages were using @onclick handlers, Timers, or HubConnections but had no @rendermode,
causing interactivity to be silently dead under static SSR. Added @rendermode RenderMode.InteractiveServer
(with the required @using Microsoft.AspNetCore.Components.Web) to: AlarmsHistorian, Certificates,
Fleet, Home, Hosts, Reservations, DraftEditor, and ImportEquipment.

Also fixed two hub URL bugs: AclsTab and RedundancyTab were connecting to the non-existent
/hubs/fleet-status path; corrected to /hubs/fleet which matches the MapHub<FleetStatusHub>
call in Program.cs. Build: 0 errors, 0 warnings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-18 04:24:29 -04:00

78 lines
2.6 KiB
Plaintext

@page "/alarms/historian"
@using Microsoft.AspNetCore.Components.Web
@using ZB.MOM.WW.OtOpcUa.Admin.Services
@using ZB.MOM.WW.OtOpcUa.Core.AlarmHistorian
@rendermode RenderMode.InteractiveServer
@inject HistorianDiagnosticsService Diag
<h1 class="page-title">Alarm historian</h1>
<p class="text-muted">Local store-and-forward queue that ships alarm events to Aveva Historian via Galaxy.Host.</p>
<section class="agg-grid rise" style="animation-delay:.02s">
<div class="agg-card">
<div class="agg-label">Drain state</div>
<div class="agg-value"><span class="chip @BadgeFor(_status.DrainState)">@_status.DrainState</span></div>
</div>
<div class="agg-card">
<div class="agg-label">Queue depth</div>
<div class="agg-value numeric">@_status.QueueDepth.ToString("N0")</div>
</div>
<div class="agg-card">
<div class="agg-label">Dead-letter depth</div>
<div class="agg-value numeric">@_status.DeadLetterDepth.ToString("N0")</div>
</div>
<div class="agg-card">
<div class="agg-label">Last success</div>
<div class="agg-value">@(_status.LastSuccessUtc?.ToString("u") ?? "—")</div>
</div>
</section>
@if (!string.IsNullOrEmpty(_status.LastError))
{
<section class="panel notice rise" style="animation-delay:.08s">
<strong>Last error:</strong> @_status.LastError
</section>
}
<div class="d-flex gap-2 mt-3">
<button class="btn btn-outline-secondary" @onclick="RefreshAsync">Refresh</button>
<button class="btn btn-warning" disabled="@(_status.DeadLetterDepth == 0)" @onclick="RetryDeadLetteredAsync">
Retry dead-lettered (@_status.DeadLetterDepth)
</button>
</div>
@if (_retryResult is not null)
{
<section class="panel notice rise" style="animation-delay:.08s">Requeued @_retryResult row(s) for retry.</section>
}
@code {
private HistorianSinkStatus _status = new(0, 0, null, null, null, HistorianDrainState.Disabled);
private int? _retryResult;
protected override void OnInitialized() => _status = Diag.GetStatus();
private Task RefreshAsync()
{
_status = Diag.GetStatus();
_retryResult = null;
return Task.CompletedTask;
}
private Task RetryDeadLetteredAsync()
{
_retryResult = Diag.TryRetryDeadLettered();
_status = Diag.GetStatus();
return Task.CompletedTask;
}
private static string BadgeFor(HistorianDrainState s) => s switch
{
HistorianDrainState.Idle => "chip-ok",
HistorianDrainState.Draining => "chip-idle",
HistorianDrainState.BackingOff => "chip-warn",
HistorianDrainState.Disabled => "chip-idle",
_ => "chip-idle",
};
}