Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates and fixes to NMEA and Seatalk libraries #2351

Merged
merged 5 commits into from
Sep 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/devices/Common/Iot/Device/Common/AngleExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ public static Angle Normalize(this Angle self, bool toFullCircle)
/// <param name="currentTrack">First angle, actual direction</param>
/// <param name="destinationTrack">Second angle, desired direction</param>
/// <returns>The normalized result of <paramref name="currentTrack"/>-<paramref name="destinationTrack"/>. The value is negative if
/// the current track is to port (left) of the the desired track and positive otherwise</returns>
/// the current track is to port (left) of the desired track and positive otherwise</returns>
public static Angle Difference(Angle currentTrack, Angle destinationTrack)
{
double val = currentTrack.Radians - destinationTrack.Radians;
Expand Down
63 changes: 63 additions & 0 deletions src/devices/Common/Iot/Device/Common/PositionExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.Text;

namespace Iot.Device.Common
{
/// <summary>
/// Extensions for positions
/// </summary>
public static partial class PositionExtensions
{
/// <summary>
/// Normalizes the longitude to +/- 180°
/// </summary>
public static GeographicPosition NormalizeAngleTo180(this GeographicPosition position)
{
return new GeographicPosition(position.Latitude, NormalizeAngleTo180(position.Longitude), position.EllipsoidalHeight);
}

/// <summary>
/// Normalizes the angle to +/- 180°
/// </summary>
public static double NormalizeAngleTo180(double angleDegree)
{
angleDegree %= 360;
if (angleDegree <= -180)
{
angleDegree += 360;
}
else if (angleDegree > 180)
{
angleDegree -= 360;
}

return angleDegree;
}

/// <summary>
/// Normalizes the longitude to [0..360°)
/// </summary>
public static GeographicPosition NormalizeAngleTo360(this GeographicPosition position)
{
return new GeographicPosition(position.Latitude, NormalizeAngleTo360(position.Longitude), position.EllipsoidalHeight);
}

/// <summary>
/// Normalizes an angle to [0..360°)
/// </summary>
public static double NormalizeAngleTo360(double angleDegree)
{
angleDegree %= 360;
if (angleDegree < 0)
{
angleDegree += 360;
}

return angleDegree;
}
}
}
75 changes: 75 additions & 0 deletions src/devices/Common/Iot/Device/Common/SimpleFileLogger.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;

namespace Iot.Device.Common
{
/// <summary>
/// A simple logger that creates textual log files. Created via <see cref="SimpleFileLoggerFactory"/>
/// </summary>
public sealed class SimpleFileLogger : ILogger
{
private readonly string _category;
private TextWriter _writer;

/// <summary>
/// Creates a new logger
/// </summary>
/// <param name="category">Logger category name</param>
/// <param name="writer">The text writer for logging.</param>
/// <remarks>
/// The <paramref name="writer"/> must be a thread-safe file writer!
/// </remarks>
public SimpleFileLogger(string category, TextWriter writer)
{
_category = category;
_writer = writer;
Enabled = true;
}

/// <summary>
/// Used by the factory to terminate all its loggers
/// </summary>
internal bool Enabled
{
get;
set;
}

/// <summary>
/// Does nothing and returns an empty IDisposable
/// </summary>
/// <typeparam name="TState">Current logger state</typeparam>
/// <param name="state">State argument</param>
/// <returns>An empty <see cref="IDisposable"/></returns>
public IDisposable BeginScope<TState>(TState state)
where TState : notnull
{
return new LogDispatcher.ScopeDisposable();
}

/// <inheritdoc />
public bool IsEnabled(LogLevel logLevel)
{
return Enabled;
}

/// <inheritdoc />
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
if (Enabled)
{
string msg = formatter(state, exception);
var time = DateTime.Now;
_writer.WriteLine($"{time.ToShortDateString()} {time.ToLongTimeString()} - {_category} - {logLevel} - {msg}");
}
}
}
}
72 changes: 72 additions & 0 deletions src/devices/Common/Iot/Device/Common/SimpleFileLoggerFactory.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;

namespace Iot.Device.Common
{
/// <summary>
/// Provides a very simple console logger that does not require a reference to Microsoft.Extensions.Logging.dll
/// </summary>
public class SimpleFileLoggerFactory : ILoggerFactory, IDisposable
{
private TextWriter? _writer;
private List<SimpleFileLogger> _createdLoggers;

/// <summary>
/// Create a logger factory that creates loggers to logs to the specified file
/// </summary>
/// <param name="fileName">File name to log to (full path)</param>
public SimpleFileLoggerFactory(string fileName)
{
_writer = TextWriter.Synchronized(new StreamWriter(fileName, true, Encoding.UTF8));
_createdLoggers = new List<SimpleFileLogger>();
}

/// <summary>
/// The console logger is built-in here
/// </summary>
/// <param name="provider">Argument is ignored</param>
public void AddProvider(ILoggerProvider provider)
{
}

/// <inheritdoc/>
public ILogger CreateLogger(string categoryName)
{
if (_writer == null)
{
return NullLogger.Instance;
}

var newLogger = new SimpleFileLogger(categoryName, _writer);
_createdLoggers.Add(newLogger);
return newLogger;
}

/// <inheritdoc />
public void Dispose()
{
foreach (var d in _createdLoggers)
{
d.Enabled = false;
}

_createdLoggers.Clear();

if (_writer != null)
{
_writer.Close();
_writer.Dispose();
_writer = null;
}
}
}
}
7 changes: 4 additions & 3 deletions src/devices/Mcp23xxx/Mcp23xxx.cs
Original file line number Diff line number Diff line change
Expand Up @@ -254,11 +254,12 @@ protected override void Dispose(bool disposing)
{
_controller?.Dispose();
_controller = null;

_pinValues.Clear();
_bus?.Dispose();
_bus = null!;
}

_pinValues.Clear();
_bus?.Dispose();
_bus = null!;
base.Dispose(disposing);
}

Expand Down
5 changes: 5 additions & 0 deletions src/devices/Nmea0183/NmeaParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,11 @@ private void Parser()
FireOnParserError(x.Message, NmeaError.PortClosed);
continue;
}
catch (OperationCanceledException x)
{
FireOnParserError(x.Message, NmeaError.PortClosed);
continue;
}

if (currentLine == null)
{
Expand Down
36 changes: 29 additions & 7 deletions src/devices/Nmea0183/NmeaUdpServer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ public class NmeaUdpServer : NmeaSinkAndSource
{
private readonly int _localPort;
private readonly int _remotePort;
private readonly string _broadcastAddress;

private UdpClient? _server;
private NmeaParser? _parser;
Expand Down Expand Up @@ -57,10 +58,26 @@ public NmeaUdpServer(string name, int port)
/// <param name="localPort">The port to receive data on</param>
/// <param name="remotePort">The network port to send data to (must be different than local port when communicating to a local process)</param>
public NmeaUdpServer(string name, int localPort, int remotePort)
: this(name, localPort, remotePort, "255.255.255.255")
{
}

/// <summary>
/// Create an UDP server with the given name on the given port, using an alternate outgoing port. The outgoing and incoming
/// port may be equal only if the sender and the receiver are not on the same computer.
/// </summary>
/// <param name="name">The network source name</param>
/// <param name="localPort">The port to receive data on</param>
/// <param name="remotePort">The network port to send data to (must be different than local port when communicating to a local process)</param>
/// <param name="broadcastAddress">Broadcast address of the network interface to use. This is the IP-Address of that interfaces with all
/// bits set to 1 that are NOT set in the subnetmask. For a default subnet mask of 255.255.255.0 and a local ip of 192.168.1.45 this is therefore
/// 192.168.1.255.</param>
public NmeaUdpServer(string name, int localPort, int remotePort, string broadcastAddress)
: base(name)
{
_localPort = localPort;
_remotePort = remotePort;
_broadcastAddress = broadcastAddress;
}

/// <summary>
Expand Down Expand Up @@ -89,8 +106,11 @@ public override void StartDecode()
throw new InvalidOperationException("Server already started");
}

_server = new UdpClient(_localPort);

_server = new UdpClient();
_server.EnableBroadcast = true;
_server.Client.Bind(new IPEndPoint(IPAddress.Any, _localPort));
// byte[] bytes = Encoding.UTF8.GetBytes("Test message\r\n");
// _server.Send(bytes, bytes.Length, "192.168.1.255", _localPort);
if (!RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
// This is unsupported on MacOS (https://github.com/dotnet/runtime/issues/27653), but this shouldn't
Expand All @@ -110,7 +130,7 @@ public override void StartDecode()
_server.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, 1000);
}

_clientStream = new UdpClientStream(_server, _localPort, _remotePort, this);
_clientStream = new UdpClientStream(_server, _localPort, _remotePort, this, _broadcastAddress);
_parser = new NmeaParser($"{InterfaceName} (Port {_localPort})", _clientStream, _clientStream);
_parser.OnNewSequence += OnSentenceReceivedFromClient;
_parser.OnParserError += ParserOnParserError;
Expand Down Expand Up @@ -179,6 +199,7 @@ private sealed class UdpClientStream : Stream, IDisposable
private readonly int _remotePort;
private readonly NmeaUdpServer _parent;
private readonly Queue<byte> _data;
private readonly string _broadcastAddress;

private object _disposalLock = new object();

Expand All @@ -187,11 +208,12 @@ private sealed class UdpClientStream : Stream, IDisposable
private CancellationTokenSource _cancellationSource;
private CancellationToken _cancellationToken;

public UdpClientStream(UdpClient client, int localPort, int remotePort, NmeaUdpServer parent)
public UdpClientStream(UdpClient client, int localPort, int remotePort, NmeaUdpServer parent, string broadcastAddress)
{
_client = client;
_localPort = localPort;
_remotePort = remotePort;
_broadcastAddress = broadcastAddress;
_parent = parent;
_data = new Queue<byte>();
_knownSenders = new();
Expand Down Expand Up @@ -230,7 +252,7 @@ public override int Read(byte[] buffer, int offset, int count)
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{
#if NET6_O_OR_GREATER
#if NET6_0_OR_GREATER
var result = _client.ReceiveAsync(_cancellationToken).GetAwaiter().GetResult();
datagram = result.Buffer;
#else
Expand Down Expand Up @@ -362,8 +384,8 @@ public override void Write(byte[] buffer, int offset, int count)

try
{
IPEndPoint pt = new IPEndPoint(IPAddress.Broadcast, _remotePort);
_client.Send(tempBuf, count, pt);
IPEndPoint pt = new IPEndPoint(IPAddress.Parse(_broadcastAddress), _remotePort);
_client.Send(tempBuf, count, pt);
_lastUnsuccessfulSend.Stop();
}
catch (SocketException x)
Expand Down
Loading