244 lines
10 KiB
Plaintext
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();
|
|
}
|
|
}
|
|
|