Files
Denso/srcs/RobotNet10/RobotApp/RobotNet10.RobotApp.Client/Components/Devices/CameraQrCard.razor
2026-07-03 16:31:37 +07:00

244 lines
10 KiB
Plaintext

@implements IAsyncDisposable
@using MudBlazor
@using Microsoft.AspNetCore.SignalR.Client
@using RobotNet10.RobotApp.Client.Clients
@using RobotNet10.RobotApp.Client.Shared.Devices
@inject CameraQrHubClient CameraQrHubClient
@inject ISnackbar Snackbar
<MudCard>
<MudCardHeader>
<CardHeaderContent>
<MudText Typo="Typo.h5">@DeviceName</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">@DeviceId</MudText>
</CardHeaderContent>
<CardHeaderActions>
<MudTooltip Text="Reload Camera QR data">
<MudIconButton Icon="@Icons.Material.Filled.Refresh"
Color="Color.Primary"
Size="Size.Small"
OnClick="ReloadCameraQrDataAsync"
Disabled="@(!CameraQrHubClient.IsConnected || IsReloading)">
</MudIconButton>
</MudTooltip>
</CardHeaderActions>
</MudCardHeader>
<MudCardContent Style="position: relative;" Class="pa-2">
<MudGrid Spacing="2">
<!-- Left Column: Camera Frame Visualization -->
<MudItem xs="12" md="6">
<MudCard Elevation="0" Class="pa-2">
<MudText Typo="Typo.subtitle2" Color="Color.Primary" Class="mb-2">Camera Frame</MudText>
<div class="d-flex flex-column align-center justify-center" style="height: 100%; width: 100%; position: relative;">
<svg width="100%" height="100%" viewBox="0 0 640 480" preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg" style="max-height: 400px; border: 2px solid var(--mud-palette-divider); border-radius: 4px;">
<defs>
<!-- Gradient cho camera frame -->
<linearGradient id="@($"cameraGradient_{DeviceId}")" x1="0%" y1="0%" x2="100%" y2="100%">
<stop offset="0%" style="stop-color:#1a1a1a;stop-opacity:1" />
<stop offset="50%" style="stop-color:#2a2a2a;stop-opacity:1" />
<stop offset="100%" style="stop-color:#1a1a1a;stop-opacity:1" />
</linearGradient>
<!-- Filter cho glow effect -->
<filter id="@($"glow_{DeviceId}")">
<feGaussianBlur stdDeviation="3" result="coloredBlur" />
<feMerge>
<feMergeNode in="coloredBlur" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<!-- Camera frame background -->
<rect x="0" y="0" width="640" height="480" fill="url(@($"#cameraGradient_{DeviceId}"))" />
</svg>
</div>
</MudCard>
</MudItem>
<!-- Right Column: Data -->
<MudItem xs="12" md="6">
<MudGrid Spacing="1">
<!-- Connection Status -->
<MudItem xs="12">
<MudCard Elevation="0" Class="pa-2">
<MudGrid Spacing="1">
<MudItem xs="12" sm="6">
<MudChip T="string" Color="@(CameraQrData.IsConnected ? Color.Success : Color.Error)"
Size="Size.Small"
Variant="Variant.Filled">
@(CameraQrData.IsConnected ? "Connected" : "Disconnected")
</MudChip>
</MudItem>
</MudGrid>
</MudCard>
</MudItem>
<!-- Detection Status -->
<MudItem xs="12">
<MudCard Elevation="0" Class="pa-2">
<MudText Typo="Typo.subtitle2" Color="Color.Primary" Class="mb-1">Detection Status</MudText>
<MudGrid Spacing="1">
<MudItem xs="12">
@if (CameraQrData.Codes is { Count: > 0 })
{
<MudTable Dense="true" Hover="true" Bordered="true" Elevation="0" Items="CameraQrData.Codes">
<HeaderContent>
<MudTh>Code</MudTh>
<MudTh>X (m)</MudTh>
<MudTh>Y (m)</MudTh>
<MudTh>Z (m)</MudTh>
<MudTh>Yaw (deg)</MudTh>
<MudTh>Time</MudTh>
</HeaderContent>
<RowTemplate Context="kv">
<MudTd>@kv.Key</MudTd>
<MudTd>@kv.Value.Pose.Position.X.ToString("F3")</MudTd>
<MudTd>@kv.Value.Pose.Position.Y.ToString("F3")</MudTd>
<MudTd>@kv.Value.Pose.Position.Z.ToString("F3")</MudTd>
<MudTd>@kv.Value.Pose.Orientation.ToYawDegrees().ToString("F1")</MudTd>
<MudTd>@kv.Value.Header.Stamp.ToLocalTime().ToString("HH:mm:ss")</MudTd>
</RowTemplate>
<FooterContent>
<MudTd ColSpan="6">
Total: @CameraQrData.Codes.Count code(s)
</MudTd>
</FooterContent>
</MudTable>
}
else
{
<MudText Typo="Typo.body2" Color="Color.Secondary">
No QR detected.
</MudText>
}
</MudItem>
</MudGrid>
</MudCard>
</MudItem>
</MudGrid>
</MudItem>
</MudGrid>
</MudCardContent>
</MudCard>
<MudOverlay Visible="@IsLoading" Absolute="true">
<MudProgressCircular Indeterminate="true" Size="Size.Large" />
</MudOverlay>
@code {
[Parameter, EditorRequired]
public string DeviceId { get; set; } = string.Empty;
private string DeviceName { get; set; } = string.Empty;
private CameraQrDataDto CameraQrData = new();
private bool IsLoading => !CameraQrHubClient.IsConnected;
private bool IsReloading = false;
protected override async Task OnInitializedAsync()
{
await base.OnInitializedAsync();
}
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender && !string.IsNullOrEmpty(DeviceId))
{
await ConnectAsync();
}
await base.OnAfterRenderAsync(firstRender);
}
private async Task ConnectAsync()
{
try
{
await CameraQrHubClient.StartAsync();
// Lấy DeviceName từ CameraQrHub
var deviceInfo = await CameraQrHubClient.GetDeviceInfoAsync(DeviceId);
if (deviceInfo != null)
{
DeviceName = deviceInfo.DeviceName;
}
else
{
DeviceName = DeviceId; // Fallback to DeviceId if not found
}
CameraQrData = await CameraQrHubClient.GetCameraQrDataAsync(DeviceId);
StateHasChanged();
}
catch (Exception ex)
{
Snackbar.Add($"Failed to connect: {ex.Message}", Severity.Error);
}
}
private async Task DisconnectAsync()
{
try
{
await CameraQrHubClient.StopAsync();
CameraQrData = new CameraQrDataDto(); // Reset về giá trị mặc định
StateHasChanged();
}
catch (Exception ex)
{
Snackbar.Add($"Failed to disconnect: {ex.Message}", Severity.Error);
}
}
private async Task ReloadCameraQrDataAsync()
{
if (!CameraQrHubClient.IsConnected || IsReloading)
return;
try
{
IsReloading = true;
StateHasChanged();
CameraQrData = await CameraQrHubClient.GetCameraQrDataAsync(DeviceId);
}
catch (Exception ex)
{
Snackbar.Add($"Failed to reload Camera QR data: {ex.Message}", Severity.Error);
}
finally
{
IsReloading = false;
StateHasChanged();
}
}
private Color GetConfidenceColor(double confidence)
{
if (confidence >= 0.9)
{
return Color.Success;
}
else if (confidence >= 0.7)
{
return Color.Info;
}
else if (confidence >= 0.5)
{
return Color.Warning;
}
else
{
return Color.Error;
}
}
public async ValueTask DisposeAsync()
{
await DisconnectAsync();
}
}