using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.SignalR.Client; using RobotNet10.RobotApp.Shared.NavigationMonitor; namespace RobotNet10.RobotApp.Client.Clients; public class NavigationMonitorHubClient : IAsyncDisposable { private readonly HubConnection _hubConnection; private bool _disposed; public bool IsConnected => _hubConnection.State == HubConnectionState.Connected; public HubConnectionState ConnectionState => _hubConnection.State; public event Action? TelemetryReceived; public event Action? SafetyViolationReceived; public event Action? MonitorStateChanged; public event Action? ConnectionStateChanged; public NavigationMonitorHubClient(NavigationManager navigationManager) { var hubUrl = navigationManager.ToAbsoluteUri("/hubs/motion/navigation-monitor"); _hubConnection = new HubConnectionBuilder() .WithUrl(hubUrl) .WithAutomaticReconnect() .Build(); _hubConnection.On("ReceiveTelemetry", dto => TelemetryReceived?.Invoke(dto)); _hubConnection.On("ReceiveSafetyViolation", dto => SafetyViolationReceived?.Invoke(dto)); _hubConnection.On("ReceiveMonitorState", dto => MonitorStateChanged?.Invoke(dto)); _hubConnection.Reconnecting += async (_) => { ConnectionStateChanged?.Invoke(_hubConnection.State); await Task.CompletedTask; }; _hubConnection.Reconnected += async (_) => { ConnectionStateChanged?.Invoke(_hubConnection.State); // Re-subscribe after reconnect await _hubConnection.InvokeAsync("Subscribe"); }; _hubConnection.Closed += async (_) => { ConnectionStateChanged?.Invoke(_hubConnection.State); await Task.CompletedTask; }; } public async Task StartAsync() { if (_hubConnection.State == HubConnectionState.Disconnected) { await _hubConnection.StartAsync(); ConnectionStateChanged?.Invoke(_hubConnection.State); await _hubConnection.InvokeAsync("Subscribe"); } } public async Task StopAsync() { if (_hubConnection.State != HubConnectionState.Disconnected) { try { await _hubConnection.InvokeAsync("Unsubscribe"); } catch { } await _hubConnection.StopAsync(); } } public async Task SetTelemetryEnabledAsync(bool enabled) { await _hubConnection.InvokeAsync("SetTelemetryEnabled", enabled); } public async Task SetSafetyStopEnabledAsync(bool enabled) { await _hubConnection.InvokeAsync("SetSafetyStopEnabled", enabled); } public async Task UpdateSafetyConfigAsync(NavigationSafetyConfigDto config) { await _hubConnection.InvokeAsync("UpdateSafetyConfig", config); } public async Task ReleaseSafetyStopAsync() { await _hubConnection.InvokeAsync("ReleaseSafetyStop"); } public async Task GetStateAsync() { return await _hubConnection.InvokeAsync("GetState"); } public async ValueTask DisposeAsync() { if (_disposed) return; _disposed = true; await StopAsync(); await _hubConnection.DisposeAsync(); } }