248 lines
8.7 KiB
Plaintext
248 lines
8.7 KiB
Plaintext
@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;
|
|
}
|
|
}
|