using System.Text.Json.Serialization; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; using RobotNet10.NavigationTune.Shared.Hubs; using RobotNet10.NavigationTune.Shared.Interfaces; namespace RobotNet10.NavigationTuneUI.Clients; /// /// SignalR client for Tuning Hub /// public class TuningHubClient : IAsyncDisposable { private readonly HubConnection _hubConnection; private bool _disposed; public bool IsConnected => _hubConnection.State == HubConnectionState.Connected; public HubConnectionState ConnectionState => _hubConnection.State; // Events public event Action? TelemetryUpdated; public event Action? TestStatusUpdated; public event Action? SafetyEventReceived; public event Action? TestResultReceived; public event Action? ConnectionStateChanged; public TuningHubClient(NavigationManager navigationManager) { var hubUrl = navigationManager.ToAbsoluteUri("/tuninghub"); _hubConnection = new HubConnectionBuilder() .WithUrl(hubUrl) .AddJsonProtocol(options => { options.PayloadSerializerOptions.NumberHandling = JsonNumberHandling.AllowNamedFloatingPointLiterals; }) .WithAutomaticReconnect() .Build(); // Subscribe to server messages _hubConnection.On("ReceiveTelemetry", dto => TelemetryUpdated?.Invoke(dto)); _hubConnection.On("ReceiveTestStatus", dto => TestStatusUpdated?.Invoke(dto)); _hubConnection.On("ReceiveSafetyEvent", dto => SafetyEventReceived?.Invoke(dto)); _hubConnection.On("ReceiveTestResult", result => TestResultReceived?.Invoke(result)); _hubConnection.Reconnecting += async (ex) => { ConnectionStateChanged?.Invoke(_hubConnection.State); await Task.CompletedTask; }; _hubConnection.Reconnected += async (id) => { ConnectionStateChanged?.Invoke(_hubConnection.State); await Task.CompletedTask; }; _hubConnection.Closed += async (ex) => { ConnectionStateChanged?.Invoke(_hubConnection.State); await Task.CompletedTask; }; } public async Task StartAsync() { if (_hubConnection.State == HubConnectionState.Disconnected) { await _hubConnection.StartAsync(); } } public async Task StopAsync() { if (_hubConnection.State != HubConnectionState.Disconnected) { await _hubConnection.StopAsync(); } } public async Task JoinTestSessionAsync(string testRunId) { await _hubConnection.InvokeAsync("JoinTestSession", testRunId); } public async Task LeaveTestSessionAsync(string testRunId) { await _hubConnection.InvokeAsync("LeaveTestSession", testRunId); } public async ValueTask DisposeAsync() { if (_disposed) return; await StopAsync(); await _hubConnection.DisposeAsync(); _disposed = true; } }