using Microsoft.AspNetCore.SignalR;
namespace RobotNet10.FleetManager.Hubs;
///
/// SignalR Hub for real-time robot state and visualization updates.
/// Manages client subscriptions to robot groups for receiving VDA5050 messages.
///
///
/// Clients subscribe to specific robots using SubscribeToRobot method.
/// Messages are broadcast to groups named "robot:{robotId}".
///
public class RobotStateHub(
Services.Logger logger,
RobotStateHubContext hubContext) : Hub
{
private readonly Services.Logger _logger = logger;
private readonly RobotStateHubContext _hubContext = hubContext;
///
/// Subscribe to receive updates for a specific robot
///
/// Robot identifier (serialNumber)
public async Task SubscribeToRobot(string robotId)
{
var groupName = GetGroupName(robotId);
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
}
///
/// Unsubscribe from updates for a specific robot
///
/// Robot identifier (serialNumber)
public async Task UnsubscribeFromRobot(string robotId)
{
var groupName = GetGroupName(robotId);
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
}
private static string GetGroupName(string robotId)
{
return $"robot:{robotId}";
}
///
/// Subscribe to receive monitor updates for a specific levelId
/// Each connection can only subscribe to one levelId at a time.
/// Maximum 5 connections per levelId (FIFO).
///
/// Level identifier (LayoutLevel.Id)
public async Task SubscribeToLevelForMonitor(Guid levelId)
{
var evictedConnectionId = _hubContext.SubscribeToLevel(Context.ConnectionId, levelId);
// If a connection was evicted, notify it
if (evictedConnectionId != null)
{
_logger.Info($"Connection {evictedConnectionId} evicted from level {levelId} (max 5 connections reached)");
await Clients.Client(evictedConnectionId).SendAsync("OnMonitorDeactivated");
}
await Task.CompletedTask;
}
///
/// Unsubscribe from monitor updates for the current level
///
public async Task UnsubscribeFromLevelForMonitor()
{
var removed = _hubContext.UnsubscribeFromLevel(Context.ConnectionId);
if (removed)
{
_logger.Info($"Client {Context.ConnectionId} unsubscribed from level for monitor");
}
await Task.CompletedTask;
}
public override async Task OnDisconnectedAsync(Exception? exception)
{
// Remove connection from subscription manager
_hubContext.RemoveConnection(Context.ConnectionId);
if (exception != null)
{
_logger.Warning($"Client {Context.ConnectionId} disconnected with error: {exception.Message}");
}
await base.OnDisconnectedAsync(exception);
}
}