using Mbproxy.Proxy;
using Microsoft.AspNetCore.SignalR;
namespace Mbproxy.Admin;
///
/// SignalR hub backing the live admin dashboard. Two subscription scopes:
///
/// - — the fleet dashboard (GET /) joins the
/// group and receives a "fleet" message every
/// push tick.
/// - — a connection-detail page (GET /plc/{name})
/// joins and receives a "plc" message. The first
/// subscriber to a PLC arms that PLC's tag-value capture; the last to leave
/// disarms it (on-demand capture).
///
///
/// The hub itself is transient (one instance per call). Cross-call state — the
/// subscriber counts that drive capture arming — lives in the singleton
/// . The actual pushes are issued by
/// , not the hub.
///
internal sealed class StatusHub : Hub
{
/// SignalR group name for fleet-dashboard subscribers.
public const string FleetGroup = "fleet";
/// SignalR group name for a single PLC's detail-page subscribers.
public static string PlcGroup(string plcName) => "plc:" + plcName;
private readonly PlcSubscriptionTracker _tracker;
private readonly TagCaptureRegistry _captureRegistry;
public StatusHub(PlcSubscriptionTracker tracker, TagCaptureRegistry captureRegistry)
{
_tracker = tracker;
_captureRegistry = captureRegistry;
}
/// Subscribes the calling connection to fleet-wide status pushes.
public Task SubscribeFleet()
=> Groups.AddToGroupAsync(Context.ConnectionId, FleetGroup);
///
/// Subscribes the calling connection to one PLC's detail pushes and arms that PLC's
/// tag-value capture if this is its first viewer.
///
public async Task SubscribePlc(string plcName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, PlcGroup(plcName)).ConfigureAwait(false);
if (_tracker.Add(Context.ConnectionId, plcName))
_captureRegistry.Arm(plcName); // no-op for an unknown PLC name
}
///
/// On disconnect, drops every subscription the connection held and disarms the
/// capture of any PLC whose last viewer just left.
///
public override Task OnDisconnectedAsync(Exception? exception)
{
foreach (var plcName in _tracker.RemoveConnection(Context.ConnectionId))
_captureRegistry.Disarm(plcName);
return base.OnDisconnectedAsync(exception);
}
}