using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.SignalR.Client; using RobotNet10.RobotApp.Client.Shared.Motion; using RobotNet10.RobotApp.Client.Shared.Modules; namespace RobotNet10.RobotApp.Client.Clients; /// /// SignalR client thống nhất để kết nối với MotionHub /// Bao gồm: ManualControl, Odometry, LiftModule, RotationModule /// public class MotionHubClient : IAsyncDisposable { private readonly HubConnection _hubConnection; private bool _disposed; public bool IsConnected => _hubConnection.State == HubConnectionState.Connected; public HubConnectionState ConnectionState => _hubConnection.State; /// /// Event fired when odometry pose changes /// public event Action? PoseUpdated; /// /// Event fired when connection state changes /// public event Action? ConnectionStateChanged; public MotionHubClient(NavigationManager navigationManager) { var hubUrl = navigationManager.ToAbsoluteUri("/hubs/motion"); _hubConnection = new HubConnectionBuilder() .WithUrl(hubUrl) .WithAutomaticReconnect() .Build(); // Subscribe to pose updates from server _hubConnection.On("PoseUpdated", dto => PoseUpdated?.Invoke(dto)); _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; }; } /// /// Connect to hub /// public async Task StartAsync() { if (_hubConnection.State == HubConnectionState.Disconnected) { await _hubConnection.StartAsync(); ConnectionStateChanged?.Invoke(_hubConnection.State); } } /// /// Disconnect from hub /// public async Task StopAsync() { if (_hubConnection.State != HubConnectionState.Disconnected) { await _hubConnection.StopAsync(); } } #region Manual Control /// /// Get current status /// public async Task GetStatusAsync() { return await _hubConnection.InvokeAsync("GetStatus"); } /// /// Enable manual control /// public async Task EnableAsync() { await _hubConnection.InvokeAsync("Enable"); } /// /// Disable manual control /// public async Task DisableAsync() { await _hubConnection.InvokeAsync("Disable"); } #endregion #region Odometry /// /// Get current odometry pose /// public async Task GetCurrentPoseAsync() { return await _hubConnection.InvokeAsync("GetCurrentPose"); } /// /// Reset odometry to initial position /// public async Task ResetOdometryAsync() { await _hubConnection.InvokeAsync("ResetOdometry"); } /// /// Reset odometry to a specific pose /// public async Task ResetOdometryPoseAsync(OdometryDto dto) { await _hubConnection.InvokeAsync("ResetOdometryPose", dto); } #endregion #region Lift Module /// /// Get lift module status /// public async Task GetLiftStatusAsync() { return await _hubConnection.InvokeAsync("GetLiftStatus"); } /// /// Get current lift position /// public async Task GetLiftCurrentPositionAsync() { return await _hubConnection.InvokeAsync("GetLiftCurrentPosition"); } /// /// Lift up /// public async Task LiftUpAsync() { await _hubConnection.InvokeAsync("LiftUp"); } /// /// Lift down /// public async Task LiftDownAsync() { await _hubConnection.InvokeAsync("LiftDown"); } /// /// Stop lift movement (when moving up/down) /// public async Task LiftStopAsync() { await _hubConnection.InvokeAsync("LiftStop"); } /// /// Move lift to position /// public async Task LiftToPositionAsync(int position) { await _hubConnection.InvokeAsync("LiftToPosition", position); } /// /// Check if lift module is homed /// public async Task IsLiftHomedAsync() { return await _hubConnection.InvokeAsync("IsLiftHomed"); } /// /// Manual homing for lift module /// public async Task LiftHomeAsync() { await _hubConnection.InvokeAsync("LiftHome"); } #endregion #region Rotation Module /// /// Get rotation module status /// public async Task GetRotationStatusAsync() { return await _hubConnection.InvokeAsync("GetRotationStatus"); } /// /// Get current rotation angle /// public async Task GetRotationCurrentAngleAsync() { return await _hubConnection.InvokeAsync("GetRotationCurrentAngle"); } /// /// Rotate to absolute angle /// public async Task RotateToAngleAsync(double angleDegrees) { await _hubConnection.InvokeAsync("RotateToAngle", angleDegrees); } /// /// Rotate relative offset /// public async Task RotateOffsetAsync(double angleOffsetDegrees) { await _hubConnection.InvokeAsync("RotateOffset", angleOffsetDegrees); } /// /// Check if rotation module is homed /// public async Task IsRotationHomedAsync() { return await _hubConnection.InvokeAsync("IsRotationHomed"); } #endregion public async ValueTask DisposeAsync() { if (_disposed) return; _disposed = true; await StopAsync(); await _hubConnection.DisposeAsync(); } }