From 5df304d0cf53ae39141afd7759986a0032831b51 Mon Sep 17 00:00:00 2001 From: joelp Date: Sun, 10 Nov 2024 10:15:53 -0800 Subject: [PATCH] Dotnet 8 updates. Use JSON serializer for payload Note on FreeBSD .NET 8 support. --- README.md | 4 +- app/Dump1090Reader.cs | 105 +++++++++++++++------------------------- app/FindAircraftType.cs | 41 ++++++---------- app/Flight.cs | 3 +- app/FlightPayload.cs | 23 +++++++++ app/Worker.cs | 15 ++---- 6 files changed, 84 insertions(+), 107 deletions(-) create mode 100644 app/FlightPayload.cs diff --git a/README.md b/README.md index 75f8a8c..c56f31e 100644 --- a/README.md +++ b/README.md @@ -54,4 +54,6 @@ You don't need to set LATITUDE and LONGITUDE to your precise location (unlike wi I replaced an aging RPi3 running [piaware](https://github.com/flightaware/piaware) with a 4gb Rock64 running dump1090-fa. Here's a repo for getting this to run: [rtl-sdr-bsd](https://github.com/idatum/rtl-sdr-bsd). Here are more details of the overall project: [Experimenting with RTL-SDR on NetBSD 10](https://www.idatum.net/experimenting-with-rtl-sdr-on-netbsd-10.html). ## FreeBSD 14 on the Rock64 running [dump1090-fa](https://github.com/flightaware/dump1090) -I've since switched from NetBSD 10 to running FreeBSD 14 on the Rock64 device and this resulted in a more stable host for dump1090. It's mainly about better support for RTL-SDR generally with USB on FreeBSD. Regardless of your O/S choice, adsb2mqtt should be able to connect to dump1090 and generate MQTT messages. +I've since switched from NetBSD 10 to running FreeBSD 14 on the Rock64 device and this resulted in a more stable host for dump1090. It's mainly about better support for RTL-SDR generally with USB on FreeBSD. FreeBSD 14 also now supports .NET 8. + +Regardless of your O/S choice, adsb2mqtt should be able to connect to dump1090 and generate MQTT messages. diff --git a/app/Dump1090Reader.cs b/app/Dump1090Reader.cs index f5b1725..10da791 100644 --- a/app/Dump1090Reader.cs +++ b/app/Dump1090Reader.cs @@ -9,6 +9,7 @@ namespace adsb2mqtt; using System.Threading.Tasks; using System.Timers; using Microsoft.Extensions.Configuration; +using System.Text.Json; using MQTTnet; using MQTTnet.Client; @@ -36,18 +37,9 @@ public Dump1090Reader(ILogger logger, IConfiguration configuration, IFindAircraftType findAircraftType) { - if (logger is null) - { - throw new ArgumentNullException("logger"); - } - if (configuration is null) - { - throw new ArgumentNullException("configuration"); - } - if (findAircraftType is null) - { - throw new ArgumentNullException("findAircraftType"); - } + ArgumentNullException.ThrowIfNull(logger); + ArgumentNullException.ThrowIfNull(configuration); + ArgumentNullException.ThrowIfNull(findAircraftType); _logger = logger; _configuration = configuration; _findAircraftType = findAircraftType; @@ -79,10 +71,10 @@ private void GroomFlightsCallback(object? source, ElapsedEventArgs e) foreach (var icao in staleFlights) { - Flight staleFlight; - if (!_icaoFlight.TryRemove(icao, out staleFlight)) + if (!_icaoFlight.TryRemove(icao, out Flight staleFlight)) { - _logger.LogInformation($"Unable to drop stale flight {staleFlight.Icao} last seen {staleFlight.LogDateTime} UTC"); + _logger.LogInformation("Unable to drop stale flight {staleFlight.Icao} last seen {staleFlight.LogDateTime} UTC", + staleFlight.Icao, staleFlight.LogDateTime); } } } @@ -105,7 +97,7 @@ private bool MqttConnected() // Loop through the AddressList to obtain the supported AddressFamily. foreach (IPAddress address in hostEntry.AddressList) { - IPEndPoint ipe = new IPEndPoint(address, port); + IPEndPoint ipe = new(address, port); var tempSocket = new Socket(ipe.AddressFamily, SocketType.Stream, ProtocolType.Tcp); @@ -133,11 +125,11 @@ private async void PublishRecordsCallback(object? source, ElapsedEventArgs e) } catch (MqttClientDisconnectedException ex) { - _logger.LogError(ex.ToString()); + _logger.LogError("{ex}", ex); } catch (Exception ex) { - _logger.LogError(ex.ToString()); + _logger.LogError("{ex}", ex); throw; } } @@ -153,26 +145,35 @@ private async Task PublishRecordsAsync() _logger.LogDebug("MQTT not connected; skipping publish."); return; } - Flight flight; foreach (var icao in _trackedIcaoFlight.Keys) { - if (!_trackedIcaoFlight.TryRemove(icao, out flight)) + if (!_trackedIcaoFlight.TryRemove(icao, out Flight flight)) { continue; } var nm = GetNauticalMiles(double.Parse(flight.Latitude), double.Parse(flight.Longitude)); if (nm < _radiusNauticalMiles) { - var payloadText = $"{{ \"icao\":\"{flight.Icao}\",\"flt\":\"{flight.Callsign}\"," + - $"\"alt\":{flight.Altitude},\"dir\":{flight.Direction},\"lat\":{flight.Latitude}," + - $"\"lng\":{flight.Longitude},\"t\":\"{flight.AircraftType}\",\"nm\":{nm} }}"; + var flightPayload = new FlightPayload + { + icao = flight.Icao, + flt = flight.Callsign, + alt = int.Parse(flight.Altitude), + dir = int.Parse(flight.Direction), + lat = double.Parse(flight.Latitude), + lng = double.Parse(flight.Longitude), + t = flight.AircraftType ?? string.Empty, + nm = nm + }; + string payloadText = JsonSerializer.Serialize(flightPayload); var payload = Encoding.UTF8.GetBytes(payloadText); + _logger.LogDebug("{payloadText}", payloadText); var message = new MqttApplicationMessageBuilder() .WithTopic($"{_topicBase}/{icao}") .WithPayload(payload) .WithQualityOfServiceLevel(MqttQualityOfServiceLevel .AtLeastOnce).Build(); - var pubResult = await _mqttClient.PublishAsync(message); + await _mqttClient.PublishAsync(message); } } } @@ -244,16 +245,14 @@ private void HandleRecord(String record) string[] recordSplit = record.Split(','); if (recordSplit.Length < 5) { - _logger.LogInformation($"Invalid record: {record}"); + _logger.LogInformation("Invalid record: {record}", record); return; } var msgType = recordSplit[0]; var transType = recordSplit[1]; var icao = recordSplit[4]; - Flight lastFlight; - _icaoFlight.TryGetValue(icao, out lastFlight); - Flight flight; - UpdateFlight(icao, recordSplit, lastFlight, out flight); + _icaoFlight.TryGetValue(icao, out Flight lastFlight); + UpdateFlight(icao, recordSplit, lastFlight, out Flight flight); _icaoFlight.AddOrUpdate(icao, flight, (key, val) => flight); if (flight.Complete) { @@ -263,11 +262,7 @@ private void HandleRecord(String record) private void ReceiveDump1090(string server, int port, CancellationToken stoppingToken) { - using var socket = ConnectSocket(server, port); - if (socket == null) - { - throw new ApplicationException("Socket connection failed"); - } + using var socket = ConnectSocket(server, port) ?? throw new ApplicationException("Socket connection failed"); var readBuffer = new Byte[1024]; int bytesRead = 0; var recordBytes = new List(); @@ -316,33 +311,13 @@ public async Task ProcessAsync(CancellationToken stoppingToken) _baseLatitudeRad = _configuration.GetValue("LATITUDE") * Math.PI / 180.0; _baseLongitudeRad = _configuration.GetValue("LONGITUDE") * Math.PI / 180.0; // TCP BaseStation output host and port. - var beastHost = _configuration["BEAST_HOST"]; - if (beastHost is null) - { - throw new InvalidOperationException("BEAST_HOST"); - } + var beastHost = _configuration["BEAST_HOST"] ?? throw new InvalidOperationException("BEAST_HOST"); int beastPort = _configuration.GetValue("BEAST_PORT"); // MQTT - _topicBase = _configuration["TOPIC_BASE"]; - if (_topicBase is null) - { - throw new InvalidOperationException("_topicBase"); - } - var mqttUsername = _configuration["MQTT_USERNAME"]; - if (mqttUsername is null) - { - throw new InvalidOperationException("MQTT_USERNAME"); - } - var mqttPassword = _configuration["MQTT_PASSWORD"]; - if (mqttPassword is null) - { - throw new InvalidOperationException("MQTT_USERNAME"); - } - var mqttServer = _configuration["MQTT_SERVER"]; - if (mqttServer is null) - { - throw new InvalidOperationException("MQTT_SERVER"); - } + _topicBase = _configuration["TOPIC_BASE"] ?? throw new InvalidOperationException("_topicBase"); + var mqttUsername = _configuration["MQTT_USERNAME"] ?? throw new InvalidOperationException("MQTT_USERNAME"); + var mqttPassword = _configuration["MQTT_PASSWORD"] ?? throw new InvalidOperationException("MQTT_USERNAME"); + var mqttServer = _configuration["MQTT_SERVER"] ?? throw new InvalidOperationException("MQTT_SERVER"); int mqttPort = _configuration.GetValue("MQTT_PORT"); bool mqttUseTls = _configuration.GetValue("MQTT_USE_TLS"); @@ -365,25 +340,25 @@ public async Task ProcessAsync(CancellationToken stoppingToken) if (!await ConnectMqtt(mqttUsername, mqttPassword, mqttServer, mqttPort, mqttUseTls)) { _logger.LogInformation("Could not connect to MQTT"); - await Task.Delay(5000); + await Task.Delay(5000, stoppingToken); continue; } ReceiveDump1090(beastHost, beastPort, stoppingToken); } catch (SocketException ex) { - _logger.LogWarning(ex.Message); - await Task.Delay(5000); + _logger.LogWarning("{ex.Message}", ex.Message); + await Task.Delay(5000, stoppingToken); } catch (MqttCommunicationException ex) { - _logger.LogError(ex.ToString()); + _logger.LogError("{ex}", ex); await DisconnectMqtt(); - await Task.Delay(5000); + await Task.Delay(5000, stoppingToken); } catch (Exception ex) { - _logger.LogError(ex.ToString()); + _logger.LogError("{ex}", ex); await DisconnectMqtt(); throw; } diff --git a/app/FindAircraftType.cs b/app/FindAircraftType.cs index 8464801..9979619 100644 --- a/app/FindAircraftType.cs +++ b/app/FindAircraftType.cs @@ -1,32 +1,21 @@ namespace adsb2mqtt; - using System.Text.Json; public class FindAircraftType : IFindAircraftType { private readonly ILogger _logger; private readonly IConfiguration _configuration; - private string? _aircraftDbPath; + private readonly string? _aircraftDbPath; public FindAircraftType(ILogger logger, IConfiguration configuration) { - if (logger is null) - { - throw new ArgumentNullException(nameof(logger)); - } - if (configuration is null) - { - throw new ArgumentNullException(nameof(configuration)); - } + ArgumentNullException.ThrowIfNull(logger); + ArgumentNullException.ThrowIfNull(configuration); _logger = logger; _configuration = configuration; // dump1090 aircraft database path. - _aircraftDbPath = _configuration.GetValue("AIRCRAFT_DB_PATH"); - if (_aircraftDbPath is null) - { - throw new InvalidOperationException("AIRCRAFT_DB_PATH"); - } + _aircraftDbPath = _configuration.GetValue("AIRCRAFT_DB_PATH") ?? throw new InvalidOperationException("AIRCRAFT_DB_PATH"); } public string? Find(string icao) @@ -38,39 +27,37 @@ public FindAircraftType(ILogger logger, JsonDocument jsonDoc; try { - var filename = $"{_aircraftDbPath}/{icao.Substring(0, i)}.json"; + var filename = $"{_aircraftDbPath}/{icao[..i]}.json"; if (!File.Exists(filename)) { continue; } - _logger.LogDebug($"Reading {filename}."); + _logger.LogDebug("Reading {filename}.", filename); jsonText = File.ReadAllText(filename); jsonDoc = JsonDocument.Parse(jsonText); - JsonElement icaoElement; - if (!jsonDoc.RootElement.TryGetProperty(icao.Substring(i), out icaoElement)) + if (!jsonDoc.RootElement.TryGetProperty(icao.Substring(i), out JsonElement icaoElement)) { - _logger.LogDebug($"No property {icao.Substring(i)}."); + _logger.LogDebug("No property {icao[i..]}.", icao[i..]); continue; } - _logger.LogDebug($"Found icao element {icao.Substring(i)} in {filename}."); - JsonElement aircraftTypeElement; - if (!icaoElement.TryGetProperty("t", out aircraftTypeElement)) + _logger.LogDebug("Found icao element {icao[..i]} in {filename}.", icao[i..], filename); + if (!icaoElement.TryGetProperty("t", out JsonElement aircraftTypeElement)) { _logger.LogDebug("No property type property."); continue; } var aircraftType = aircraftTypeElement.GetString(); - _logger.LogDebug($"Aircraft type: {aircraftType}"); + _logger.LogDebug("Aircraft type: {aircraftType}", aircraftType); return aircraftType; } catch (Exception ex) { - _logger.LogError(ex.ToString()); + _logger.LogError("{ex}", ex); continue; } } - _logger.LogDebug($"No aircraft info for {icao}."); + _logger.LogDebug("No aircraft info for {icao}.", icao); return string.Empty; } -} \ No newline at end of file +} diff --git a/app/Flight.cs b/app/Flight.cs index 7cff425..2639155 100644 --- a/app/Flight.cs +++ b/app/Flight.cs @@ -1,5 +1,4 @@ namespace adsb2mqtt; - using System; public struct Flight @@ -9,7 +8,7 @@ public struct Flight public string Icao; public string Callsign { - get { return string.IsNullOrEmpty(callSign) ? Icao : callSign; } + readonly get { return string.IsNullOrEmpty(callSign) ? Icao : callSign; } set { callSign = value; } } public string Altitude; diff --git a/app/FlightPayload.cs b/app/FlightPayload.cs new file mode 100644 index 0000000..a4b30ec --- /dev/null +++ b/app/FlightPayload.cs @@ -0,0 +1,23 @@ +using System.Text.Json.Serialization; + +namespace adsb2mqtt; + +public struct FlightPayload +{ + [JsonInclude] + public string icao; + [JsonInclude] + public string flt; + [JsonInclude] + public int alt; + [JsonInclude] + public int dir; + [JsonInclude] + public double lat; + [JsonInclude] + public double lng; + [JsonInclude] + public string t; + [JsonInclude] + public double nm; +} \ No newline at end of file diff --git a/app/Worker.cs b/app/Worker.cs index 9374cc1..019080e 100644 --- a/app/Worker.cs +++ b/app/Worker.cs @@ -10,18 +10,9 @@ public Worker(IConfiguration configuration, ILogger logger, IFindAircraftType findAircraftType) { - if (configuration is null) - { - throw new ArgumentNullException(nameof(configuration)); - } - if (logger is null) - { - throw new ArgumentNullException(nameof(logger)); - } - if (findAircraftType is null) - { - throw new ArgumentNullException(nameof(findAircraftType)); - } + ArgumentNullException.ThrowIfNull(configuration); + ArgumentNullException.ThrowIfNull(logger); + ArgumentNullException.ThrowIfNull(findAircraftType); _configuration = configuration; _logger = logger; _findAircraftType = findAircraftType;