Initial commit
This commit is contained in:
@@ -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