Initial commit

This commit is contained in:
2026-07-03 16:31:37 +07:00
commit 899c7c637d
1939 changed files with 641750 additions and 0 deletions

View File

@@ -0,0 +1,247 @@
@page "/devices"
@implements IAsyncDisposable
@rendermode InteractiveWebAssemblyNoPrerender
@using Microsoft.AspNetCore.SignalR.Client
@using RobotNet10.RobotApp.Client.Components.Devices
@using RobotNet10.RobotApp.Client.Services
@using RobotNet10.RobotApp.Client.Shared.Devices
@using MudBlazor
@inject DeviceHubClient DeviceHubClient
@inject ISnackbar Snackbar
<PageTitle>Device Diagnostics</PageTitle>
<MudContainer MaxWidth="MaxWidth.ExtraLarge" Class="mt-4">
<MudPaper Class="pa-4 mb-4">
<MudStack Row="true" AlignItems="@AlignItems.Center" Justify="@Justify.SpaceBetween" Spacing="3">
<MudText Typo="Typo.h5" Class="mb-4">Device Diagnostics</MudText>
<MudStack Row="true" AlignItems="@AlignItems.Center" Spacing="3">
<MudTextField @bind-Value="searchText" @bind-Value:after="OnFilterChanged" Placeholder="Search devices..." Class="flex-grow-1"
Variant="Variant.Outlined" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Search" Margin="@Margin.Dense" />
<MudIconButton Color="Color.Primary" OnClick="RefreshDevices" Disabled="@(!DeviceHubClient.IsConnected || isLoading)" Icon="@Icons.Material.Filled.Refresh" />
</MudStack>
</MudStack>
</MudPaper>
@* Loading State *@
@if (isLoading)
{
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="mb-4" />
}
@* Device Count Summary *@
<MudGrid Class="mb-4">
<MudItem xs="12" sm="6" md="3">
<MudPaper Class="pa-3 text-center">
<MudText Typo="Typo.h6">@devices.Count</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">Total Devices</MudText>
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudPaper Class="pa-3 text-center">
<MudText Typo="Typo.h6" Color="Color.Success">@devices.Count(d => d.IsConnected)</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">Connected</MudText>
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudPaper Class="pa-3 text-center">
<MudText Typo="Typo.h6" Color="Color.Error">@devices.Count(d => d.Status == DeviceStatus.Error)</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">Errors</MudText>
</MudPaper>
</MudItem>
<MudItem xs="12" sm="6" md="3">
<MudPaper Class="pa-3 text-center">
<MudText Typo="Typo.h6" Color="Color.Warning">@devices.Count(d => d.Status == DeviceStatus.Disconnected)</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">Disconnected</MudText>
</MudPaper>
</MudItem>
</MudGrid>
@* Devices List *@
@if (filteredDevices != null && filteredDevices.Any())
{
<MudGrid Spacing="2">
@foreach (var device in filteredDevices)
{
<MudItem xs="12" sm="6" md="4" lg="3" xl="2">
<DeviceCard Device="@device" />
</MudItem>
}
</MudGrid>
}
else if (!isLoading && (devices == null || !devices.Any()))
{
<MudPaper Class="pa-8 text-center">
<MudIcon Icon="@Icons.Material.Filled.Devices" Size="Size.Large" Color="Color.Secondary" Class="mb-4" />
<MudText Typo="Typo.h6">No devices found</MudText>
<MudText Typo="Typo.body2" Color="Color.Secondary">No devices are currently registered in the system.</MudText>
</MudPaper>
}
else if (!isLoading)
{
<MudPaper Class="pa-8 text-center">
<MudText Typo="Typo.h6">No devices match the filter criteria</MudText>
</MudPaper>
}
</MudContainer>
<MudThemeProvider IsDarkMode />
<MudPopoverProvider />
<MudDialogProvider />
<MudSnackbarProvider />
@code {
private List<DeviceDto> devices = [];
private List<DeviceDto> filteredDevices = [];
private bool isLoading = false;
private string searchText = string.Empty;
protected override async Task OnInitializedAsync()
{
DeviceHubClient.DeviceUpdated += OnDeviceUpdated;
DeviceHubClient.DeviceStatusChanged += OnDeviceStatusChanged;
DeviceHubClient.ConnectionStateChanged += OnConnectionStateChanged;
await DeviceHubClient.StartAsync();
await LoadDevices();
}
private async Task LoadDevices()
{
try
{
isLoading = true;
if (DeviceHubClient.IsConnected)
{
devices = (await DeviceHubClient.GetAllDevicesAsync()).ToList();
ApplyFilters();
}
}
catch (Exception ex)
{
Snackbar.Add($"Error loading devices: {ex.Message}", Severity.Error);
}
finally
{
isLoading = false;
StateHasChanged();
}
}
private async Task RefreshDevices()
{
await LoadDevices();
}
private void OnDeviceUpdated(DeviceUpdateDto update)
{
if (devices == null) return;
var device = devices.FirstOrDefault(d => d.DeviceId == update.DeviceId);
if (device != null)
{
if (update.Status.HasValue)
device.Status = update.Status.Value;
if (update.Properties != null)
device.Properties = update.Properties;
if (update.LastError != null)
device.LastError = update.LastError;
if (update.LastUpdateTime.HasValue)
device.LastUpdateTime = update.LastUpdateTime.Value;
if (update.LastConnectedTime.HasValue)
device.LastConnectedTime = update.LastConnectedTime;
if (update.LastDisconnectedTime.HasValue)
device.LastDisconnectedTime = update.LastDisconnectedTime;
if (update.ReconnectAttemptCount.HasValue)
device.ReconnectAttemptCount = update.ReconnectAttemptCount.Value;
device.IsConnected = device.Status == DeviceStatus.Connected;
ApplyFilters();
InvokeAsync(StateHasChanged);
}
}
private void OnDeviceStatusChanged(string deviceId, DeviceStatus status)
{
if (devices == null) return;
var device = devices.FirstOrDefault(d => d.DeviceId == deviceId);
if (device != null)
{
device.Status = status;
device.IsConnected = status == DeviceStatus.Connected;
ApplyFilters();
InvokeAsync(StateHasChanged);
}
}
private void OnConnectionStateChanged(HubConnectionState state)
{
InvokeAsync(StateHasChanged);
if (state == HubConnectionState.Connected)
{
_ = LoadDevices();
}
}
private void OnFilterChanged()
{
ApplyFilters();
StateHasChanged();
}
private void ApplyFilters()
{
if (devices == null)
{
filteredDevices = [];
return;
}
var query = devices.AsEnumerable();
if (!string.IsNullOrWhiteSpace(searchText))
{
var searchLower = searchText.ToLowerInvariant();
query = query.Where(d =>
d.DeviceId.ToLowerInvariant().Contains(searchLower) ||
d.DeviceName.ToLowerInvariant().Contains(searchLower) ||
(d.Description != null && d.Description.ToLowerInvariant().Contains(searchLower))
);
}
filteredDevices = query.ToList();
}
private string GetConnectionIcon()
{
return DeviceHubClient.ConnectionState switch
{
HubConnectionState.Connected => Icons.Material.Filled.CheckCircle,
HubConnectionState.Connecting => Icons.Material.Filled.HourglassEmpty,
HubConnectionState.Reconnecting => Icons.Material.Filled.Sync,
_ => Icons.Material.Filled.Error
};
}
private Color GetConnectionColor()
{
return DeviceHubClient.ConnectionState switch
{
HubConnectionState.Connected => Color.Success,
HubConnectionState.Connecting => Color.Warning,
HubConnectionState.Reconnecting => Color.Warning,
_ => Color.Error
};
}
public async ValueTask DisposeAsync()
{
DeviceHubClient.DeviceUpdated -= OnDeviceUpdated;
DeviceHubClient.DeviceStatusChanged -= OnDeviceStatusChanged;
DeviceHubClient.ConnectionStateChanged -= OnConnectionStateChanged;
}
}