diff --git a/RobotApp.Client/Pages/Dashboard.razor b/RobotApp.Client/Pages/Dashboard.razor index 937e889..3a22372 100644 --- a/RobotApp.Client/Pages/Dashboard.razor +++ b/RobotApp.Client/Pages/Dashboard.razor @@ -13,7 +13,7 @@
- Robot Dashboard + Robot Dashboard @if (CurrentState != null) { @@ -38,8 +38,8 @@ { 20 ? Color.Warning : Color.Error)" Class="mb-4" Style="height: 28px;" /> @@ -266,7 +266,7 @@ @context.Level @@ -317,8 +317,8 @@ @context.ActionStatus @@ -373,45 +373,34 @@ private StateMsg? CurrentState; private bool IsConnected; - private readonly string RobotSerial = "T800-002"; private List MessageRows = new(); - protected override async Task OnInitializedAsync() + protected override async Task OnAfterRenderAsync(bool firstRender) { + await base.OnAfterRenderAsync(firstRender); + if (!firstRender) return; RobotStateClient.OnStateReceived += OnRobotStateReceived; RobotStateClient.OnRobotConnectionChanged += OnRobotConnectionChanged; - if (RobotStateClient.ConnectionState == RobotClientState.Disconnected) - { - await RobotStateClient.StartAsync(); - } - - await RobotStateClient.SubscribeRobotAsync(RobotSerial); - - CurrentState = RobotStateClient.GetLatestState(RobotSerial); + await RobotStateClient.StartAsync(); + CurrentState = RobotStateClient.GetLatestState(); IsConnected = RobotStateClient.IsRobotConnected; UpdateMessageRows(); } + private void OnRobotConnectionChanged(bool connected) { - InvokeAsync(() => - { - IsConnected = connected; - StateHasChanged(); - }); + IsConnected = connected; + StateHasChanged(); } private void OnRobotStateReceived(string serialNumber, StateMsg state) { - if (serialNumber != RobotSerial) return; - InvokeAsync(() => - { - CurrentState = state; - UpdateMessageRows(); - StateHasChanged(); - }); + CurrentState = state; + UpdateMessageRows(); + StateHasChanged(); } private void UpdateMessageRows() diff --git a/RobotApp.Client/Pages/RobotMonitor.razor b/RobotApp.Client/Pages/RobotMonitor.razor index 5d767ab..4309da8 100644 --- a/RobotApp.Client/Pages/RobotMonitor.razor +++ b/RobotApp.Client/Pages/RobotMonitor.razor @@ -17,6 +17,7 @@ protected override async Task OnAfterRenderAsync(bool firstRender) { + await base.OnAfterRenderAsync(firstRender); if (firstRender) { MonitorService.OnDataReceived += OnMonitorDataReceived; @@ -28,7 +29,7 @@ { _monitorData = data; RobotMonitorViewRef?.UpdatePath(); - InvokeAsync(StateHasChanged); + StateHasChanged(); } public async ValueTask DisposeAsync() @@ -41,3 +42,7 @@ + + + + diff --git a/RobotApp.Client/Services/RobotStateClient.cs b/RobotApp.Client/Services/RobotStateClient.cs index ece5fa0..9834846 100644 --- a/RobotApp.Client/Services/RobotStateClient.cs +++ b/RobotApp.Client/Services/RobotStateClient.cs @@ -107,7 +107,7 @@ public sealed class RobotStateClient : IAsyncDisposable // ================= SIGNALR HANDLERS ================= // VDA5050 State - _connection.On("ReceiveState", HandleState); + _connection.On("ReceiveState", HandleState); // Robot connection (bool only) _connection.On("ReceiveRobotConnection", HandleRobotConnection); @@ -125,21 +125,21 @@ public sealed class RobotStateClient : IAsyncDisposable } // ================= HANDLE STATE ================= - private void HandleState(string stateJson) + private void HandleState(StateMsg state) { - StateMsg? state; + //StateMsg? state; - try - { - state = JsonSerializer.Deserialize( - stateJson, - JsonOptionExtends.Read - ); - } - catch - { - return; - } + //try + //{ + // state = JsonSerializer.Deserialize( + // stateJson, + // JsonOptionExtends.Read + // ); + //} + //catch + //{ + // return; + //} if (state?.SerialNumber == null) return; @@ -189,10 +189,10 @@ public sealed class RobotStateClient : IAsyncDisposable } // ================= GET CACHE ================= - public StateMsg? GetLatestState(string serialNumber) + public StateMsg? GetLatestState() { - LatestStates.TryGetValue(serialNumber, out var state); - return state; + if (!LatestStates.IsEmpty) return LatestStates.First().Value; + return null; } // ================= DISPOSE ================= diff --git a/RobotApp.Client/wwwroot/js/robotMonitor.js b/RobotApp.Client/wwwroot/js/robotMonitor.js index 192537f..10a95b6 100644 --- a/RobotApp.Client/wwwroot/js/robotMonitor.js +++ b/RobotApp.Client/wwwroot/js/robotMonitor.js @@ -60,3 +60,7 @@ window.robotMonitor = { + + + + diff --git a/RobotApp/Controllers/OrderController.cs b/RobotApp/Controllers/OrderController.cs index 33ec6f9..63792ef 100644 --- a/RobotApp/Controllers/OrderController.cs +++ b/RobotApp/Controllers/OrderController.cs @@ -28,7 +28,6 @@ public class OrderController(IOrder robotOrderController, IInstantActions instan { robotOrderController.StopOrder(); instantActions.StopOrderAction(); - return Ok(new { success = true, diff --git a/RobotApp/Hubs/RobotHub.cs b/RobotApp/Hubs/RobotHub.cs index 0ab9023..161625a 100644 --- a/RobotApp/Hubs/RobotHub.cs +++ b/RobotApp/Hubs/RobotHub.cs @@ -21,8 +21,8 @@ namespace RobotApp.Hubs // Phương thức này sẽ được gọi từ service để broadcast public async Task SendState(string serialNumber, StateMsg state) { - var json = JsonSerializer.Serialize(state, JsonOptionExtends.Write); - await Clients.Group(serialNumber).SendAsync("ReceiveState", json); + //var json = JsonSerializer.Serialize(state, JsonOptionExtends.Write); + await Clients.Group(serialNumber).SendAsync("ReceiveState", state); } } } \ No newline at end of file diff --git a/RobotApp/Hubs/RobotMonitorHub.cs b/RobotApp/Hubs/RobotMonitorHub.cs index fa1421f..c5bf2c9 100644 --- a/RobotApp/Hubs/RobotMonitorHub.cs +++ b/RobotApp/Hubs/RobotMonitorHub.cs @@ -12,3 +12,7 @@ public class RobotMonitorHub : Hub + + + + diff --git a/RobotApp/Program.cs b/RobotApp/Program.cs index 7696d26..6e570f0 100644 --- a/RobotApp/Program.cs +++ b/RobotApp/Program.cs @@ -63,7 +63,7 @@ builder.Services.AddRobot(); builder.Services.AddSingleton(); builder.Services.AddHostedService(sp => sp.GetRequiredService()); -builder.Services.AddScoped(); +//builder.Services.AddScoped(); builder.Services.AddHostedService(); var app = builder.Build(); diff --git a/RobotApp/Services/Robot/RobotErrors.cs b/RobotApp/Services/Robot/RobotErrors.cs index a8b289c..cdaf990 100644 --- a/RobotApp/Services/Robot/RobotErrors.cs +++ b/RobotApp/Services/Robot/RobotErrors.cs @@ -105,7 +105,8 @@ public class RobotErrors : IError => CreateError(ErrorType.INITIALIZE_ORDER, "Vui lòng kiểm tra lại order", ErrorLevel.WARNING, $"Order mới nhận được không phải là nối tiếp của order khi LastNodeSequenceId: {lastNodeSequenceId} mà node đầu tiên của order mới có sequence: {newStartNodeSequenceId}"); public static Error Error1018(int oldOrderUpdateId, int newOrderUpdateId) => CreateError(ErrorType.INITIALIZE_ORDER, "Vui lòng kiểm tra lại OrderUpdateId", ErrorLevel.WARNING, $"OrderUpdateId {newOrderUpdateId} nhận được nhỏ hơn OrderUpdateId hiện tại là {oldOrderUpdateId}"); - + public static Error Error1019() + => CreateError(ErrorType.INITIALIZE_ORDER, "Vui lòng kiểm tra lại Order", ErrorLevel.WARNING, "Order có node đầu tiên quá xa robot"); public static Error Error2001() => CreateError(ErrorType.READ_PERIPHERAL_FAILURE, "2001", ErrorLevel.FATAL, "Có lỗi xảy ra trong quá trình đọc tín hiệu từ hệ thống ngoại vi(PLC)"); diff --git a/RobotApp/Services/Robot/RobotOrderController.cs b/RobotApp/Services/Robot/RobotOrderController.cs index 730f17d..f20f78c 100644 --- a/RobotApp/Services/Robot/RobotOrderController.cs +++ b/RobotApp/Services/Robot/RobotOrderController.cs @@ -9,6 +9,7 @@ using RobotApp.VDA5050.Order; using RobotApp.VDA5050.State; using System.Collections.Concurrent; using System.Data; +using System.Xml.Linq; using Action = RobotApp.VDA5050.InstantAction.Action; namespace RobotApp.Services.Robot; @@ -247,11 +248,6 @@ public class RobotOrderController(INavigation NavigationManager, UpdateState(); } - private bool IsNewPath() - { - return true; - } - private void ClearLastNode() { if (LastNode is null) return; @@ -292,6 +288,7 @@ public class RobotOrderController(INavigation NavigationManager, private void HandleOrder() { + if (Nodes.Length <= 0) return; if (IsCancelOrder) { NavigationManager.CancelMovement(); @@ -309,9 +306,13 @@ public class RobotOrderController(INavigation NavigationManager, { var action = FinalAction[0]; var robotAction = ActionManager[action.ActionId]; - if (robotAction is null) return; - if (robotAction.IsCompleted) FinalAction.Remove(action); - if (robotAction.Status == ActionStatus.WAITING) ActionManager.StartOrderAction(action.ActionId); + if (robotAction is null) + { + FinalAction.Remove(action); + return; + } + if (robotAction.IsCompleted) + if (robotAction.Status == ActionStatus.WAITING) ActionManager.StartOrderAction(action.ActionId); } else { @@ -398,6 +399,10 @@ public class RobotOrderController(INavigation NavigationManager, if (NodeStates.Length != 0 || EdgeStates.Length != 0) HandleUpdateOrder(NewOrderHandler); else { + Node startNode = NewOrderHandler.Nodes[0]; + var nodeDeviation = startNode.NodePosition.AllowedDeviationXY == 0.0 ? NewOrderHandler.Nodes.Length == 1 ? 0.3 : 0.5 : startNode.NodePosition.AllowedDeviationXY; + var distance = Localization.DistanceTo(startNode.NodePosition.X, startNode.NodePosition.Y); + if (distance > nodeDeviation) throw new OrderException(RobotErrors.Error1019()); HandleNewOrder(NewOrderHandler); } } @@ -409,6 +414,7 @@ public class RobotOrderController(INavigation NavigationManager, { ErrorManager.AddError(orEx.Error, TimeSpan.FromSeconds(10)); Logger.Warning($"Lỗi khi xử lí Order: {orEx.Error.ErrorDescription}"); + HandleOrderStop(); } else Logger.Warning($"Lỗi khi xử lí Order: {orEx.Message}"); } diff --git a/RobotApp/Services/Robot/RobotStatePublisher.cs b/RobotApp/Services/Robot/RobotStatePublisher.cs index 8f1d189..fbda7b9 100644 --- a/RobotApp/Services/Robot/RobotStatePublisher.cs +++ b/RobotApp/Services/Robot/RobotStatePublisher.cs @@ -12,148 +12,27 @@ using System.Text.Json; namespace RobotApp.Services.Robot; -public class RobotStatePublisher : BackgroundService +public class RobotStatePublisher( + IHubContext _hubContext, + RobotStates _robotState, + RobotConnection _robotConnection) : BackgroundService { - private readonly IHubContext _hubContext; - private readonly RobotConfiguration _robotConfig; - private readonly IOrder _orderManager; - private readonly IInstantActions _actionManager; - private readonly IPeripheral _peripheralManager; - private readonly IInfomation _infoManager; - private readonly IError _errorManager; - private readonly ILocalization _localizationManager; - private readonly IBattery _batteryManager; - private readonly ILoad _loadManager; - private readonly INavigation _navigationManager; - private readonly RobotStateMachine _stateManager; - private readonly RobotConnection _robotConnection; private bool? _lastRobotConnectionState; - private uint _headerId = 0; private readonly PeriodicTimer _timer = new(TimeSpan.FromMilliseconds(1000)); // 1 giây/lần - public RobotStatePublisher( - IHubContext hubContext, - RobotConfiguration robotConfig, - IOrder orderManager, - IInstantActions actionManager, - IPeripheral peripheralManager, - IInfomation infoManager, - IError errorManager, - ILocalization localizationManager, - IBattery batteryManager, - ILoad loadManager, - INavigation navigationManager, - RobotStateMachine stateManager, - RobotConnection robotConnection) - { - _hubContext = hubContext; - _robotConfig = robotConfig; - _orderManager = orderManager; - _actionManager = actionManager; - _peripheralManager = peripheralManager; - _infoManager = infoManager; - _errorManager = errorManager; - _localizationManager = localizationManager; - _batteryManager = batteryManager; - _loadManager = loadManager; - _navigationManager = navigationManager; - _stateManager = stateManager; - _robotConnection = robotConnection; - } - - private StateMsg GetStateMsg() - { - return new StateMsg - { - HeaderId = _headerId++, - Timestamp = DateTime.UtcNow.ToString("o"), // ISO 8601 - Manufacturer = _robotConfig.VDA5050Setting.Manufacturer, - Version = _robotConfig.VDA5050Setting.Version, - SerialNumber = _robotConfig.SerialNumber, - Maps = [], - OrderId = _orderManager.OrderId, - OrderUpdateId = _orderManager.OrderUpdateId, - ZoneSetId = "", - LastNodeId = _orderManager.LastNodeId, - LastNodeSequenceId = _orderManager.LastNodeSequenceId, - Driving = Math.Abs(_navigationManager.VelocityX) > 0.01 || Math.Abs(_navigationManager.Omega) > 0.01, - Paused = false, - NewBaseRequest = true, - DistanceSinceLastNode = 0, - OperatingMode = _peripheralManager.PeripheralMode.ToString(), - NodeStates = _orderManager.NodeStates, - EdgeStates = _orderManager.EdgeStates, - ActionStates = _actionManager.ActionStates, - Information = [General, .. _infoManager.InformationState], - Errors = _errorManager.ErrorsState, - AgvPosition = new AgvPosition - { - X = _localizationManager.X, - Y = _localizationManager.Y, - Theta = _localizationManager.Theta, - LocalizationScore = _localizationManager.MatchingScore, - MapId = _localizationManager.CurrentActiveMap, - DeviationRange = _localizationManager.Reliability, - PositionInitialized = _localizationManager.IsReady, - }, - BatteryState = new BatteryState - { - Charging = _batteryManager.IsCharging, - BatteryHealth = _batteryManager.SOH, - Reach = 0, - BatteryVoltage = _batteryManager.Voltage, - BatteryCharge = _batteryManager.SOC, - }, - Loads = _loadManager.Load, - Velocity = new Velocity - { - Vx = _navigationManager.VelocityX, - Vy = _navigationManager.VelocityY, - Omega = _navigationManager.Omega, - }, - SafetyState = new SafetyState - { - FieldViolation = _peripheralManager.LidarBackProtectField || - _peripheralManager.LidarFrontProtectField || - _peripheralManager.LidarFrontTimProtectField, - EStop = (_peripheralManager.Emergency || _peripheralManager.Bumper) - ? EStop.AUTOACK.ToString() - : EStop.NONE.ToString(), - } - }; - } - - private Information General => new() - { - InfoType = InformationType.robot_general.ToString(), - InfoDescription = "Thông tin chung của robot", - InfoLevel = InfoLevel.INFO.ToString(), - InfoReferences = - [ - new InfomationReferences - { - ReferenceKey = InformationReferencesKey.robot_state.ToString(), - ReferenceValue = _stateManager.CurrentStateName, - } - ] - }; - protected override async Task ExecuteAsync(CancellationToken stoppingToken) { while (await _timer.WaitForNextTickAsync(stoppingToken)) { try { - var serialNumber = _robotConfig.SerialNumber; - // ===== SEND STATE ===== - var state = GetStateMsg(); - var json = JsonSerializer.Serialize(state, JsonOptionExtends.Write); + var state = _robotState.GetStateMsg(); + //var json = JsonSerializer.Serialize(state, JsonOptionExtends.Write); - await _hubContext.Clients - .Group(serialNumber) - .SendAsync("ReceiveState", json, stoppingToken); + await _hubContext.Clients.All + .SendAsync("ReceiveState", state, stoppingToken); // ===== SEND ROBOT CONNECTION (ONLY WHEN CHANGED) ===== var isConnected = _robotConnection.IsConnected; @@ -162,8 +41,7 @@ public class RobotStatePublisher : BackgroundService { _lastRobotConnectionState = isConnected; - await _hubContext.Clients - .Group(serialNumber) // routing only + await _hubContext.Clients.All .SendAsync( "ReceiveRobotConnection", isConnected, // payload only bool @@ -177,10 +55,10 @@ public class RobotStatePublisher : BackgroundService } } - - public override void Dispose() + public override Task StopAsync(CancellationToken cancellationToken) { _timer?.Dispose(); base.Dispose(); + return base.StopAsync(cancellationToken); } } \ No newline at end of file diff --git a/RobotApp/Services/Robot/RobotStates.cs b/RobotApp/Services/Robot/RobotStates.cs index a8f6032..16a945e 100644 --- a/RobotApp/Services/Robot/RobotStates.cs +++ b/RobotApp/Services/Robot/RobotStates.cs @@ -36,7 +36,7 @@ public class RobotStates(RobotConfiguration RobotConfiguration, catch { } } - private StateMsg GetStateMsg() + public StateMsg GetStateMsg() { return new StateMsg {