using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; using ZB.MOM.WW.MxGateway.Server.Configuration; namespace ZB.MOM.WW.MxGateway.Server.Dashboard.Hubs; /// /// SignalR hub that pushes a fresh on every /// snapshot refresh. New connections receive the effective configuration and the /// current snapshot immediately via ; subsequent /// refreshes are broadcast by without the /// configuration, which is startup-static and serialized only once per connection. /// /// /// Connections are counted into /// so can stop building and broadcasting /// snapshots while nobody is watching. /// [Authorize(Policy = DashboardAuthenticationDefaults.HubClientsPolicy)] public sealed class DashboardSnapshotHub( IDashboardSnapshotService snapshotService, DashboardSnapshotHubConnectionCounter connectionCounter) : Hub { /// Method name used to push snapshot updates to clients. public const string SnapshotMessage = "SnapshotUpdated"; /// /// Method name of the one-per-connection effective-configuration push. Snapshot /// ticks omit the configuration, so a client keeps the copy it got here. /// public const string ConfigurationMessage = "ConfigurationUpdated"; /// /// Returns the effective gateway configuration — the same object /// carries — for a client that needs it again. /// /// The effective configuration. public EffectiveGatewayConfiguration GetConfiguration() => snapshotService.GetSnapshot().Configuration; /// public override async Task OnConnectedAsync() { // Count the viewer before seeding it, so the publisher resumes its tick // no later than the first snapshot this connection renders. connectionCounter.Increment(); DashboardSnapshot snapshot = snapshotService.GetSnapshot(); await Clients.Caller.SendAsync(ConfigurationMessage, snapshot.Configuration).ConfigureAwait(false); await Clients.Caller.SendAsync(SnapshotMessage, snapshot).ConfigureAwait(false); await base.OnConnectedAsync().ConfigureAwait(false); } /// public override async Task OnDisconnectedAsync(Exception? exception) { connectionCounter.Decrement(); await base.OnDisconnectedAsync(exception).ConfigureAwait(false); } }