106 lines
3.4 KiB
C#
106 lines
3.4 KiB
C#
using System.ComponentModel;
|
|
using CommunityToolkit.Mvvm.ComponentModel;
|
|
using FieldLogger.Models;
|
|
using FieldLogger.Services;
|
|
|
|
namespace FieldLogger.ViewModels;
|
|
|
|
/// <summary>
|
|
/// Shared, glanceable hardware status shown above each primary workflow. The timer marks
|
|
/// position data stale even when the receiver stops sending without disconnecting.
|
|
/// </summary>
|
|
public sealed partial class AppStatusViewModel : ObservableObject
|
|
{
|
|
private static readonly TimeSpan StaleAfter = TimeSpan.FromSeconds(2);
|
|
|
|
private readonly DeviceConnectionManager _manager;
|
|
private readonly MaglinkService _gps;
|
|
|
|
[ObservableProperty]
|
|
private ConnectionState _locatorState;
|
|
|
|
[ObservableProperty]
|
|
private ConnectionState _gnssState;
|
|
|
|
[ObservableProperty]
|
|
private string _locatorStatus = "Locator disconnected";
|
|
|
|
[ObservableProperty]
|
|
private string _gnssStatus = "GNSS disconnected";
|
|
|
|
[ObservableProperty]
|
|
private string _positionSummary = "No GNSS fix · accuracy —";
|
|
|
|
public AppStatusViewModel(DeviceConnectionManager manager, MaglinkService gps)
|
|
{
|
|
_manager = manager;
|
|
_gps = gps;
|
|
|
|
_manager.PropertyChanged += OnConnectionPropertyChanged;
|
|
_gps.FixReceived += OnFixReceived;
|
|
Refresh();
|
|
}
|
|
|
|
private void OnConnectionPropertyChanged(object? sender, PropertyChangedEventArgs e)
|
|
{
|
|
if (e.PropertyName is nameof(DeviceConnectionManager.LocatorState)
|
|
or nameof(DeviceConnectionManager.GpsState)
|
|
or nameof(DeviceConnectionManager.LocatorName)
|
|
or nameof(DeviceConnectionManager.GpsName))
|
|
{
|
|
Refresh();
|
|
}
|
|
}
|
|
|
|
private void OnFixReceived(object? sender, GnssFix fix) => Refresh();
|
|
|
|
/// <summary>Refreshes status safely from device callbacks or a UI dispatcher timer.</summary>
|
|
public void Refresh()
|
|
{
|
|
var dispatcher = Application.Current?.Dispatcher;
|
|
if (dispatcher?.IsDispatchRequired == true)
|
|
{
|
|
dispatcher.Dispatch(RefreshCore);
|
|
return;
|
|
}
|
|
|
|
RefreshCore();
|
|
}
|
|
|
|
private void RefreshCore()
|
|
{
|
|
LocatorState = _manager.LocatorState;
|
|
GnssState = _manager.GpsState;
|
|
LocatorStatus = DeviceLabel("Locator", _manager.LocatorName, LocatorState);
|
|
GnssStatus = DeviceLabel("GNSS", _manager.GpsName, GnssState);
|
|
|
|
var fix = _gps.LatestFix;
|
|
if (fix is null)
|
|
{
|
|
PositionSummary = GnssState == ConnectionState.Connected
|
|
? "Waiting for fix · accuracy —"
|
|
: "No GNSS fix · accuracy —";
|
|
return;
|
|
}
|
|
|
|
var stale = DateTime.UtcNow - fix.ReceivedUtc > StaleAfter;
|
|
var staleLabel = stale ? " · STALE" : string.Empty;
|
|
PositionSummary = $"{fix.StatusLabel}{staleLabel} · H ±{fix.Hrms:F3} m · " +
|
|
$"V ±{fix.Vrms:F3} m · {fix.SatellitesUsed} sats · corr {fix.CorrectionAgeSeconds:F1} s";
|
|
}
|
|
|
|
private static string DeviceLabel(string kind, string? name, ConnectionState state)
|
|
{
|
|
var stateText = state switch
|
|
{
|
|
ConnectionState.Connected => "connected",
|
|
ConnectionState.Connecting => "connecting",
|
|
_ => "disconnected",
|
|
};
|
|
|
|
return state == ConnectionState.Disconnected || string.IsNullOrWhiteSpace(name)
|
|
? $"{kind} {stateText}"
|
|
: $"{kind} {stateText} · {name}";
|
|
}
|
|
}
|