Initial commit
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet10.RobotApp.Client.Shared.Devices;
|
||||
using RobotNet10.Shared.Sensor;
|
||||
|
||||
namespace RobotNet10.RobotApp.Client.Clients;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR client để kết nối với BatteryHub
|
||||
/// </summary>
|
||||
public class BatteryHubClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
public BatteryHubClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/devices/battery");
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
_hubConnection.Reconnecting += error =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Reconnected += connectionId =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Closed += error =>
|
||||
{
|
||||
return 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<BatteryState> GetBatteryDataAsync(string deviceId)
|
||||
{
|
||||
var result = await _hubConnection.InvokeAsync<BatteryState?>("GetBatteryData", deviceId);
|
||||
return result ?? new BatteryState();
|
||||
}
|
||||
|
||||
public async Task<DeviceInfoDto?> GetDeviceInfoAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<DeviceInfoDto?>("GetDeviceInfo", deviceId);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet10.RobotApp.Client.Shared.Devices;
|
||||
using RobotNet10.Shared.Geometry;
|
||||
|
||||
namespace RobotNet10.RobotApp.Client.Clients;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR client để kết nối với CameraQrHub
|
||||
/// </summary>
|
||||
public class CameraQrHubClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
public CameraQrHubClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/devices/cameraqr");
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
_hubConnection.Reconnecting += error =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Reconnected += connectionId =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Closed += error =>
|
||||
{
|
||||
return 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<CameraQrDataDto> GetCameraQrDataAsync(string deviceId)
|
||||
{
|
||||
var result = await _hubConnection.InvokeAsync<CameraQrDataDto?>("GetCameraQrData", deviceId);
|
||||
return result ?? new CameraQrDataDto();
|
||||
}
|
||||
|
||||
public async Task<DeviceInfoDto?> GetDeviceInfoAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<DeviceInfoDto?>("GetDeviceInfo", deviceId);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,322 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet10.RobotApp.Client.Shared.Devices;
|
||||
|
||||
namespace RobotNet10.RobotApp.Client.Clients;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR client để kết nối với CiA402ServoHub
|
||||
/// </summary>
|
||||
public class CiA402ServoHubClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
public CiA402ServoHubClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/devices/cia402servo");
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
_hubConnection.Reconnecting += error =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Reconnected += connectionId =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Closed += error =>
|
||||
{
|
||||
return 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<CiA402ServoDataDto> GetServoDataAsync(string deviceId)
|
||||
{
|
||||
var result = await _hubConnection.InvokeAsync<CiA402ServoDataDto?>("GetServoData", deviceId);
|
||||
return result ?? new CiA402ServoDataDto();
|
||||
}
|
||||
|
||||
public async Task<DeviceInfoDto?> GetDeviceInfoAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<DeviceInfoDto?>("GetDeviceInfo", deviceId);
|
||||
}
|
||||
|
||||
// State Machine Control Methods
|
||||
public async Task EnableOperationAsync(string deviceId)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("EnableOperationAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task DisableOperationAsync(string deviceId)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("DisableOperationAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task QuickStopAsync(string deviceId)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("QuickStopAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task FaultResetAsync(string deviceId)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("FaultResetAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task ShutdownAsync(string deviceId)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("ShutdownAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task SwitchOnAsync(string deviceId)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SwitchOnAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task EnableAsync(string deviceId)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("EnableAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task DisableAsync(string deviceId)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("DisableAsync", deviceId);
|
||||
}
|
||||
|
||||
// Operation Mode
|
||||
public async Task SetOperationModeAsync(string deviceId, string mode)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetOperationModeAsync", deviceId, mode);
|
||||
}
|
||||
|
||||
// Position Control
|
||||
public async Task SetTargetPositionAsync(string deviceId, int position)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetTargetPositionAsync", deviceId, position);
|
||||
}
|
||||
|
||||
public async Task MoveToPositionAsync(string deviceId, int position, uint velocity = 1000, uint acceleration = 5000, uint deceleration = 5000)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("MoveToPositionAsync", deviceId, position, velocity, acceleration, deceleration);
|
||||
}
|
||||
|
||||
// Velocity Control
|
||||
public async Task SetTargetVelocityAsync(string deviceId, int velocity)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetTargetVelocityAsync", deviceId, velocity);
|
||||
}
|
||||
|
||||
public async Task TargetVelocityAsync(string deviceId, int targetVelocity, uint acceleration = 5000, uint deceleration = 5000)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("TargetVelocityAsync", deviceId, targetVelocity, acceleration, deceleration);
|
||||
}
|
||||
|
||||
public async Task ProfileVelocityAsync(string deviceId, int targetVelocity, uint acceleration = 5000, uint deceleration = 5000)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("ProfileVelocityAsync", deviceId, targetVelocity, acceleration, deceleration);
|
||||
}
|
||||
|
||||
// Torque Control
|
||||
public async Task SetTargetTorqueAsync(string deviceId, short torque)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetTargetTorqueAsync", deviceId, torque);
|
||||
}
|
||||
|
||||
public async Task RunTorqueAsync(string deviceId, short torque)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("RunTorqueAsync", deviceId, torque);
|
||||
}
|
||||
|
||||
// Profile Settings
|
||||
public async Task SetProfileAccelerationAsync(string deviceId, uint acceleration)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetProfileAccelerationAsync", deviceId, acceleration);
|
||||
}
|
||||
|
||||
public async Task SetProfileDecelerationAsync(string deviceId, uint deceleration)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetProfileDecelerationAsync", deviceId, deceleration);
|
||||
}
|
||||
|
||||
public async Task SetProfileVelocityAsync(string deviceId, uint velocity)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetProfileVelocityAsync", deviceId, velocity);
|
||||
}
|
||||
|
||||
public async Task SetProfileSpeedAsync(string deviceId, uint velocity)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetProfileSpeedAsync", deviceId, velocity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ghi cùng lúc cả 3 profile (speed, acceleration, deceleration) xuống drive.
|
||||
/// </summary>
|
||||
public async Task SetProfileSettingsAsync(string deviceId, uint profileSpeed, uint profileAcceleration, uint profileDeceleration)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetProfileSettingsAsync", deviceId, profileSpeed, profileAcceleration, profileDeceleration);
|
||||
}
|
||||
|
||||
// Homing
|
||||
public async Task SetHomingMethodAsync(string deviceId, byte method)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetHomingMethodAsync", deviceId, method);
|
||||
}
|
||||
|
||||
public async Task SetHomingSpeedAsync(string deviceId, int speed)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetHomingSpeedAsync", deviceId, speed);
|
||||
}
|
||||
|
||||
public async Task SetHomingOffsetAsync(string deviceId, int offset)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetHomingOffsetAsync", deviceId, offset);
|
||||
}
|
||||
|
||||
public async Task StartHomingAsync(string deviceId, byte method, int speed)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("StartHomingAsync", deviceId, method, speed);
|
||||
}
|
||||
|
||||
/// <summary>Đọc lại homing method từ drive để kiểm tra đã ghi xuống chưa.</summary>
|
||||
public async Task<byte> GetHomingMethodAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<byte>("GetHomingMethodAsync", deviceId);
|
||||
}
|
||||
|
||||
/// <summary>Đọc lại homing speed từ drive để kiểm tra đã ghi xuống chưa.</summary>
|
||||
public async Task<int> GetHomingSpeedAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<int>("GetHomingSpeedAsync", deviceId);
|
||||
}
|
||||
|
||||
/// <summary>Đọc lại homing offset từ drive để kiểm tra đã ghi xuống chưa.</summary>
|
||||
public async Task<int> GetHomingOffsetAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<int>("GetHomingOffsetAsync", deviceId);
|
||||
}
|
||||
|
||||
// Position Control - Additional Methods
|
||||
public async Task StartPositionMoveAsync(string deviceId)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("StartPositionMoveAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task WaitUntilAtTargetAsync(string deviceId, int tolerance = 100, bool useStatusword = true, int checkIntervalMs = 10)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("WaitUntilAtTargetAsync", deviceId, tolerance, useStatusword, checkIntervalMs);
|
||||
}
|
||||
|
||||
// Error and Status Information
|
||||
public async Task<bool> IsInFaultStateAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<bool>("IsInFaultStateAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task<byte> GetErrorRegisterAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<byte>("GetErrorRegisterAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task<ushort[]> GetErrorHistoryAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<ushort[]>("GetErrorHistoryAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task<ushort> GetLatestErrorCodeAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<ushort>("GetLatestErrorCodeAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task<bool> TryFaultResetAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<bool>("TryFaultResetAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task<bool> IsEnabledAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<bool>("IsEnabledAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task<bool> IsReadyAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<bool>("IsReadyAsync", deviceId);
|
||||
}
|
||||
|
||||
// Statusword & Controlword
|
||||
public async Task<ushort> GetStatuswordAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<ushort>("GetStatuswordAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task SetControlwordAsync(string deviceId, ushort controlwordValue)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetControlwordAsync", deviceId, controlwordValue);
|
||||
}
|
||||
|
||||
public async Task<string> GetStateAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<string>("GetStateAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task<string> GetOperationModeAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<string>("GetOperationModeAsync", deviceId);
|
||||
}
|
||||
|
||||
// Position, Velocity, Torque
|
||||
public async Task<int> GetActualPositionAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<int>("GetActualPositionAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task<int> GetActualVelocityAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<int>("GetActualVelocityAsync", deviceId);
|
||||
}
|
||||
|
||||
public async Task<short> GetActualTorqueAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<short>("GetActualTorqueAsync", deviceId);
|
||||
}
|
||||
|
||||
// Target Position Checking
|
||||
public async Task<bool> IsAtTargetAsync(string deviceId, int tolerance = 100, bool useStatusword = true)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<bool>("IsAtTarget", deviceId, tolerance, useStatusword);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet10.RobotApp.Client.Shared.Devices;
|
||||
using RobotNet10.Shared.Sensor;
|
||||
|
||||
namespace RobotNet10.RobotApp.Client.Clients;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR client để kết nối với InertialMeasurementUnitHub
|
||||
/// </summary>
|
||||
public class InertialMeasurementUnitHubClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
public InertialMeasurementUnitHubClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/devices/inertialmeasurementunit");
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
_hubConnection.Reconnecting += error =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Reconnected += connectionId =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Closed += error =>
|
||||
{
|
||||
return 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<Imu> GetImuDataAsync(string deviceId)
|
||||
{
|
||||
var result = await _hubConnection.InvokeAsync<Imu?>("GetImuData", deviceId);
|
||||
return result ?? new Imu();
|
||||
}
|
||||
|
||||
public async Task<Imu> ReadAllDataAsync(string deviceId)
|
||||
{
|
||||
var result = await _hubConnection.InvokeAsync<Imu?>("ReadAllData", deviceId);
|
||||
return result ?? new Imu();
|
||||
}
|
||||
|
||||
public async Task<DeviceInfoDto?> GetDeviceInfoAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<DeviceInfoDto?>("GetDeviceInfo", deviceId);
|
||||
}
|
||||
|
||||
public async Task<Dictionary<string, string>?> GetDevicePropertiesAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<Dictionary<string, string>?>("GetDeviceProperties", deviceId);
|
||||
}
|
||||
|
||||
public async Task<List<PropertyDescription>?> GetDevicePropertyDescriptionsAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<List<PropertyDescription>?>("GetDevicePropertyDescriptions", deviceId);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet10.RobotApp.Client.Shared.Devices;
|
||||
using RobotNet10.Shared.Sensor;
|
||||
|
||||
namespace RobotNet10.RobotApp.Client.Clients;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR client để kết nối với LidarHub
|
||||
/// </summary>
|
||||
public class LidarHubClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
public LidarHubClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/devices/lidar");
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
_hubConnection.Reconnecting += error =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Reconnected += connectionId =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Closed += error =>
|
||||
{
|
||||
return 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<LaserScan> GetLidarDataAsync(string deviceId)
|
||||
{
|
||||
var result = await _hubConnection.InvokeAsync<LaserScan?>("GetLidarData", deviceId);
|
||||
return result ?? new LaserScan();
|
||||
}
|
||||
|
||||
public async Task<DeviceInfoDto?> GetDeviceInfoAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<DeviceInfoDto?>("GetDeviceInfo", deviceId);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet10.Shared;
|
||||
using RobotNet10.Shared.Detection;
|
||||
using RobotNet10.Shared.Geometry;
|
||||
|
||||
namespace RobotNet10.RobotApp.Client.Clients;
|
||||
|
||||
public class MarkerDetectorHubClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
public MarkerDetectorHubClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("hubs/marker-detect");
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
_hubConnection.Reconnecting += error =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Reconnected += connectionId =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Closed += error =>
|
||||
{
|
||||
return 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<MessageResult<Guid>> CreateSessionAsync(MarkersSearchRequest request)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<MessageResult<Guid>>("CreateSession", request);
|
||||
}
|
||||
|
||||
public async Task<MessageResult<Pose>> GetGoalAsync(Guid sessionId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<MessageResult<Pose>>("GetGoal", sessionId);
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet10.RobotApp.Client.Shared.Devices;
|
||||
using RobotNet10.Shared;
|
||||
|
||||
namespace RobotNet10.RobotApp.Client.Clients;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR client để kết nối với ModbusTcpHub
|
||||
/// </summary>
|
||||
public class ModbusTcpHubClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
public ModbusTcpHubClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/devices/modbustcp");
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
_hubConnection.Reconnecting += error =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Reconnected += connectionId =>
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
|
||||
_hubConnection.Closed += error =>
|
||||
{
|
||||
return 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<ModbusTcpData> GetModbusDataAsync(string deviceId)
|
||||
{
|
||||
var result = await _hubConnection.InvokeAsync<ModbusTcpData?>("GetModbusData", deviceId);
|
||||
return result ?? new ModbusTcpData();
|
||||
}
|
||||
|
||||
public async Task<DeviceInfoDto?> GetDeviceInfoAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<DeviceInfoDto?>("GetDeviceInfo", deviceId);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ghi một coil vào device
|
||||
/// </summary>
|
||||
/// <param name="deviceId">Device ID</param>
|
||||
/// <param name="address">Địa chỉ coil</param>
|
||||
/// <param name="value">Giá trị cần ghi (true/false)</param>
|
||||
/// <returns>True nếu thành công, false nếu thất bại</returns>
|
||||
public async Task<bool> WriteCoilAsync(string deviceId, ushort address, bool value)
|
||||
{
|
||||
try
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<bool>("WriteCoil", deviceId, address, value);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
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<NavigationTelemetryDto>? TelemetryReceived;
|
||||
public event Action<NavigationSafetyViolationDto>? SafetyViolationReceived;
|
||||
public event Action<NavigationMonitorStateDto>? MonitorStateChanged;
|
||||
public event Action<HubConnectionState>? ConnectionStateChanged;
|
||||
|
||||
public NavigationMonitorHubClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/motion/navigation-monitor");
|
||||
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
_hubConnection.On<NavigationTelemetryDto>("ReceiveTelemetry", dto => TelemetryReceived?.Invoke(dto));
|
||||
_hubConnection.On<NavigationSafetyViolationDto>("ReceiveSafetyViolation", dto => SafetyViolationReceived?.Invoke(dto));
|
||||
_hubConnection.On<NavigationMonitorStateDto>("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<NavigationMonitorStateDto> GetStateAsync()
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<NavigationMonitorStateDto>("GetState");
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed) return;
|
||||
_disposed = true;
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet10.RobotApp.Client.Shared.Motion;
|
||||
|
||||
namespace RobotNet10.RobotApp.Client.Clients;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR client kết nối tới OdometryHub - hiển thị odom (pose + velocity) realtime
|
||||
/// </summary>
|
||||
public class OdometryHubClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Sự kiện nhận odometry mới (broadcast từ server ~10 Hz)
|
||||
/// </summary>
|
||||
public event Action<OdometryDto>? OdometryReceived;
|
||||
|
||||
/// <summary>
|
||||
/// Trạng thái kết nối thay đổi
|
||||
/// </summary>
|
||||
public event Action<HubConnectionState>? ConnectionStateChanged;
|
||||
|
||||
public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
public OdometryHubClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/odometry");
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
_hubConnection.On<OdometryDto>("ReceiveOdometry", dto => OdometryReceived?.Invoke(dto));
|
||||
|
||||
_hubConnection.Reconnecting += _ =>
|
||||
{
|
||||
ConnectionStateChanged?.Invoke(_hubConnection.State);
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
_hubConnection.Reconnected += _ =>
|
||||
{
|
||||
ConnectionStateChanged?.Invoke(_hubConnection.State);
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
_hubConnection.Closed += _ =>
|
||||
{
|
||||
ConnectionStateChanged?.Invoke(_hubConnection.State);
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
if (_hubConnection.State == HubConnectionState.Disconnected)
|
||||
{
|
||||
await _hubConnection.StartAsync();
|
||||
ConnectionStateChanged?.Invoke(_hubConnection.State);
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StopAsync()
|
||||
{
|
||||
if (_hubConnection.State != HubConnectionState.Disconnected)
|
||||
{
|
||||
await _hubConnection.StopAsync();
|
||||
ConnectionStateChanged?.Invoke(_hubConnection.State);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lấy odometry hiện tại (on-demand)
|
||||
/// </summary>
|
||||
public async Task<OdometryDto?> GetCurrentOdometryAsync()
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<OdometryDto?>("GetCurrentOdometry");
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
_disposed = true;
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet10.RobotApp.Client.Shared.Plc;
|
||||
|
||||
namespace RobotNet10.RobotApp.Client.Clients;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR client để kết nối với PlcControllerHub (read-only)
|
||||
/// </summary>
|
||||
public class PlcControllerHubClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
/// <summary>
|
||||
/// Event fired when connection state changes
|
||||
/// </summary>
|
||||
public event Action<HubConnectionState>? ConnectionStateChanged;
|
||||
|
||||
public PlcControllerHubClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/plc/controller");
|
||||
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
_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();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get current status
|
||||
/// </summary>
|
||||
public async Task<PlcControllerStatusDto> GetStatusAsync()
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<PlcControllerStatusDto>("GetStatus");
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet10.RobotApp.Client.Shared.Devices;
|
||||
|
||||
namespace RobotNet10.RobotApp.Client.Clients;
|
||||
|
||||
public class RfHandleHubClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private bool _disposed;
|
||||
|
||||
public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
public RfHandleHubClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/devices/rfhandle");
|
||||
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
_hubConnection.Reconnecting += async (ex) =>
|
||||
{
|
||||
// optionally notify UI
|
||||
await Task.CompletedTask;
|
||||
};
|
||||
_hubConnection.Reconnected += async (id) =>
|
||||
{
|
||||
// optionally notify UI
|
||||
await Task.CompletedTask;
|
||||
};
|
||||
_hubConnection.Closed += async (ex) =>
|
||||
{
|
||||
// optionally notify UI
|
||||
await Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
// CONNECT / DISCONNECT
|
||||
public async Task StartAsync()
|
||||
{
|
||||
if (_hubConnection.State == HubConnectionState.Disconnected)
|
||||
{
|
||||
await _hubConnection.StartAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StopAsync()
|
||||
{
|
||||
if (_hubConnection.State != HubConnectionState.Disconnected)
|
||||
await _hubConnection.StopAsync();
|
||||
}
|
||||
|
||||
// SERVER CALLS (RPC)
|
||||
public async Task<RfHandleDataDto> GetRfHandleDataAsync(string deviceId)
|
||||
{
|
||||
var result = await _hubConnection.InvokeAsync<RfHandleDataDto?>("GetRfHandleData", deviceId);
|
||||
return result ?? new RfHandleDataDto();
|
||||
}
|
||||
|
||||
public async Task<DeviceInfoDto?> GetDeviceInfoAsync(string deviceId)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<DeviceInfoDto?>("GetDeviceInfo", deviceId);
|
||||
}
|
||||
|
||||
// DISPOSE
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
|
||||
try
|
||||
{
|
||||
// stop but don't throw
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
}
|
||||
catch { }
|
||||
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using Microsoft.AspNetCore.SignalR.Client;
|
||||
using RobotNet10.RobotApp.Client.Shared.SLAM;
|
||||
using RobotNet10.RobotApp.Shared.Enums;
|
||||
using RobotNet10.Shared.Geometry;
|
||||
using RobotNet10.Shared.Numbers;
|
||||
|
||||
namespace RobotNet10.RobotApp.Client.Clients;
|
||||
|
||||
/// <summary>
|
||||
/// SignalR client để kết nối với CartographerHub
|
||||
/// </summary>
|
||||
public class SLAMClient : IAsyncDisposable
|
||||
{
|
||||
private readonly HubConnection _hubConnection;
|
||||
private bool _disposed;
|
||||
|
||||
// Events từ server
|
||||
public event Action<SLAMState>? StateChanged;
|
||||
public event Action<int, int, int>? MapSaveProgressChanged;
|
||||
public event Action<string, bool>? IsProcessingChanged;
|
||||
|
||||
public bool IsConnected => _hubConnection.State == HubConnectionState.Connected;
|
||||
public HubConnectionState ConnectionState => _hubConnection.State;
|
||||
|
||||
// Cached state
|
||||
public SLAMState? CurrentState { get; private set; }
|
||||
public PoseDto? CurrentPose { get; private set; }
|
||||
|
||||
public SLAMClient(NavigationManager navigationManager)
|
||||
{
|
||||
var hubUrl = navigationManager.ToAbsoluteUri("/hubs/slam");
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(hubUrl)
|
||||
.WithAutomaticReconnect()
|
||||
.Build();
|
||||
|
||||
// Subscribe to server events
|
||||
_hubConnection.On<SLAMState>("OnStateChanged", state =>
|
||||
{
|
||||
CurrentState = state;
|
||||
StateChanged?.Invoke(state);
|
||||
});
|
||||
|
||||
_hubConnection.On<int, int, int>("OnMapSaveProgress", (workItemsAdded, workItemsCompleted, percentComplete) =>
|
||||
{
|
||||
MapSaveProgressChanged?.Invoke(workItemsAdded, workItemsCompleted, percentComplete);
|
||||
});
|
||||
|
||||
_hubConnection.On<string, bool>("OnMapProcessingChanged", (mapName, isProcessing) =>
|
||||
{
|
||||
IsProcessingChanged?.Invoke(mapName, isProcessing);
|
||||
});
|
||||
|
||||
_hubConnection.Reconnected += connectionId =>
|
||||
{
|
||||
// Refresh state after reconnect
|
||||
_ = RefreshStateAsync();
|
||||
return Task.CompletedTask;
|
||||
};
|
||||
}
|
||||
|
||||
public async Task StartAsync()
|
||||
{
|
||||
if (_hubConnection.State == HubConnectionState.Disconnected)
|
||||
{
|
||||
await _hubConnection.StartAsync();
|
||||
// Get initial state
|
||||
await RefreshStateAsync();
|
||||
}
|
||||
}
|
||||
|
||||
public async Task StopAsync()
|
||||
{
|
||||
if (_hubConnection.State != HubConnectionState.Disconnected)
|
||||
{
|
||||
await _hubConnection.StopAsync();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Refresh current state from server
|
||||
/// </summary>
|
||||
public async Task RefreshStateAsync()
|
||||
{
|
||||
if (IsConnected)
|
||||
{
|
||||
try
|
||||
{
|
||||
CurrentState = await _hubConnection.InvokeAsync<SLAMState>("GetCurrentState");
|
||||
}
|
||||
catch
|
||||
{
|
||||
// Ignore errors
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Server method invocations
|
||||
|
||||
public async Task<SLAMState> GetCurrentStateAsync()
|
||||
{
|
||||
var state = await _hubConnection.InvokeAsync<SLAMState>("GetCurrentState");
|
||||
CurrentState = state;
|
||||
return state;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lấy tên map hiện tại đang được sử dụng
|
||||
/// </summary>
|
||||
public async Task<string?> GetCurrentMapAsync()
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<string?>("GetCurrentMap");
|
||||
}
|
||||
|
||||
public async Task<bool> StartLocalizationAsync(string mapName, PoseDto? initialPose = null)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<bool>("StartLocalization", mapName, initialPose);
|
||||
}
|
||||
|
||||
public async Task StopLocalizationAsync()
|
||||
{
|
||||
await _hubConnection.InvokeAsync("StopLocalization");
|
||||
}
|
||||
|
||||
public async Task<bool> StartScanMappingAsync(string mapName)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<bool>("StartScanMapping", mapName);
|
||||
}
|
||||
|
||||
public async Task<string?> SaveMapAsync()
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<string?>("SaveMap");
|
||||
}
|
||||
|
||||
public async Task<MapInfoDto[]> ListMapsAsync()
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<MapInfoDto[]>("ListMaps");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lấy thông tin map và đăng ký nhận cập nhật trạng thái xử lý
|
||||
/// </summary>
|
||||
/// <param name="mapName">Tên map</param>
|
||||
/// <returns>MapInfoDto với trạng thái IsProcessing</returns>
|
||||
public async Task<MapInfoDto?> GetMapInfoAndSubscribeProcessingAsync(string mapName)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<MapInfoDto?>("GetMapInfoAndSubscribeProcessing", mapName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Hủy đăng ký nhận cập nhật trạng thái xử lý
|
||||
/// </summary>
|
||||
/// <param name="mapName">Tên map</param>
|
||||
public async Task UnsubscribeMapProcessingAsync(string mapName)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("UnsubscribeMapProcessing", mapName);
|
||||
}
|
||||
|
||||
public async Task<bool> DeleteMapAsync(string mapName)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<bool>("DeleteMap", mapName);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Transform map origin to a new pose
|
||||
/// </summary>
|
||||
/// <param name="mapName">Name of the map to transform</param>
|
||||
/// <param name="newOrigin">New origin pose (position and orientation)</param>
|
||||
/// <returns>True if transform was successful</returns>
|
||||
public async Task<bool> TransformMapOriginAsync(string mapName, PoseDto newOrigin)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<bool>("TransformMapOrigin", mapName, newOrigin);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rerender map image files (PNG, JPG, PGM) with custom OccupancyGridConfiguration.
|
||||
/// This is an async operation - returns true if processing started successfully.
|
||||
/// Monitor IsProcessingChanged event for completion notification.
|
||||
/// </summary>
|
||||
/// <param name="mapName">Name of the map to rerender</param>
|
||||
/// <param name="config">Custom occupancy grid configuration</param>
|
||||
/// <returns>True if rerender was started successfully</returns>
|
||||
public async Task<bool> RerenderMapWithConfigAsync(string mapName, OccupancyGridConfigurationDto config)
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<bool>("RerenderMapWithConfig", mapName, config);
|
||||
}
|
||||
|
||||
public async Task SetInitialPoseAsync(PoseDto pose)
|
||||
{
|
||||
await _hubConnection.InvokeAsync("SetInitialPose", pose);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lấy occupancy grid mới hơn thời gian chỉ định
|
||||
/// </summary>
|
||||
/// <param name="since">Thời gian để so sánh</param>
|
||||
/// <returns>OccupancyGridDto nếu có update mới hơn, null nếu không có</returns>
|
||||
public async Task<OccupancyGridDto?> GetOccupancyGridAsync(DateTime since)
|
||||
{
|
||||
var grid = await _hubConnection.InvokeAsync<OccupancyGridDto?>("GetOccupancyGrid", since);
|
||||
return grid;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lấy pose hiện tại của robot
|
||||
/// </summary>
|
||||
/// <returns>PoseDto nếu có pose, null nếu không có</returns>
|
||||
public async Task<PoseDto?> GetCurrentPoseAsync()
|
||||
{
|
||||
var pose = await _hubConnection.InvokeAsync<PoseDto?>("GetCurrentPose");
|
||||
if (pose != null)
|
||||
{
|
||||
CurrentPose = pose;
|
||||
}
|
||||
return pose;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Lấy sample point cloud từ tất cả lidar devices (trong global frame)
|
||||
/// </summary>
|
||||
/// <returns>Danh sách Point32 trong global frame</returns>
|
||||
public async Task<RobotNet10.Shared.Numbers.Vector3[]> GetSamplePointCloudAsync()
|
||||
{
|
||||
return await _hubConnection.InvokeAsync<RobotNet10.Shared.Numbers.Vector3[]>("GetSamplePointCloud") ?? [];
|
||||
}
|
||||
|
||||
public async ValueTask DisposeAsync()
|
||||
{
|
||||
if (_disposed)
|
||||
return;
|
||||
|
||||
_disposed = true;
|
||||
await StopAsync();
|
||||
await _hubConnection.DisposeAsync();
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user