@page "/robots/{robotId}/detail"
@rendermode RobotNet10.Components.RenderMode.InteractiveWebAssemblyNoPrerender
@implements IAsyncDisposable
Robot Detail - @robotName
@using RobotNet10.FleetManager.Client.Services
@using RobotNet10.FleetManager.Client.Components.RobotDetail
@using RobotNet10.FleetManager.Shared.DTOs.Robot
@using RobotNet.VDA5050.State
@using RobotNet.VDA5050.Visualization
@using Microsoft.AspNetCore.SignalR.Client
@inject RobotApiService RobotApiService
@inject RobotStateHubClient HubClient
@inject NavigationManager NavigationManager
@inject ISnackbar Snackbar
Robot Detail: @robotName
@if (isRefreshing)
{
}
Refresh
@if (isLoadingRobot)
{
Loading robot information...
}
else if (robot == null)
{
Robot Not Found
The robot with ID '@RobotId' could not be found.
Go Back to Robot List
}
else
{
}
@code {
[Parameter]
public string RobotId { get; set; } = string.Empty;
private RobotDto? robot;
private string robotName = "Loading...";
private bool isLoadingRobot = true;
private bool isRefreshing = false;
private HeaderInfoCard HeaderInfoCardRef = default!;
private PositionCard PositionCardRef = default!;
private VelocityCard VelocityCardRef = default!;
private BatteryCard BatteryCardRef = default!;
private SafetyStateCard SafetyStateCardRef = default!;
private MapsCard MapsCardRef = default!;
private NodeStatesCard NodeStatesCardRef = default!;
private EdgeStatesCard EdgeStatesCardRef = default!;
private OrderInfoCard OrderInfoCardRef = default!;
private ActionStatesCard ActionStatesCardRef = default!;
private LoadsCard LoadsCardRef = default!;
private ErrorsCard ErrorsCardRef = default!;
private InformationCard InformationCardRef = default!;
private int ActivePanelIndex = 0;
protected override async Task OnInitializedAsync()
{
await LoadRobotAsync();
await InitializeSignalRAsync();
}
private async Task LoadRobotAsync()
{
isLoadingRobot = true;
try
{
robot = await RobotApiService.GetByRobotIdAsync(RobotId);
if (robot != null)
{
robotName = robot.Name;
}
else
{
Snackbar.Add($"Robot with ID '{RobotId}' not found", Severity.Error);
}
}
catch (Exception ex)
{
Snackbar.Add($"Error loading robot: {ex.Message}", Severity.Error);
}
finally
{
isLoadingRobot = false;
StateHasChanged();
}
}
private async Task InitializeSignalRAsync()
{
try
{
// Register event handlers
HubClient.OnStateUpdate += HandleStateUpdate;
HubClient.OnConnectionError += HandleConnectionError;
// Connect and subscribe
await HubClient.ConnectAsync();
await HubClient.SubscribeToRobotAsync(RobotId);
}
catch (Exception ex)
{
Snackbar.Add($"Error connecting to SignalR: {ex.Message}", Severity.Error);
}
}
private void StateUpdate(StateMsg? state)
{
switch (ActivePanelIndex)
{
case 0:
HeaderInfoCardRef.Update(state);
BatteryCardRef.Update(state?.BatteryState);
SafetyStateCardRef.Update(state);
PositionCardRef.Update(state);
VelocityCardRef.Update(state);
break;
case 1:
MapsCardRef.Update(state);
NodeStatesCardRef.Update(state);
EdgeStatesCardRef.Update(state);
break;
case 2:
OrderInfoCardRef.Update(state);
ActionStatesCardRef.Update(state);
LoadsCardRef.Update(state);
break;
case 3:
ErrorsCardRef.Update(state?.Errors);
InformationCardRef.Update(state?.Information);
break;
}
}
private void HandleStateUpdate(StateMsg state)
{
if (state.SerialNumber != RobotId) return;
StateUpdate(state);
}
private void HandleConnectionError(string error)
{
Snackbar.Add($"SignalR connection error: {error}", Severity.Warning);
}
private async Task HandleRefresh()
{
isRefreshing = true;
StateHasChanged();
await LoadRobotAsync();
isRefreshing = false;
StateHasChanged();
}
private void NavigateBack()
{
NavigationManager.NavigateTo("/robots");
}
public async ValueTask DisposeAsync()
{
if (HubClient != null)
{
HubClient.OnStateUpdate -= HandleStateUpdate;
HubClient.OnConnectionError -= HandleConnectionError;
await HubClient.UnsubscribeFromRobotAsync(RobotId);
}
}
}