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>
78 lines
2.6 KiB
Plaintext
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",
|
|
};
|
|
}
|