diff --git a/.github/workflows/CIBuild.yml b/.github/workflows/CIBuild.yml index 1dd5a5d..0aea018 100644 --- a/.github/workflows/CIBuild.yml +++ b/.github/workflows/CIBuild.yml @@ -21,5 +21,5 @@ jobs: - name: Build run: | call "C:\Program Files (x86)\Microsoft Visual Studio\2019\Enterprise\Common7\Tools\VsDevCmd.bat" - msbuild /restore /t:Build src/NmeaParser.sln /p:Configuration=Release + msbuild /restore /t:Build src/NmeaParser.sln /p:Configuration=Release /p:JavaSdkDirectory="$(JAVA_HOME_8_X64)" diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 8de345b..cec482c 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -7,7 +7,7 @@ trigger: - master pool: - vmImage: 'windows-latest' + vmImage: 'windows-2019' variables: solution: '**/*.sln' diff --git a/src/NmeaParser.sln b/src/NmeaParser.sln index bd0b36f..2e70d94 100644 --- a/src/NmeaParser.sln +++ b/src/NmeaParser.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2003 +# Visual Studio 16 +VisualStudioVersion = 16.0.0.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NmeaParser", "NmeaParser", "{1701F3BA-A09C-4706-A612-24FD9340FC18}" EndProject diff --git a/src/NmeaParser/BluetoothDevice.Android.cs b/src/NmeaParser/BluetoothDevice.Android.cs index 46ff7b6..0f1d56a 100644 --- a/src/NmeaParser/BluetoothDevice.Android.cs +++ b/src/NmeaParser/BluetoothDevice.Android.cs @@ -29,7 +29,7 @@ namespace NmeaParser { private static Java.Util.UUID SERIAL_UUID = Java.Util.UUID.FromString("00001101-0000-1000-8000-00805F9B34FB"); private Android.Bluetooth.BluetoothDevice m_device; - private BluetoothSocket m_socket; + private BluetoothSocket? m_socket; /// /// Gets a list of bluetooth devices that supports serial communication @@ -51,7 +51,7 @@ namespace NmeaParser /// The Android Bluetooth Device. public BluetoothDevice(Android.Bluetooth.BluetoothDevice device) { - m_device = device; + m_device = device ?? throw new ArgumentNullException(nameof(device)); } /// @@ -80,7 +80,7 @@ namespace NmeaParser if (stream == null) throw new ArgumentNullException("stream"); stream.Dispose(); - m_socket.Dispose(); + m_socket?.Dispose(); m_socket = null; return Task.FromResult(true); } diff --git a/src/NmeaParser/BluetoothDevice.UWP.cs b/src/NmeaParser/BluetoothDevice.UWP.cs index 13a2b9b..f4d6bff 100644 --- a/src/NmeaParser/BluetoothDevice.UWP.cs +++ b/src/NmeaParser/BluetoothDevice.UWP.cs @@ -33,9 +33,9 @@ namespace NmeaParser /// public class BluetoothDevice : NmeaDevice { - private Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService m_deviceService; - private Windows.Networking.Proximity.PeerInformation m_devicePeer; - private StreamSocket m_socket; + private Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService? m_deviceService; + private Windows.Networking.Proximity.PeerInformation? m_devicePeer; + private StreamSocket? m_socket; private bool m_disposeService; private SemaphoreSlim m_semaphoreSlim = new SemaphoreSlim(1, 1); @@ -60,7 +60,7 @@ namespace NmeaParser /// Whether this devicee should also dispose the RfcommDeviceService provided when this device disposes. public BluetoothDevice(RfcommDeviceService service, bool disposeService = false) { - m_deviceService = service; + m_deviceService = service ?? throw new ArgumentNullException(nameof(service)); m_disposeService = disposeService; } @@ -70,7 +70,7 @@ namespace NmeaParser /// The peer information device. public BluetoothDevice(Windows.Networking.Proximity.PeerInformation peer) { - m_devicePeer = peer; + m_devicePeer = peer ?? throw new ArgumentNullException(nameof(peer)); } /// @@ -95,20 +95,37 @@ namespace NmeaParser { await socket.ConnectAsync(m_devicePeer.HostName, "1"); } - else + else if (m_deviceService != null) { await socket.ConnectAsync(m_deviceService.ConnectionHostName, m_deviceService.ConnectionServiceName); } + else + throw new InvalidOperationException(); m_socket = socket; - return null; //We're going to use WinRT buffers instead and will handle read/write, so no reason to return a stream. This is mainly done to avoid locking issues reading and writing at the same time + + return new DummyStream(); //We're going to use WinRT buffers instead and will handle read/write, so no reason to return a real stream. This is mainly done to avoid locking issues reading and writing at the same time } - /// - /// Closes the stream the NmeaDevice is working on top off. - /// - /// The stream. - /// - protected override Task CloseStreamAsync(System.IO.Stream stream) + private class DummyStream : Stream + { + public override bool CanRead => false; + public override bool CanSeek => false; + public override bool CanWrite => false; + public override long Length => throw new NotSupportedException(); + public override long Position { get => throw new NotSupportedException(); set => throw new NotSupportedException(); } + public override void Flush() => throw new NotSupportedException(); + public override int Read(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + public override long Seek(long offset, SeekOrigin origin) => throw new NotSupportedException(); + public override void SetLength(long value) => throw new NotSupportedException(); + public override void Write(byte[] buffer, int offset, int count) => throw new NotSupportedException(); + } + + /// + /// Closes the stream the NmeaDevice is working on top off. + /// + /// The stream. + /// + protected override Task CloseStreamAsync(System.IO.Stream stream) { if(m_socket == null) throw new InvalidOperationException("No connection to close"); @@ -124,6 +141,8 @@ namespace NmeaParser // Reading and writing to the Bluetooth serial connection at the same time seems very unstable in UWP, // so we use a semaphore to ensure we don't read and write at the same time await m_semaphoreSlim.WaitAsync().ConfigureAwait(false); + if (m_socket == null) + throw new InvalidOperationException("Socket not initialized"); try { var r = await m_socket.InputStream.ReadAsync(buffer.AsBuffer(), (uint)count, Windows.Storage.Streams.InputStreamOptions.None); diff --git a/src/NmeaParser/BufferedStreamDevice.cs b/src/NmeaParser/BufferedStreamDevice.cs index f4e182f..3cd218a 100644 --- a/src/NmeaParser/BufferedStreamDevice.cs +++ b/src/NmeaParser/BufferedStreamDevice.cs @@ -27,7 +27,7 @@ namespace NmeaParser /// public abstract class BufferedStreamDevice : NmeaDevice { - private BufferedStream m_stream; + private BufferedStream? m_stream; private readonly int m_readSpeed; /// @@ -71,7 +71,7 @@ namespace NmeaParser /// protected override Task CloseStreamAsync(System.IO.Stream stream) { - m_stream.Dispose(); + m_stream?.Dispose(); return Task.FromResult(true); } @@ -83,8 +83,8 @@ namespace NmeaParser private byte[] m_buffer = new byte[0]; private readonly System.Threading.Timer m_timer; private readonly object lockObj = new object(); - private string groupToken = null; - private string lastLineRead = null; + private string? groupToken = null; + private string? lastLineRead = null; /// /// Initializes a new instance of the class. /// diff --git a/src/NmeaParser/Nmea/Bod.cs b/src/NmeaParser/Nmea/Bod.cs index 551172d..ef906c1 100644 --- a/src/NmeaParser/Nmea/Bod.cs +++ b/src/NmeaParser/Nmea/Bod.cs @@ -63,11 +63,11 @@ namespace NmeaParser.Nmea /// /// Name of origin /// - public string OriginId { get; } + public string? OriginId { get; } /// /// Name of destination /// - public string DestinationId { get; } + public string? DestinationId { get; } } } \ No newline at end of file diff --git a/src/NmeaParser/Nmea/Gns.cs b/src/NmeaParser/Nmea/Gns.cs index 50234a6..db70972 100644 --- a/src/NmeaParser/Nmea/Gns.cs +++ b/src/NmeaParser/Nmea/Gns.cs @@ -228,7 +228,7 @@ namespace NmeaParser.Nmea /// /// eference station ID1, range 0000-4095 - Null if talker ID is GN, additional GNS messages follow with GP and/or GL Reference station ID /// - public string DgpsStationId { get; } + public string? DgpsStationId { get; } /// /// Navigational status diff --git a/src/NmeaParser/NmeaDevice.cs b/src/NmeaParser/NmeaDevice.cs index ec1567a..7ad7b77 100644 --- a/src/NmeaParser/NmeaDevice.cs +++ b/src/NmeaParser/NmeaDevice.cs @@ -29,10 +29,10 @@ namespace NmeaParser { private readonly object m_lockObject = new object(); private string m_message = ""; - private Stream m_stream; - private CancellationTokenSource m_cts; + private Stream? m_stream; + private CancellationTokenSource? m_cts; private bool m_isOpening; - private Task m_ParserTask; + private Task? m_ParserTask; /// /// Initializes a new instance of the class. @@ -183,7 +183,9 @@ namespace NmeaParser private void OnMessageReceived(Nmea.NmeaMessage msg) { - Nmea.NmeaMessage[] messageParts = null; + if (msg == null) + return; + Nmea.NmeaMessage[]? messageParts = null; if (msg is IMultiPartMessage multi) { string messageType = msg.MessageType.Substring(2); //We don't care about the two first characters. Ie GPGSV, GLGSV, GAGSV etc are all part of the same multi-part message @@ -221,7 +223,7 @@ namespace NmeaParser /// /// Occurs when an NMEA message is received. /// - public event EventHandler MessageReceived; + public event EventHandler? MessageReceived; /// /// Releases unmanaged and - optionally - managed resources. @@ -287,7 +289,7 @@ namespace NmeaParser /// public sealed class NmeaMessageReceivedEventArgs : EventArgs { - internal NmeaMessageReceivedEventArgs(Nmea.NmeaMessage message, IReadOnlyList messageParts) + internal NmeaMessageReceivedEventArgs(Nmea.NmeaMessage message, IReadOnlyList? messageParts) { Message = message; MessageParts = messageParts; @@ -315,6 +317,6 @@ namespace NmeaParser /// /// The message parts. /// - public IReadOnlyList MessageParts { get; } + public IReadOnlyList? MessageParts { get; } } } diff --git a/src/NmeaParser/NmeaFileDevice.cs b/src/NmeaParser/NmeaFileDevice.cs index 395fa23..e7f7d13 100644 --- a/src/NmeaParser/NmeaFileDevice.cs +++ b/src/NmeaParser/NmeaFileDevice.cs @@ -27,7 +27,7 @@ namespace NmeaParser public class NmeaFileDevice : BufferedStreamDevice { #if NETFX_CORE - private Windows.Storage.IStorageFile m_storageFile; + private Windows.Storage.IStorageFile? m_storageFile; #endif private string m_filename; @@ -44,8 +44,8 @@ namespace NmeaParser /// /// Initializes a new instance of the class. /// - /// - public NmeaFileDevice(Windows.Storage.IStorageFile fileName) : this(fileName, 1000) + /// + public NmeaFileDevice(Windows.Storage.IStorageFile storageFile) : this(storageFile, 1000) { } #endif @@ -63,12 +63,13 @@ namespace NmeaParser /// /// Initializes a new instance of the class. /// - /// + /// /// The time to wait between each group of lines being read in milliseconds - public NmeaFileDevice(Windows.Storage.IStorageFile fileName, int readSpeed) + public NmeaFileDevice(Windows.Storage.IStorageFile storageFile, int readSpeed) : base(readSpeed) { - m_storageFile = fileName; + m_storageFile = storageFile ?? throw new ArgumentNullException(nameof(storageFile)); + m_filename = storageFile.Path; } #endif @@ -79,10 +80,6 @@ namespace NmeaParser { get { -#if NETFX_CORE - if (m_storageFile != null) - return m_storageFile.Path; -#endif return m_filename; } } diff --git a/src/NmeaParser/NmeaParser.csproj b/src/NmeaParser/NmeaParser.csproj index 80dbe53..447244c 100644 --- a/src/NmeaParser/NmeaParser.csproj +++ b/src/NmeaParser/NmeaParser.csproj @@ -1,7 +1,7 @@ - + - netstandard1.4;net451;monoandroid50;monoandroid70;xamarinios10;uap10.0.14393 + netstandard1.4;net451;monoandroid50;monoandroid70;xamarinios10;uap10.0.16299 true true Debug;Release @@ -27,9 +27,10 @@ Updated license to Apache 2.0 true true $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb - 7.3 + 8.0 true true + enable @@ -41,11 +42,13 @@ Updated license to Apache 2.0 + false $(DefineConstants);XAMARIN;API_LEVEL_21 $(NoWarn);XA0113;XA0114 false + false $(DefineConstants);XAMARIN;API_LEVEL_24 $(NoWarn);XA0113;XA0114 false @@ -56,10 +59,10 @@ Updated license to Apache 2.0 $(NoWarn);VSX1000 - + - + diff --git a/src/NmeaParser/SerialPortDevice.Desktop.cs b/src/NmeaParser/SerialPortDevice.Desktop.cs index 61522fc..7c84311 100644 --- a/src/NmeaParser/SerialPortDevice.Desktop.cs +++ b/src/NmeaParser/SerialPortDevice.Desktop.cs @@ -64,7 +64,7 @@ namespace NmeaParser { if (Port.IsOpen) Port.Close(); - return Task.FromResult(null); + return Task.FromResult(null); } /// @@ -90,7 +90,7 @@ namespace NmeaParser throw new InvalidOperationException("Device not open"); Port.Write(buffer, offset, length); - return Task.FromResult(null); + return Task.FromResult(null); } } } diff --git a/src/NmeaParser/StreamDevice.cs b/src/NmeaParser/StreamDevice.cs index d84d6f7..f92cbef 100644 --- a/src/NmeaParser/StreamDevice.cs +++ b/src/NmeaParser/StreamDevice.cs @@ -33,7 +33,7 @@ namespace NmeaParser /// The stream. public StreamDevice(Stream stream) : base() { - m_stream = stream; + m_stream = stream ?? throw new ArgumentNullException(nameof(stream)); } /// @@ -64,7 +64,6 @@ namespace NmeaParser base.Dispose(disposing); if (m_stream != null) m_stream.Dispose(); - m_stream = null; } /// diff --git a/src/NmeaParser/SystemNmeaDevice.Android.cs b/src/NmeaParser/SystemNmeaDevice.Android.cs index 6f14bf0..37d9b11 100644 --- a/src/NmeaParser/SystemNmeaDevice.Android.cs +++ b/src/NmeaParser/SystemNmeaDevice.Android.cs @@ -32,16 +32,18 @@ namespace NmeaParser /// public class SystemNmeaDevice : NmeaDevice { - private StringStream stream; - private Listener listener; + private StringStream? stream; + private Listener? listener; private LocationManager manager; /// /// Initializes a new instance of the class. /// - public SystemNmeaDevice() + public SystemNmeaDevice(Context context) { - manager = Application.Context.GetSystemService(Context.LocationService) as LocationManager; + if (context == null) + throw new ArgumentNullException(nameof(context)); + manager = context.GetSystemService(Context.LocationService) as LocationManager ?? throw new InvalidOperationException("Cannot acces the Location Service"); } /// @@ -76,10 +78,10 @@ namespace NmeaParser { manager.RemoveUpdates(listener); manager.RemoveNmeaListener(listener); - listener.Dispose(); + listener?.Dispose(); listener = null; stream.Dispose(); - return Task.FromResult(null); + return Task.CompletedTask; } private class Listener : Java.Lang.Object, @@ -102,7 +104,7 @@ namespace NmeaParser NmeaMessage?.Invoke(this, message); } - public event EventHandler NmeaMessage; + public event EventHandler? NmeaMessage; public float Accuracy = float.NaN; diff --git a/src/SampleApp.Droid/MainActivity.cs b/src/SampleApp.Droid/MainActivity.cs index 444e64e..f584d7e 100644 --- a/src/SampleApp.Droid/MainActivity.cs +++ b/src/SampleApp.Droid/MainActivity.cs @@ -96,7 +96,7 @@ namespace SampleApp.Droid return; launched = true; - listener = new NmeaParser.SystemNmeaDevice(); + listener = new NmeaParser.SystemNmeaDevice(ApplicationContext); } else //Bluetooth { diff --git a/src/SampleApp.UWP/SampleApp.UWP.csproj b/src/SampleApp.UWP/SampleApp.UWP.csproj index 4dd1261..6182b26 100644 --- a/src/SampleApp.UWP/SampleApp.UWP.csproj +++ b/src/SampleApp.UWP/SampleApp.UWP.csproj @@ -11,8 +11,8 @@ SampleApp.UWP en-US UAP - 10.0.14393.0 - 10.0.14393.0 + 10.0.16299.0 + 10.0.16299.0 14 true 512 diff --git a/src/UnitTests/NmeaParser.Tests.NET45/NmeaParser.Tests.NET45.csproj b/src/UnitTests/NmeaParser.Tests.NET45/NmeaParser.Tests.NET45.csproj index 8d18743..c3d4368 100644 --- a/src/UnitTests/NmeaParser.Tests.NET45/NmeaParser.Tests.NET45.csproj +++ b/src/UnitTests/NmeaParser.Tests.NET45/NmeaParser.Tests.NET45.csproj @@ -19,6 +19,8 @@ UnitTest + 8.0 + enable true diff --git a/src/UnitTests/NmeaParser.Tests.UWP/NmeaParser.Tests.UWP.csproj b/src/UnitTests/NmeaParser.Tests.UWP/NmeaParser.Tests.UWP.csproj index 95e231d..a115610 100644 --- a/src/UnitTests/NmeaParser.Tests.UWP/NmeaParser.Tests.UWP.csproj +++ b/src/UnitTests/NmeaParser.Tests.UWP/NmeaParser.Tests.UWP.csproj @@ -13,12 +13,14 @@ UAP 10.0.17763.0 10.0.16299.0 - 14 + 16 512 {A5A43C5B-DE2A-4C0C-9213-0A381AF9435A};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} NmeaParser.Tests.UWP_TemporaryKey.pfx $(VisualStudioVersion) D07B149B4E796AB0184B2E2FC9DDC1A2F5CA5A7E + 8.0 + enable true diff --git a/src/UnitTests/NmeaParser.Tests.UWP/UnitTestApp.xaml.cs b/src/UnitTests/NmeaParser.Tests.UWP/UnitTestApp.xaml.cs index bbcb58c..4d18550 100644 --- a/src/UnitTests/NmeaParser.Tests.UWP/UnitTestApp.xaml.cs +++ b/src/UnitTests/NmeaParser.Tests.UWP/UnitTestApp.xaml.cs @@ -47,7 +47,7 @@ namespace NmeaParser.Tests.UWP } #endif - Frame rootFrame = Window.Current.Content as Frame; + Frame? rootFrame = Window.Current?.Content as Frame; // Do not repeat app initialization when the Window already has content, // just ensure that the window is active @@ -64,13 +64,15 @@ namespace NmeaParser.Tests.UWP } // Place the frame in the current Window - Window.Current.Content = rootFrame; + if (Window.Current != null) + Window.Current.Content = rootFrame; } Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.CreateDefaultUI(); // Ensure the current window is active - Window.Current.Activate(); + if (Window.Current != null) + Window.Current.Activate(); Microsoft.VisualStudio.TestPlatform.TestExecutor.UnitTestClient.Run(e.Arguments); } diff --git a/src/UnitTests/NmeaParser.Tests/DeviceTests.cs b/src/UnitTests/NmeaParser.Tests/DeviceTests.cs index d7702d3..ae045ea 100644 --- a/src/UnitTests/NmeaParser.Tests/DeviceTests.cs +++ b/src/UnitTests/NmeaParser.Tests/DeviceTests.cs @@ -13,6 +13,7 @@ namespace NmeaParser.Tests { [TestMethod] [TestCategory("Device")] + [Timeout(2000)] public async Task TestGpgsvGroupMessage() { var message = "$GPGSV,3,1,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4C\n$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F\n$GPGSV,3,3,9,32,10,037,00,,,,,,,,,,,,*74"; @@ -26,7 +27,7 @@ namespace NmeaParser.Tests { Assert.IsTrue(e.IsMultipart, "IsMultiPart"); Assert.IsInstanceOfType(e.Message, typeof(NmeaParser.Nmea.Gsv)); - var msg = e.Message as NmeaParser.Nmea.Gsv; + var msg = (NmeaParser.Nmea.Gsv)e.Message; if (msg.TotalMessages == msg.MessageNumber) { Assert.IsNotNull(e.MessageParts); @@ -50,6 +51,7 @@ namespace NmeaParser.Tests [TestMethod] [TestCategory("Device")] + [Timeout(2000)] public async Task TestMixedGsvGroupMessage() { // A group message can have multiple diffent GSV types. @@ -67,7 +69,7 @@ $GAGSV,4,4,14,19,82,349,40,1,44,220,40,4,24,314,38*5F"; { Assert.IsTrue(e.IsMultipart, "IsMultiPart"); Assert.IsInstanceOfType(e.Message, typeof(NmeaParser.Nmea.Gsv)); - var msg = e.Message as NmeaParser.Nmea.Gsv; + var msg = (NmeaParser.Nmea.Gsv)e.Message; if (msg.TotalMessages == msg.MessageNumber) { Assert.IsNotNull(e.MessageParts); @@ -100,7 +102,8 @@ $GAGSV,4,4,14,19,82,349,40,1,44,220,40,4,24,314,38*5F"; [TestMethod] [TestCategory("Device")] - public async Task TestInvalidGpgsvGroupMessage() + [Timeout(2000)] + public async Task TestInvalidGpgsvGroupMessage() { var message = "$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4D\n$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F\n$GPGSV,3,3,9,32,10,037,00,,,,,,,,,,,,*74"; NmeaDevice dev = new BufferedStringDevice(message);