using Microsoft.AspNetCore.SignalR; namespace ZB.MOM.WW.OtOpcUa.Admin.Hubs; /// /// Pushes per-node generation-apply state changes (ClusterNodeGenerationState) to /// subscribed browser clients. Clients call SubscribeCluster(clusterId) on connect to /// scope notifications; the server sends NodeStateChanged messages whenever the poller /// observes a delta. /// public sealed class FleetStatusHub : Hub { public Task SubscribeCluster(string clusterId) { if (string.IsNullOrWhiteSpace(clusterId)) return Task.CompletedTask; return Groups.AddToGroupAsync(Context.ConnectionId, GroupName(clusterId)); } public Task UnsubscribeCluster(string clusterId) { if (string.IsNullOrWhiteSpace(clusterId)) return Task.CompletedTask; return Groups.RemoveFromGroupAsync(Context.ConnectionId, GroupName(clusterId)); } /// Clients call this once to also receive fleet-wide status — used by the dashboard. public Task SubscribeFleet() => Groups.AddToGroupAsync(Context.ConnectionId, FleetGroup); public const string FleetGroup = "__fleet__"; public static string GroupName(string clusterId) => $"cluster:{clusterId}"; } public sealed record NodeStateChangedMessage( string NodeId, string ClusterId, long? CurrentGenerationId, string? LastAppliedStatus, string? LastAppliedError, DateTime? LastAppliedAt, DateTime? LastSeenAt);