using FieldLogger.Models; using FieldLogger.Services.Data; using Microsoft.Extensions.Logging; namespace FieldLogger.Services; /// /// Joins UM receiver log packets with the current GNSS fix and persists them /// under the active job. Runs for the lifetime of the app. /// public sealed class PointLogger { private readonly UmReceiverService _locator; private readonly MaglinkService _gps; private readonly AppDatabase _database; private readonly SettingsService _settings; private readonly ILogger _logger; /// Raised (on the UI thread) after a point is saved. public event EventHandler? PointSaved; /// Raised when a packet arrives but no active job is selected, so the point was dropped. public event EventHandler? PacketIgnoredNoJob; public PointLogger(UmReceiverService locator, MaglinkService gps, AppDatabase database, SettingsService settings, ILogger logger) { _locator = locator; _gps = gps; _database = database; _settings = settings; _logger = logger; _locator.PacketReceived += OnPacketReceived; } private void OnPacketReceived(object? sender, UmLogPacket packet) { _ = HandlePacketAsync(packet); } private async Task HandlePacketAsync(UmLogPacket packet) { try { var jobId = _settings.ActiveJobId; if (jobId is null) { _logger.LogWarning("Log packet received but no active job; point dropped"); MainThread.BeginInvokeOnMainThread(() => PacketIgnoredNoJob?.Invoke(this, EventArgs.Empty)); return; } var fix = _gps.FreshFix(); var point = LoggedPoint.From(jobId.Value, packet, _locator.DeviceInfo, fix, _gps.DeviceInfo); await _database.AddPointAsync(point); _logger.LogInformation("Point {Id} saved to job {JobId} (gps valid: {GpsValid})", point.Id, jobId, point.GpsValid); MainThread.BeginInvokeOnMainThread(() => PointSaved?.Invoke(this, point)); } catch (Exception ex) { _logger.LogError(ex, "Failed to save logged point"); } } }