397 lines
18 KiB
Plaintext
397 lines
18 KiB
Plaintext
@page "/robot-state"
|
|
@using RobotApp.VDA5050.State
|
|
@rendermode InteractiveWebAssemblyNoPrerender
|
|
|
|
@inject StateMsg RobotState
|
|
|
|
<MudContainer MaxWidth="MaxWidth.False" Class="pa-4">
|
|
<!-- ===================================================== -->
|
|
<!-- HEADER -->
|
|
<!-- ===================================================== -->
|
|
<MudPaper Class="pa-6 mb-4 d-flex align-center justify-space-between" Elevation="3">
|
|
<div>
|
|
<MudText Typo="Typo.h4">🤖 VDA 5050 Robot Dashboard</MudText>
|
|
<MudText Typo="Typo.subtitle2" Color="Color.Secondary">
|
|
@RobotState.Version •
|
|
@RobotState.Manufacturer
|
|
@RobotState.SerialNumber
|
|
</MudText>
|
|
</div>
|
|
@* @if (RobotState.Current != null)
|
|
{
|
|
<MudChip T="string" Size="Size.Large"
|
|
Color="@(RobotState.Current ? Color.Success : Color.Error)">
|
|
@(RobotState.Current.IsOnline ? "ONLINE" : "OFFLINE")
|
|
</MudChip>
|
|
} *@
|
|
</MudPaper>
|
|
|
|
@if (RobotState == null)
|
|
{
|
|
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined">
|
|
Waiting for robot state (VDA5050)...
|
|
<MudProgressLinear Indeterminate Class="mt-3" />
|
|
</MudAlert>
|
|
}
|
|
else
|
|
{
|
|
var msg = RobotState;
|
|
|
|
<!-- ===================================================== -->
|
|
<!-- 📨 MESSAGE META (DEBUG / TRACE) -->
|
|
<!-- ===================================================== -->
|
|
<MudPaper Class="pa-3 mb-4" Elevation="1">
|
|
<MudGrid Spacing="2">
|
|
<MudItem xs="12" md="3">
|
|
<MudText Typo="Typo.caption">HeaderId</MudText>
|
|
<MudText>@msg.HeaderId</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12" md="5">
|
|
<MudText Typo="Typo.caption">Timestamp (UTC)</MudText>
|
|
<MudText>@msg.Timestamp</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12" md="2">
|
|
<MudText Typo="Typo.caption">Version</MudText>
|
|
<MudText>@msg.Version</MudText>
|
|
</MudItem>
|
|
<MudItem xs="12" md="2">
|
|
<MudText Typo="Typo.caption">OrderUpdateId</MudText>
|
|
<MudChip T="string" Color="Color.Primary">
|
|
@msg.OrderUpdateId
|
|
</MudChip>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudPaper>
|
|
|
|
<!-- ===================================================== -->
|
|
<!-- MAIN GRID -->
|
|
<!-- ===================================================== -->
|
|
<MudGrid Spacing="4">
|
|
<!-- POSITION + VELOCITY -->
|
|
<MudItem xs="12" md="6" lg="4">
|
|
<MudPaper Class="pa-5 h-100" Elevation="2">
|
|
<MudGrid AlignItems="AlignItems.Center">
|
|
<MudItem xs="8">
|
|
<MudText Typo="Typo.h6">📍 Position & Velocity</MudText>
|
|
</MudItem>
|
|
<MudItem xs="4" Class="d-flex justify-end">
|
|
<MudChip T="string" Size="Size.Small"
|
|
Color="@(msg.NewBaseRequest ? Color.Success : Color.Error)">
|
|
NewBase: @(msg.NewBaseRequest ? "TRUE" : "FALSE")
|
|
</MudChip>
|
|
</MudItem>
|
|
</MudGrid>
|
|
<MudDivider Class="my-3" />
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="6">
|
|
<MudText>X: <b>@msg.AgvPosition.X.ToString("F2")</b> m</MudText>
|
|
<MudText>Y: <b>@msg.AgvPosition.Y.ToString("F2")</b> m</MudText>
|
|
<MudText>θ: <b>@msg.AgvPosition.Theta.ToString("F2")</b> rad</MudText>
|
|
</MudItem>
|
|
<MudItem xs="6">
|
|
<MudText>Vx: <b>@msg.Velocity.Vx.ToString("F2")</b> m/s</MudText>
|
|
<MudText>Vy: <b>@msg.Velocity.Vy.ToString("F2")</b> m/s</MudText>
|
|
<MudText>Ω: <b>@msg.Velocity.Omega.ToString("F3")</b> rad/s</MudText>
|
|
</MudItem>
|
|
</MudGrid>
|
|
<MudDivider Class="my-3" />
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="6">
|
|
<MudChip T="string" Size="Size.Small"
|
|
Color="@(msg.AgvPosition.PositionInitialized ? Color.Success : Color.Error)">
|
|
Initialized: @(msg.AgvPosition.PositionInitialized ? "TRUE" : "FALSE")
|
|
</MudChip>
|
|
</MudItem>
|
|
<MudItem xs="6" Class="d-flex justify-end">
|
|
<MudText Typo="Typo.caption">
|
|
DeviationRange: <b>@msg.AgvPosition.DeviationRange</b>
|
|
</MudText>
|
|
</MudItem>
|
|
</MudGrid>
|
|
<MudProgressLinear Value="@(msg.AgvPosition.LocalizationScore * 100)"
|
|
Class="mt-3"
|
|
Color="Color.Success" />
|
|
<MudText Typo="Typo.caption">
|
|
Localization Score: @(msg.AgvPosition.LocalizationScore * 100) %
|
|
</MudText>
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
<!-- BATTERY -->
|
|
<MudItem xs="12" md="6" lg="4">
|
|
<MudPaper Class="pa-5 h-100" Elevation="2">
|
|
<MudText Typo="Typo.h6">🔋 Battery</MudText>
|
|
<MudDivider Class="my-3" />
|
|
<MudProgressLinear Value="@msg.BatteryState.BatteryCharge"
|
|
Size="Size.Large"
|
|
Rounded
|
|
Color="@(msg.BatteryState.BatteryCharge > 50 ? Color.Success :
|
|
msg.BatteryState.BatteryCharge > 20 ? Color.Warning : Color.Error)" />
|
|
<MudText Typo="Typo.h4" Class="mt-2">
|
|
@msg.BatteryState.BatteryCharge:F1 %
|
|
</MudText>
|
|
<MudChip T="string" Size="Size.Small"
|
|
Color="@(msg.BatteryState.Charging ? Color.Info : Color.Default)">
|
|
@(msg.BatteryState.Charging ? "⚡ Charging" : "Discharging")
|
|
</MudChip>
|
|
<MudDivider Class="my-3" />
|
|
<MudGrid Spacing="1">
|
|
<MudItem xs="4">
|
|
<MudText Typo="Typo.caption">Voltage</MudText>
|
|
<MudText><b>@msg.BatteryState.BatteryVoltage.ToString("F1")</b> V</MudText>
|
|
</MudItem>
|
|
<MudItem xs="4">
|
|
<MudText Typo="Typo.caption">Health</MudText>
|
|
<MudText><b>@msg.BatteryState.BatteryHealth</b> %</MudText>
|
|
</MudItem>
|
|
<MudItem xs="4">
|
|
<MudText Typo="Typo.caption">Reach</MudText>
|
|
<MudText><b>@((int)msg.BatteryState.Reach)</b> m</MudText>
|
|
</MudItem>
|
|
</MudGrid>
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
<!-- ORDER & PATH -->
|
|
<MudItem xs="12" md="6" lg="4">
|
|
<MudPaper Class="pa-5 h-100" Elevation="2">
|
|
<MudText Typo="Typo.h6">🧭 Order & Path</MudText>
|
|
<MudDivider Class="my-3" />
|
|
<MudText>Order ID: <b>@(msg.OrderId ?? "—")</b></MudText>
|
|
<MudText>Update ID: <b>@msg.OrderUpdateId</b></MudText>
|
|
<MudDivider Class="my-2" />
|
|
<MudText>
|
|
Last Node: <b>@msg.LastNodeId</b>
|
|
<MudText Typo="Typo.caption" Inline="true">Seq: @msg.LastNodeSequenceId</MudText>
|
|
</MudText>
|
|
<MudText>Distance: @msg.DistanceSinceLastNode:F1 m</MudText>
|
|
<MudDivider Class="my-3" />
|
|
|
|
@{
|
|
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;
|
|
}
|
|
|
|
<div class="d-flex align-center flex-wrap gap-2">
|
|
<MudChip T="string" Color="Color.Info">Nodes: @nodeReleased / @nodeTotal</MudChip>
|
|
<MudChip T="string" Color="Color.Info">Edges: @edgeReleased / @edgeTotal</MudChip>
|
|
<MudChip T="string" Size="Size.Small" Color="@(msg.Driving ? Color.Success : Color.Default)">
|
|
@(msg.Driving ? "DRIVING" : "STOPPED")
|
|
</MudChip>
|
|
<MudChip T="string" Size="Size.Small" Color="@(msg.Paused ? Color.Warning : Color.Success)">
|
|
@(msg.Paused ? "PAUSED" : "ACTIVE")
|
|
</MudChip>
|
|
</div>
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
<!-- ERRORS + INFORMATION -->
|
|
<MudItem xs="12" md="12" lg="6">
|
|
<MudPaper Class="pa-5 h-100" Elevation="2">
|
|
|
|
<MudText Typo="Typo.h6">🚨 Errors & Information</MudText>
|
|
<MudDivider Class="my-3" />
|
|
|
|
@{
|
|
var rows = new List<MessageRow>();
|
|
|
|
if (msg.Errors != null)
|
|
{
|
|
foreach (var err in msg.Errors)
|
|
{
|
|
rows.Add(new MessageRow(
|
|
err.ErrorType ?? "-",
|
|
err.ErrorLevel ?? "-",
|
|
err.ErrorDescription ?? "",
|
|
true
|
|
));
|
|
}
|
|
}
|
|
|
|
if (msg.Information != null)
|
|
{
|
|
foreach (var info in msg.Information)
|
|
{
|
|
rows.Add(new MessageRow(
|
|
info.InfoType ?? "-",
|
|
info.InfoLevel ?? "-",
|
|
info.InfoDescription ?? "",
|
|
false
|
|
));
|
|
}
|
|
}
|
|
|
|
var sortedMessages = rows
|
|
.OrderBy(r => r.IsError ? 0 : 1) // Errors trước
|
|
.ThenBy(r => r.Type)
|
|
.ToList();
|
|
}
|
|
|
|
<MudTable Items="@sortedMessages"
|
|
Dense="true"
|
|
Hover="true"
|
|
Bordered="true"
|
|
Elevation="0"
|
|
Height="180px"
|
|
Breakpoint="Breakpoint.Sm"
|
|
HorizontalScrollbar="true">
|
|
|
|
<ColGroup>
|
|
<col style="width: 35%" />
|
|
<col style="width: 20%" />
|
|
<col />
|
|
</ColGroup>
|
|
|
|
<HeaderContent>
|
|
<MudTh>Type</MudTh>
|
|
<MudTh>Level</MudTh>
|
|
<MudTh>Description</MudTh>
|
|
</HeaderContent>
|
|
|
|
<RowTemplate>
|
|
<MudTd DataLabel="Type">
|
|
<MudText Class="@(context.IsError ? "text-error" : "text-info")">
|
|
<b>@context.Type</b>
|
|
</MudText>
|
|
</MudTd>
|
|
|
|
<MudTd DataLabel="Level">
|
|
<MudChip T="string"
|
|
Size="Size.Small"
|
|
Color="@(context.Level == "ERROR"
|
|
? Color.Error
|
|
: context.Level == "WARNING"
|
|
? Color.Warning
|
|
: Color.Info)">
|
|
@context.Level
|
|
</MudChip>
|
|
</MudTd>
|
|
|
|
<MudTd DataLabel="Description">
|
|
<MudText Typo="Typo.caption"
|
|
Class="text-truncate"
|
|
Title="@context.Description">
|
|
@context.Description
|
|
</MudText>
|
|
</MudTd>
|
|
</RowTemplate>
|
|
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.caption"
|
|
Color="Color.Secondary"
|
|
Class="pa-4 text-center">
|
|
No errors or information messages
|
|
</MudText>
|
|
</NoRecordsContent>
|
|
|
|
</MudTable>
|
|
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
<!-- ACTIONS -->
|
|
<MudItem xs="12" md="6" lg="3">
|
|
<MudPaper Class="pa-5 h-100" Elevation="2">
|
|
|
|
<MudText Typo="Typo.h6">⚙️ Actions</MudText>
|
|
<MudDivider Class="my-3" />
|
|
|
|
<MudTable Items="msg.ActionStates"
|
|
Dense="true"
|
|
Hover="true"
|
|
Bordered="true"
|
|
Elevation="0"
|
|
Height="160px"
|
|
Breakpoint="Breakpoint.Sm"
|
|
HorizontalScrollbar="true"
|
|
FixedHeader="true">
|
|
|
|
<ColGroup>
|
|
<col style="width: 40%" />
|
|
<col style="width: 35%" />
|
|
<col style="width: 25%" />
|
|
</ColGroup>
|
|
|
|
<HeaderContent>
|
|
<MudTh>Action</MudTh>
|
|
<MudTh>Action ID</MudTh>
|
|
<MudTh class="text-right">Status</MudTh>
|
|
</HeaderContent>
|
|
|
|
<RowTemplate>
|
|
<MudTd DataLabel="Action">
|
|
<MudText Typo="Typo.body2"
|
|
Class="text-truncate"
|
|
Title="@context.ActionType">
|
|
@context.ActionType
|
|
</MudText>
|
|
</MudTd>
|
|
|
|
<MudTd DataLabel="Action ID">
|
|
<MudText Typo="Typo.caption">
|
|
@context.ActionId
|
|
</MudText>
|
|
</MudTd>
|
|
|
|
<MudTd DataLabel="Status" Class="text-right">
|
|
<MudChip T="string"
|
|
Size="Size.Small"
|
|
Variant="Variant.Filled"
|
|
Color="@(context.ActionStatus == "RUNNING"
|
|
? Color.Info
|
|
: context.ActionStatus == "FINISHED"
|
|
? Color.Success
|
|
: Color.Error)">
|
|
@context.ActionStatus
|
|
</MudChip>
|
|
</MudTd>
|
|
</RowTemplate>
|
|
|
|
<NoRecordsContent>
|
|
<MudText Typo="Typo.caption"
|
|
Color="Color.Secondary"
|
|
Class="pa-4 text-center">
|
|
No active actions
|
|
</MudText>
|
|
</NoRecordsContent>
|
|
|
|
</MudTable>
|
|
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
<!-- SAFETY -->
|
|
<MudItem xs="12" md="6" lg="3">
|
|
<MudPaper Class="pa-5 h-100" Elevation="2">
|
|
<MudText Typo="Typo.h6">🛑 Safety</MudText>
|
|
<MudDivider Class="my-3" />
|
|
<MudChip T="string" Size="Size.Large" Class="w-100 mb-2"
|
|
Color="@(msg.SafetyState.EStop == "NONE" ? Color.Success : Color.Error)">
|
|
E-STOP: @msg.SafetyState.EStop
|
|
</MudChip>
|
|
<MudChip T="string" Size="Size.Large" Class="w-100"
|
|
Color="@(msg.SafetyState.FieldViolation ? Color.Error : Color.Success)">
|
|
Field Violation: @(msg.SafetyState.FieldViolation ? "YES" : "NO")
|
|
</MudChip>
|
|
</MudPaper>
|
|
</MudItem>
|
|
|
|
</MudGrid>
|
|
}
|
|
</MudContainer>
|
|
|
|
@code {
|
|
|
|
private async Task OnStateChanged()
|
|
=> await InvokeAsync(StateHasChanged);
|
|
|
|
|
|
private record MessageRow(
|
|
string Type,
|
|
string Level,
|
|
string Description,
|
|
bool IsError
|
|
);
|
|
} |