From 660a29a1c1025d8ae29f13a812e2ff6af586354c Mon Sep 17 00:00:00 2001 From: Valentin Saugnier Date: Thu, 28 Mar 2024 19:50:06 +0100 Subject: [PATCH] APRS-IS beacon item --- Monitor/Monitor/Workers/AprsIsApp.cs | 57 ++++++++++++++++++-- Monitor/Monitor/appsettings.Development.json | 5 ++ Monitor/Monitor/appsettings.json | 9 +++- 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/Monitor/Monitor/Workers/AprsIsApp.cs b/Monitor/Monitor/Workers/AprsIsApp.cs index 89fae8c..6b4999d 100644 --- a/Monitor/Monitor/Workers/AprsIsApp.cs +++ b/Monitor/Monitor/Workers/AprsIsApp.cs @@ -2,6 +2,8 @@ using System.Reactive.Linq; using AprsSharp.AprsIsClient; using AprsSharp.AprsParser; +using AprsSharp.Shared; +using GeoCoordinatePortable; using Microsoft.EntityFrameworkCore; using Monitor.Context; using Monitor.Extensions; @@ -14,8 +16,11 @@ public class AprsIsApp : AEnabledWorker private readonly SerialMessageService _serialMessageService; private readonly AprsIsClient _aprsIsClient; private readonly string _callsign, _passcode, _server, _filter; + private readonly string? _objectName, _objectComment; private readonly DataContext _context; + private readonly Position? _objectPosition; private readonly TimeSpan? _durationHeard = TimeSpan.FromMinutes(30); + private readonly TcpConnection _tcpConnection = new(); public AprsIsApp(ILogger logger, IServiceProvider serviceProvider, IConfiguration configuration, IDbContextFactory contextFactory) : base(logger, serviceProvider) @@ -23,21 +28,49 @@ public class AprsIsApp : AEnabledWorker _serialMessageService = Services.GetRequiredService(); _context = contextFactory.CreateDbContext(); - _aprsIsClient = new AprsIsClient(Services.GetRequiredService>()); + _aprsIsClient = new AprsIsClient(Services.GetRequiredService>(), _tcpConnection); _aprsIsClient.ReceivedPacket += ComputeReceivedPacket; + // _aprsIsClient.ReceivedTcpMessage += message => Logger.LogTrace(message); var configurationSection = configuration.GetSection("AprsIs"); var positionSection = configuration.GetSection("Position"); + var latitude = positionSection.GetValueOrThrow("Latitude"); + var longitude = positionSection.GetValueOrThrow("Longitude"); + _callsign = configurationSection.GetValueOrThrow("Callsign"); _passcode = configurationSection.GetValueOrThrow("Passcode"); _server = configurationSection.GetValueOrThrow("Server"); - _filter = $"r/{positionSection.GetValueOrThrow("Latitude").ToString( CultureInfo.InvariantCulture)}/{positionSection.GetValueOrThrow("Longitude").ToString(CultureInfo.InvariantCulture)}/{configurationSection.GetValueOrThrow("RadiusKm")} -e/{_callsign} {configurationSection.GetValueOrThrow("Filter")}\n"; + _filter = $"r/{latitude.ToString( CultureInfo.InvariantCulture)}/{longitude.ToString(CultureInfo.InvariantCulture)}/{configurationSection.GetValueOrThrow("RadiusKm")} -e/{_callsign} {configurationSection.GetValueOrThrow("Filter")}\n"; if (configurationSection.GetValue("AlwaysTx", true)) { _durationHeard = null; } + + var beaconObjectSection = configurationSection.GetSection("BeaconObject"); + if (beaconObjectSection.GetValue("Enable", false)) + { + _objectName = beaconObjectSection.GetValueOrThrow("Name"); + _objectComment = beaconObjectSection.GetValue("Comment"); + _objectPosition = new Position(new GeoCoordinate(latitude, longitude), + beaconObjectSection.GetValueOrThrow("SymbolTable"), + beaconObjectSection.GetValueOrThrow("SymbolCode") + ); + + _aprsIsClient.ChangedState += state => + { + if (state == ConnectionState.LoggedIn) + { + SendBeaconObjectPacket(); + } + }; + + AddDisposable(Observable.Timer(TimeSpan.FromMinutes(30)).Subscribe(_ => + { + SendBeaconObjectPacket(); + })); + } } protected override Task Start() @@ -47,11 +80,18 @@ protected override Task Start() return Task.CompletedTask; } - protected override Task Stop() + protected override async Task Stop() { + if (_objectPosition != null) + { + SendBeaconObjectPacket(false); + + await Task.Delay(1500); + } + _aprsIsClient.Disconnect(); - return base.Stop(); + await base.Stop(); } private void ComputeReceivedPacket(Packet packet) @@ -97,4 +137,13 @@ private bool HasStationHeard() var lastHeard = DateTime.UtcNow - _durationHeard.Value; return _context.LoRas.Any(e => !e.IsTx && e.CreatedAt >= lastHeard); } + + private void SendBeaconObjectPacket(bool alive = true) + { + var packet = $"{_callsign}>TCPIP:){_objectName}{(alive ? '!' : '_')}{_objectPosition!.Encode()}{_objectComment} Up:{EntitiesManagerService.Entities.SystemUptime.Value}"; + + Logger.LogInformation("Send packet beacon object alive ? {alive} : {packet}", alive, packet); + + _tcpConnection.SendString(packet + "\n"); + } } \ No newline at end of file diff --git a/Monitor/Monitor/appsettings.Development.json b/Monitor/Monitor/appsettings.Development.json index 39a4090..29c25a3 100644 --- a/Monitor/Monitor/appsettings.Development.json +++ b/Monitor/Monitor/appsettings.Development.json @@ -38,5 +38,10 @@ "Skip": 0 } ] + }, + "AprsIs": { + "BeaconObject": { + "Enable": false + } } } diff --git a/Monitor/Monitor/appsettings.json b/Monitor/Monitor/appsettings.json index abc9ec5..17096dc 100644 --- a/Monitor/Monitor/appsettings.json +++ b/Monitor/Monitor/appsettings.json @@ -74,6 +74,13 @@ "Server": "france.aprs2.net", "RadiusKm": "20", "Filter": "-e/PE2KMV", - "AlwaysTx": false + "AlwaysTx": false, + "BeaconObject": { + "Enable": true, + "Name": "F4HVV Cam", + "SymbolTable": "/", + "SymbolCode": "I", + "Comment": "https://f4hvv.valentin-saugnier.fr" + } } }