@page "/" @attribute [Authorize] @using RobotApp.Client.Services @using RobotApp.VDA5050.State @using MudBlazor @inject RobotStateClient RobotStateClient @implements IDisposable @rendermode InteractiveWebAssembly
Robot Dashboard @if (CurrentState != null) { @CurrentState.Version • @CurrentState.Manufacturer • @CurrentState.SerialNumber } else { Connecting to robot... }
@if (CurrentState != null) { @(IsConnected ? "ONLINE" : "OFFLINE") }
@{ var msg = CurrentState ?? EmptyState; } Header ID @msg.HeaderId Timestamp (UTC) @msg.Timestamp Version @msg.Version Order Update ID @msg.OrderUpdateId Position & Velocity NewBase: @(msg.NewBaseRequest ? "TRUE" : "FALSE") X: @msg.AgvPosition.X.ToString("F2") m Y: @msg.AgvPosition.Y.ToString("F2") m θ: @msg.AgvPosition.Theta.ToString("F2") rad Vx: @msg.Velocity.Vx.ToString("F2") m/s Vy: @msg.Velocity.Vy.ToString("F2") m/s Ω: @msg.Velocity.Omega.ToString("F3") rad/s
@(msg.AgvPosition.PositionInitialized ? "Initialized" : "Not Initialized") Deviation: @msg.AgvPosition.DeviationRange
Localization Score: @(msg.AgvPosition.LocalizationScore * 100)%
Battery Status
@msg.BatteryState.BatteryCharge:F1% @(msg.BatteryState.Charging ? "Charging" : "Discharging")
Voltage @msg.BatteryState.BatteryVoltage:F1 V Health (SOH) @msg.BatteryState.BatteryHealth% Reach @((int)msg.BatteryState.Reach) m
Order & Path Order ID: @(msg.OrderId ?? "—") Update ID: @msg.OrderUpdateId Last Node: @msg.LastNodeId (Seq: @msg.LastNodeSequenceId) Distance since last: @msg.DistanceSinceLastNode:F1 m @{ var nodeReleased = msg.NodeStates?.Count(n => n.Released) ?? 0; var nodeTotal = msg.NodeStates?.Length ?? 0; var edgeReleased = msg.EdgeStates?.Count(e => e.Released) ?? 0; var edgeTotal = msg.EdgeStates?.Length ?? 0; }
Nodes: @nodeReleased/@nodeTotal Edges: @edgeReleased/@edgeTotal @(msg.Driving ? "DRIVING" : "STOPPED") @(msg.Paused ? "PAUSED" : "ACTIVE")
Errors & Information Type Level Description @context.Type @context.Level @context.Description No errors or information messages Active Actions Action ID Status @context.ActionType @context.ActionId @context.ActionStatus No active actions Safety State E-STOP: @msg.SafetyState.EStop Field Violation: @(msg.SafetyState.FieldViolation ? "YES" : "NO")
@code { private static readonly StateMsg EmptyState = new() { }; private StateMsg? CurrentState; private bool IsConnected => RobotStateClient.LatestStates.ContainsKey(RobotSerial); private readonly string RobotSerial = "T800-002"; private List MessageRows = new(); protected override async Task OnInitializedAsync() { RobotStateClient.OnStateReceived += OnRobotStateReceived; if (RobotStateClient.LatestStates.Count == 0) { await RobotStateClient.StartAsync(); } await RobotStateClient.SubscribeRobotAsync(RobotSerial); CurrentState = RobotStateClient.GetLatestState(RobotSerial); UpdateMessageRows(); } private void OnRobotStateReceived(string serialNumber, StateMsg state) { if (serialNumber != RobotSerial) return; InvokeAsync(() => { CurrentState = state; UpdateMessageRows(); StateHasChanged(); }); } private void UpdateMessageRows() { MessageRows.Clear(); if (CurrentState?.Errors != null) { foreach (var err in CurrentState.Errors) { MessageRows.Add(new MessageRow(err.ErrorType ?? "-", err.ErrorLevel ?? "ERROR", err.ErrorDescription ?? "", true)); } } if (CurrentState?.Information != null) { foreach (var info in CurrentState.Information) { MessageRows.Add(new MessageRow(info.InfoType ?? "-", info.InfoLevel ?? "INFO", info.InfoDescription ?? "", false)); } } } public void Dispose() { RobotStateClient.OnStateReceived -= OnRobotStateReceived; } private record MessageRow(string Type, string Level, string Description, bool IsError); }