Initial commit

This commit is contained in:
2026-07-03 16:37:12 +07:00
commit 63b8c1ea8b
1931 changed files with 640587 additions and 0 deletions

View File

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