Files
BQP/srcs/RobotNet10/FleetManager/RobotNet10.FleetManager.Client/Components/RobotMonitor/SelectedRobotInfo.razor
2026-07-13 09:25:40 +07:00

201 lines
8.4 KiB
Plaintext

@using MudBlazor
@using RobotNet.VDA5050.State
@using RobotNet.VDA5050.Type
@using RobotNet10.FleetManager.Client.Components.RobotDetail
@using RobotNet10.FleetManager.Client.Components.RobotMonitor
@using RobotNet10.FleetManager.Client.Services
@implements IDisposable
<MudStack Spacing="3">
<!-- Robot Header Info -->
<MudPaper Class="pa-3" Elevation="1" Style="background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);">
<MudStack Spacing="2">
<MudText Typo="Typo.h6" Style="color: white;">
@GetRobotName()
</MudText>
<MudText Typo="Typo.body2" Style="color: rgba(255, 255, 255, 0.8);">
ID: @RobotData.RobotId
</MudText>
@if (RobotData.Model != null)
{
<MudChip T="string" Size="Size.Small" Style="background: rgba(255, 255, 255, 0.2); color: white;">
@RobotData.Model.ModelName
</MudChip>
}
@if (RobotData.LastUpdateTime != default)
{
<MudText Typo="Typo.caption" Style="color: rgba(255, 255, 255, 0.7);">
Last update: @RobotData.LastUpdateTime.ToLocalTime().ToString("HH:mm:ss")
</MudText>
}
</MudStack>
</MudPaper>
<!-- Position Info (Quick View) -->
@if (RobotData.Data != null)
{
<MudPaper Class="pa-3" Elevation="1">
<MudText Typo="Typo.subtitle2" Class="mb-2">Visualization</MudText>
<MudSimpleTable Dense="true" Elevation="0">
<tbody>
<tr>
<td><strong>X:</strong></td>
<td>@RobotData.Data.AgvPosition.X.ToString("F2") m</td>
</tr>
<tr>
<td><strong>Y:</strong></td>
<td>@RobotData.Data.AgvPosition.Y.ToString("F2") m</td>
</tr>
<tr>
<td><strong>Θ:</strong></td>
<td>@((RobotData.Data.AgvPosition.Theta * 180.0 / Math.PI).ToString("F1"))°</td>
</tr>
<tr>
<td><strong>Vx:</strong></td>
<td>@RobotData.Data.AgvVelocity.Vx.ToString("F2") m/s</td>
</tr>
<tr>
<td><strong>Vy:</strong></td>
<td>@RobotData.Data.AgvVelocity.Vy.ToString("F2") m/s</td>
</tr>
<tr>
<td><strong>Omega:</strong></td>
<td>@RobotData.Data.AgvVelocity.Omega.ToString("F2") rad/s</td>
</tr>
<tr>
<td><strong>Position Initialized:</strong></td>
<td>
<MudChip T="string"
Size="Size.Small"
Color="@(RobotData.Data.AgvPosition.PositionInitialized ? Color.Success : Color.Warning)">
@(RobotData.Data.AgvPosition.PositionInitialized ? "Yes" : "No")
</MudChip>
</td>
</tr>
@if (RobotData.Data.AgvPosition.LocalizationScore >= 0)
{
<tr>
<td><strong>Localization Score:</strong></td>
<td>@RobotData.Data.AgvPosition.LocalizationScore.ToString("F2")</td>
</tr>
}
@if (RobotData.Data.AgvPosition.DeviationRange >= 0)
{
<tr>
<td><strong>Deviation Range:</strong></td>
<td>@RobotData.Data.AgvPosition.DeviationRange.ToString("F2") m</td>
</tr>
}
</tbody>
</MudSimpleTable>
</MudPaper>
}
<!-- Expansion Panels for Detailed Info -->
<MudExpansionPanels Elevation="0" MultiExpansion="true" Gutters="false">
<!-- Battery State Panel -->
<MudExpansionPanel Icon="@Icons.Material.Filled.BatteryChargingFull"
Expanded="true">
<TitleContent>
<MudText>Battery State</MudText>
</TitleContent>
<ChildContent>
<BatteryCard @ref="BatteryCardRef" ShowNameCard="false"/>
</ChildContent>
</MudExpansionPanel>
<!-- Errors Panel -->
<MudExpansionPanel Icon="@Icons.Material.Filled.Error"
Expanded="false">
<TitleContent>
<div class="d-flex">
<MudText>Errors</MudText>
<MudBadge Content="Errors.Length" Color="Color.Info" Overlap="true" Class="d-flex ml-auto">
<MudIcon Icon="@Icons.Material.Filled.Info" Color="Color.Secondary" />
</MudBadge>
</div>
</TitleContent>
<ChildContent>
<MudPaper Class="pa-4" Elevation="2">
@foreach (var error in Errors)
{
<MudTooltip Text="@error.ErrorDescription" Placement="Placement.Top" Color="Color.Info">
<MudButton Class="m-2" Color="@(error.ErrorLevel == ErrorLevel.FATAL ? Color.Error : Color.Warning)" Variant="Variant.Filled" Size="Size.Small" Style="text-transform:none">@error.ErrorType</MudButton>
</MudTooltip>
}
</MudPaper>
</ChildContent>
</MudExpansionPanel>
<!-- Information Panel -->
<MudExpansionPanel Icon="@Icons.Material.Filled.Info"
Expanded="false">
<TitleContent>
<div class="d-flex">
<MudText>Notification</MudText>
<MudBadge Content="Information.Length" Color="Color.Info" Overlap="true" Class="d-flex ml-auto">
<MudIcon Icon="@Icons.Material.Filled.Notifications" Color="Color.Warning" />
</MudBadge>
</div>
</TitleContent>
<ChildContent>
<MudPaper Class="pa-4" Elevation="2">
<div class="d-flex flex-column">
@foreach (var info in Information)
{
<MudTooltip Text="@info.InfoDescription" Placement="Placement.Top" Color="Color.Info">
<MudButton Class="m-2" Color="@(info.InfoLevel == InfoLevel.INFO ? Color.Info : Color.Default)" Variant="Variant.Filled" Size="Size.Small" Style="text-transform:none">@info.InfoType</MudButton>
</MudTooltip>
}
</div>
</MudPaper>
</ChildContent>
</MudExpansionPanel>
</MudExpansionPanels>
</MudStack>
@code {
[Parameter]
public RobotMonitorData RobotData { get; set; } = null!;
[Parameter]
public RobotMonitorState State { get; set; } = null!;
private BatteryCard BatteryCardRef = default!;
private Error[] Errors = [];
private Information[] Information = [];
protected override async Task OnAfterRenderAsync(bool firstRender)
{
await base.OnAfterRenderAsync(firstRender);
if (!firstRender) return;
State.OnDataChanged += OnDataChanged;
}
private void OnDataChanged()
{
if (State.SelectedRobotId != null && State.Robots.TryGetValue(State.SelectedRobotId, out var robot))
{
BatteryCardRef.Update(robot.Data?.Battery);
Errors = robot.Data?.Errors ?? [];
Information = robot.Data?.Infomations ?? [];
RobotData = robot;
StateHasChanged();
}
}
public void Dispose()
{
State.OnDataChanged -= OnDataChanged;
}
private string GetRobotName()
{
// Try to get robot name from AvailableRobots
var robot = State.AvailableRobots.FirstOrDefault(r => r.RobotId == RobotData.RobotId);
return robot?.Name ?? RobotData.RobotId;
}
}