Initial commit
This commit is contained in:
@@ -0,0 +1,227 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet.VDA5050.State;
|
||||
using RobotNet10.FleetManager.Shared.DTOs.Robot;
|
||||
|
||||
namespace RobotNet10.FleetManager.Client.Services;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR Hub Client for robot state and visualization updates.
|
||||
/// Manages connection to the SignalR hub and provides methods to subscribe/unsubscribe to robot updates.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This client automatically handles reconnection and provides events for state and visualization updates.
|
||||
/// Dispose the client when done to properly clean up the connection.
|
||||
/// </remarks>
|
||||
public class RobotStateHubClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private readonly ILogger<RobotStateHubClient>? _logger;
|
||||
|
||||
public event Action<RobotMonitorBoardcastData>? OnMonitorBoardcastUpdate;
|
||||
public event Action<StateMsg>? OnStateUpdate;
|
||||
public event Action<string>? OnConnectionError;
|
||||
public event Action? OnMonitorDeactivated;
|
||||
|
||||
public RobotStateHubClient(NavigationManager navigationManager, ILogger<RobotStateHubClient>? logger = null)
|
||||
{
|
||||
_logger = logger;
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/robot-state");
|
||||
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
// Register event handlers
|
||||
_hubConnection.On<StateMsg>("OnStateUpdate", (state) =>
|
||||
{
|
||||
OnStateUpdate?.Invoke(state);
|
||||
});
|
||||
|
||||
_hubConnection.On<RobotMonitorBoardcastData>("OnMonitorUpdate", (state) =>
|
||||
{
|
||||
OnMonitorBoardcastUpdate?.Invoke(state);
|
||||
});
|
||||
|
||||
_hubConnection.On("OnMonitorDeactivated", () =>
|
||||
{
|
||||
OnMonitorDeactivated?.Invoke();
|
||||
});
|
||||
|
||||
_hubConnection.Closed += async (error) =>
|
||||
{
|
||||
if (error != null)
|
||||
{
|
||||
_logger?.LogError(error, "SignalR connection closed with error");
|
||||
OnConnectionError?.Invoke(error.Message);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.LogInformation("SignalR connection closed");
|
||||
}
|
||||
await Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Reconnecting += async (error) =>
|
||||
{
|
||||
_logger?.LogWarning(error, "SignalR connection reconnecting");
|
||||
await Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Reconnected += async (connectionId) =>
|
||||
{
|
||||
_logger?.LogInformation("SignalR connection reconnected with ID {ConnectionId}", connectionId);
|
||||
await Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connect to the SignalR hub
|
||||
/// </summary>
|
||||
public async Task ConnectAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_hubConnection.State == HubConnectionState.Disconnected)
|
||||
{
|
||||
await _hubConnection.StartAsync();
|
||||
_logger?.LogInformation("SignalR hub connected");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "Error connecting to SignalR hub");
|
||||
OnConnectionError?.Invoke(ex.Message);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Disconnect from the SignalR hub
|
||||
/// </summary>
|
||||
public async Task DisconnectAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_hubConnection.State != HubConnectionState.Disconnected)
|
||||
{
|
||||
await _hubConnection.StopAsync();
|
||||
_logger?.LogInformation("SignalR hub disconnected");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "Error disconnecting from SignalR hub");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Subscribe to receive updates for a specific robot
|
||||
/// </summary>
|
||||
/// <param name="robotId">Robot identifier (serialNumber)</param>
|
||||
public async Task SubscribeToRobotAsync(string robotId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_hubConnection.State == HubConnectionState.Connected)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SubscribeToRobot", robotId);
|
||||
_logger?.LogInformation("Subscribed to robot {RobotId}", robotId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.LogWarning("Cannot subscribe to robot {RobotId}: Hub not connected", robotId);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "Error subscribing to robot {RobotId}", robotId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from updates for a specific robot
|
||||
/// </summary>
|
||||
/// <param name="robotId">Robot identifier (serialNumber)</param>
|
||||
public async Task UnsubscribeFromRobotAsync(string robotId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_hubConnection.State == HubConnectionState.Connected)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("UnsubscribeFromRobot", robotId);
|
||||
_logger?.LogInformation("Unsubscribed from robot {RobotId}", robotId);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "Error unsubscribing from robot {RobotId}", robotId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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).
|
||||
/// </summary>
|
||||
/// <param name="levelId">Level identifier (LayoutLevel.Id)</param>
|
||||
public async Task SubscribeToLevelForMonitorAsync(Guid levelId)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_hubConnection.State == HubConnectionState.Connected)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SubscribeToLevelForMonitor", levelId);
|
||||
_logger?.LogInformation("Subscribed to level {LevelId} for monitor", levelId);
|
||||
}
|
||||
else
|
||||
{
|
||||
_logger?.LogWarning("Cannot subscribe to level {LevelId}: Hub not connected", levelId);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "Error subscribing to level {LevelId} for monitor", levelId);
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Unsubscribe from monitor updates for the current level
|
||||
/// </summary>
|
||||
public async Task UnsubscribeFromLevelForMonitorAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_hubConnection.State == HubConnectionState.Connected)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("UnsubscribeFromLevelForMonitor");
|
||||
_logger?.LogInformation("Unsubscribed from level for monitor");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, "Error unsubscribing from level for monitor");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the current connection state
|
||||
/// </summary>
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_hubConnection != null)
|
||||
{
|
||||
await DisconnectAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
}
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user