Initial commit

This commit is contained in:
2026-07-03 16:37:12 +07:00
commit 63b8c1ea8b
1931 changed files with 640587 additions and 0 deletions

View File

@@ -0,0 +1,281 @@
@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);
}
}
}