282 lines
11 KiB
Plaintext
282 lines
11 KiB
Plaintext
@page "/robots/{robotId}/detail"
|
|
@rendermode RobotNet10.Components.RenderMode.InteractiveWebAssemblyNoPrerender
|
|
@implements IAsyncDisposable
|
|
|
|
<PageTitle>Robot Detail - @robotName</PageTitle>
|
|
|
|
@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
|
|
|
|
<MudContainer MaxWidth="MaxWidth.ExtraExtraLarge" Class="mt-4">
|
|
<!-- Header -->
|
|
<MudPaper Class="pa-4 mb-4 align-content-center" MinHeight="80px">
|
|
<MudStack Row="true" AlignItems="AlignItems.Center" Justify="Justify.SpaceBetween" Spacing="3">
|
|
<MudStack Row="true" AlignItems="AlignItems.Center" Spacing="2">
|
|
<MudIconButton Icon="@Icons.Material.Filled.ArrowBack" OnClick="NavigateBack" />
|
|
<MudText Typo="Typo.h5">Robot Detail: @robotName</MudText>
|
|
</MudStack>
|
|
<MudButton StartIcon="@Icons.Material.Filled.Refresh"
|
|
Variant="Variant.Outlined"
|
|
Color="Color.Primary"
|
|
OnClick="HandleRefresh"
|
|
Disabled="@isRefreshing">
|
|
@if (isRefreshing)
|
|
{
|
|
<MudProgressCircular Class="mr-2" Size="Size.Small" Indeterminate="true" />
|
|
}
|
|
Refresh
|
|
</MudButton>
|
|
</MudStack>
|
|
</MudPaper>
|
|
|
|
@if (isLoadingRobot)
|
|
{
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudStack AlignItems="AlignItems.Center" Spacing="3">
|
|
<MudProgressCircular Color="Color.Primary" Indeterminate="true" Size="Size.Large" />
|
|
<MudText Typo="Typo.body1">Loading robot information...</MudText>
|
|
</MudStack>
|
|
</MudPaper>
|
|
}
|
|
else if (robot == null)
|
|
{
|
|
<MudPaper Class="pa-4" Elevation="1">
|
|
<MudAlert Severity="Severity.Error" Variant="Variant.Filled">
|
|
<MudText Typo="Typo.h6">Robot Not Found</MudText>
|
|
<MudText Typo="Typo.body2">The robot with ID '@RobotId' could not be found.</MudText>
|
|
<MudButton Variant="Variant.Text" Color="Color.Error" OnClick="NavigateBack" Class="mt-2">
|
|
<MudIcon Icon="@Icons.Material.Filled.ArrowBack" Class="mr-2" />
|
|
Go Back to Robot List
|
|
</MudButton>
|
|
</MudAlert>
|
|
</MudPaper>
|
|
}
|
|
else
|
|
{
|
|
<!-- Main Content -->
|
|
<MudGrid>
|
|
<!-- Left: State Cards with Tabs (60%) -->
|
|
<MudItem xs="12" md="7">
|
|
<MudPaper Class="pa-2" Elevation="1" Style="height: calc(100vh - 200px); overflow: hidden">
|
|
<MudTabs @bind-ActivePanelIndex="ActivePanelIndex" Elevation="0" Rounded="true" Color="Color.Primary">
|
|
<!-- Overview Tab -->
|
|
<MudTabPanel Text="Overview" Icon="@Icons.Material.Filled.Dashboard">
|
|
<MudStack Spacing="3" Class="mt-2" Style="overflow-y: auto; height: calc(100vh - 288px);">
|
|
<HeaderInfoCard @ref="HeaderInfoCardRef" />
|
|
|
|
<MudGrid Spacing="3">
|
|
<MudItem xs="12" md="6">
|
|
<PositionCard @ref="PositionCardRef" />
|
|
</MudItem>
|
|
<MudItem xs="12" md="6">
|
|
<VelocityCard @ref="VelocityCardRef" />
|
|
</MudItem>
|
|
</MudGrid>
|
|
|
|
<MudGrid Spacing="3">
|
|
<MudItem xs="12" md="6">
|
|
<BatteryCard @ref="BatteryCardRef" />
|
|
</MudItem>
|
|
<MudItem xs="12" md="6">
|
|
<SafetyStateCard @ref="SafetyStateCardRef" />
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudStack>
|
|
</MudTabPanel>
|
|
|
|
<!-- Navigation Tab -->
|
|
<MudTabPanel Text="Navigation" Icon="@Icons.Material.Filled.Navigation">
|
|
<MudStack Spacing="3" Class="mt-2" Style="overflow-y: auto; height: calc(100vh - 288px);">
|
|
<MapsCard @ref="MapsCardRef" />
|
|
<NodeStatesCard @ref="NodeStatesCardRef" />
|
|
<EdgeStatesCard @ref="EdgeStatesCardRef" />
|
|
</MudStack>
|
|
</MudTabPanel>
|
|
|
|
<!-- Orders & Actions Tab -->
|
|
<MudTabPanel Text="Orders & Actions" Icon="@Icons.Material.Filled.ListAlt">
|
|
<MudStack Spacing="3" Class="mt-2" Style="overflow-y: auto; height: calc(100vh - 288px);">
|
|
<OrderInfoCard @ref="OrderInfoCardRef" />
|
|
<ActionStatesCard @ref="ActionStatesCardRef" />
|
|
<LoadsCard @ref="LoadsCardRef" />
|
|
</MudStack>
|
|
</MudTabPanel>
|
|
|
|
<!-- Errors & Information Tab -->
|
|
<MudTabPanel Text="Errors & Info" Icon="@Icons.Material.Filled.Info">
|
|
<MudStack Spacing="3" Class="mt-2" Style="overflow-y: auto; height: calc(100vh - 288px);">
|
|
<ErrorsCard @ref="ErrorsCardRef" />
|
|
<InformationCard @ref="InformationCardRef" />
|
|
</MudStack>
|
|
</MudTabPanel>
|
|
</MudTabs>
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
<!-- Right: Manual Actions & Orders (40%) -->
|
|
<MudItem xs="12" md="5">
|
|
<MudStack Spacing="3" Style="max-height: calc(100vh - 200px); overflow-y: auto;">
|
|
<MudPaper Class="pa-2" Elevation="2">
|
|
<ManualOrderPanel RobotId="@RobotId" MapId="@robot?.MapId" />
|
|
</MudPaper>
|
|
<MudPaper Class="pa-2" Elevation="2">
|
|
<ManualActionsPanel RobotId="@RobotId" />
|
|
</MudPaper>
|
|
</MudStack>
|
|
</MudItem>
|
|
</MudGrid>
|
|
}
|
|
</MudContainer>
|
|
|
|
<MudThemeProvider IsDarkMode />
|
|
<MudPopoverProvider />
|
|
<MudDialogProvider />
|
|
<MudSnackbarProvider />
|
|
|
|
@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);
|
|
}
|
|
}
|
|
}
|