@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 Device Diagnostics Device Diagnostics @* Loading State *@ @if (isLoading) { } @* Device Count Summary *@ @devices.Count Total Devices @devices.Count(d => d.IsConnected) Connected @devices.Count(d => d.Status == DeviceStatus.Error) Errors @devices.Count(d => d.Status == DeviceStatus.Disconnected) Disconnected @* Devices List *@ @if (filteredDevices != null && filteredDevices.Any()) { @foreach (var device in filteredDevices) { } } else if (!isLoading && (devices == null || !devices.Any())) { No devices found No devices are currently registered in the system. } else if (!isLoading) { No devices match the filter criteria } @code { private List devices = []; private List 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; } }