feat(dashboard): AlarmsPage reads provider status from IGatewayAlarmService in-process
This commit is contained in:
@@ -1,10 +1,9 @@
|
||||
@page "/alarms"
|
||||
@implements IAsyncDisposable
|
||||
@using Microsoft.AspNetCore.SignalR.Client
|
||||
@using ZB.MOM.WW.MxGateway.Server.Dashboard.Hubs
|
||||
@using ZB.MOM.WW.MxGateway.Server.Alarms
|
||||
@inject IDashboardLiveDataService LiveData
|
||||
@inject IOptions<GatewayOptions> GatewayOptions
|
||||
@inject DashboardHubConnectionFactory HubFactory
|
||||
@inject IGatewayAlarmService AlarmService
|
||||
|
||||
<PageTitle>Dashboard Alarms</PageTitle>
|
||||
|
||||
@@ -173,13 +172,13 @@
|
||||
private Task? _pollTask;
|
||||
|
||||
private DashboardAlarmProviderStatus _providerStatus = DashboardAlarmProviderStatus.Healthy;
|
||||
private HubConnection? _alarmsHub;
|
||||
private Task? _providerStatusTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_pollTask = PollLoopAsync();
|
||||
_ = AttachAlarmsHubAsync();
|
||||
_providerStatusTask = ProviderStatusLoopAsync();
|
||||
}
|
||||
|
||||
private string? ProviderStatusTitle()
|
||||
@@ -189,26 +188,48 @@
|
||||
: null;
|
||||
}
|
||||
|
||||
private async Task AttachAlarmsHubAsync()
|
||||
// The badge tracks the central monitor directly rather than looping back through
|
||||
// /hubs/alarms: the alarm service is an in-process multi-subscriber fan-out, so a
|
||||
// server-rendered page needs no SignalR client, no loopback socket and no auth token.
|
||||
// Alarm rows still come from the 3-second poll below — this loop only feeds the badge.
|
||||
private async Task ProviderStatusLoopAsync()
|
||||
{
|
||||
_alarmsHub = HubFactory.Create("/hubs/alarms");
|
||||
_alarmsHub.On<AlarmFeedMessage>(AlarmsHub.AlarmMessage, async message =>
|
||||
while (!_cts.IsCancellationRequested)
|
||||
{
|
||||
if (message.PayloadCase == AlarmFeedMessage.PayloadOneofCase.ProviderStatus)
|
||||
try
|
||||
{
|
||||
await foreach (AlarmFeedMessage message in AlarmService
|
||||
.StreamAsync(alarmFilterPrefix: null, _cts.Token)
|
||||
.ConfigureAwait(false))
|
||||
{
|
||||
if (message.PayloadCase != AlarmFeedMessage.PayloadOneofCase.ProviderStatus)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
_providerStatus = DashboardAlarmProviderStatus.FromFeed(message);
|
||||
await InvokeAsync(StateHasChanged).ConfigureAwait(false);
|
||||
}
|
||||
});
|
||||
|
||||
try
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
await _alarmsHub.StartAsync(_cts.Token).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
catch
|
||||
{
|
||||
// The badge is best-effort; it stays at the healthy default until
|
||||
// the hub reconnects and delivers a fresh provider-status message.
|
||||
// The monitor completes a subscriber's stream when it falls behind, and
|
||||
// again when the monitor restarts. Both are recoverable by resubscribing;
|
||||
// the badge holds its last value in the meantime.
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await Task.Delay(TimeSpan.FromSeconds(1), _cts.Token).ConfigureAwait(false);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -312,18 +333,6 @@
|
||||
{
|
||||
await _cts.CancelAsync();
|
||||
|
||||
if (_alarmsHub is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _alarmsHub.DisposeAsync();
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Disposal-time errors are best-effort.
|
||||
}
|
||||
}
|
||||
|
||||
if (_pollTask is not null)
|
||||
{
|
||||
try
|
||||
@@ -335,6 +344,17 @@
|
||||
}
|
||||
}
|
||||
|
||||
if (_providerStatusTask is not null)
|
||||
{
|
||||
try
|
||||
{
|
||||
await _providerStatusTask;
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
_cts.Dispose();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user