using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; using FieldLogger.Models; using FieldLogger.Services; using FieldLogger.Services.Ble; namespace FieldLogger.ViewModels; [QueryProperty(nameof(KindName), "kind")] public sealed partial class DeviceScanViewModel : ObservableObject { private readonly BleScanner _scanner; private readonly DeviceConnectionManager _manager; private CancellationTokenSource? _scanCts; [ObservableProperty] private string _kindName = nameof(DeviceKind.Locator); [ObservableProperty] [NotifyPropertyChangedFor(nameof(ScanButtonText))] private bool _isScanning; [ObservableProperty] private string _title = "Select Device"; [ObservableProperty] private string _hint = ""; public ObservableCollection Devices { get; } = new(); public DeviceKind Kind => Enum.TryParse(KindName, out var k) ? k : DeviceKind.Locator; public string ScanButtonText => IsScanning ? "Scanning…" : "Scan Again"; public DeviceScanViewModel(BleScanner scanner, DeviceConnectionManager manager) { _scanner = scanner; _manager = manager; _scanner.DeviceDiscovered += OnDeviceDiscovered; } partial void OnKindNameChanged(string value) { Title = Kind == DeviceKind.Locator ? "Select Locating Receiver" : "Select RTK GPS Receiver"; Hint = Kind == DeviceKind.Locator ? "Looking for UM receivers (UMRX / DT100). Make sure Bluetooth is enabled on the receiver." : "Looking for Maglink RTK receivers. Make sure the receiver is powered on."; } [RelayCommand] public async Task ScanAsync() { if (IsScanning) return; Devices.Clear(); IsScanning = true; _scanCts = new CancellationTokenSource(); try { await _scanner.ScanAsync(TimeSpan.FromSeconds(12), _scanCts.Token); } catch (Exception ex) { await Shell.Current.DisplayAlert("Scan Failed", ex.Message, "OK"); } finally { IsScanning = false; _scanCts?.Dispose(); _scanCts = null; } } public void StopScan() => _scanCts?.Cancel(); [RelayCommand] private async Task SelectAsync(DiscoveredDevice device) { StopScan(); await _manager.UseDeviceAsync(Kind, device.Id, device.Name); await Shell.Current.GoToAsync(".."); } private void OnDeviceDiscovered(object? sender, DiscoveredDevice device) { // Filter by device name prefix based on device type. if (Kind == DeviceKind.Locator && !UmReceiverService.IsUmReceiverName(device.Name)) return; if (Kind == DeviceKind.RtkGps && !MaglinkService.IsMaglinkName(device.Name)) return; MainThread.BeginInvokeOnMainThread(() => { var existing = Devices.FirstOrDefault(d => d.Id == device.Id); if (existing is not null) Devices.Remove(existing); // Keep the list sorted by signal strength. var index = 0; while (index < Devices.Count && Devices[index].Rssi >= device.Rssi) index++; Devices.Insert(index, device); }); } }