diff --git a/src/NmeaParser/NmeaDevice.cs b/src/NmeaParser/NmeaDevice.cs index 3794903..2a2c076 100644 --- a/src/NmeaParser/NmeaDevice.cs +++ b/src/NmeaParser/NmeaDevice.cs @@ -80,6 +80,7 @@ namespace NmeaParser readCount = await ReadAsync(buffer, 0, 1024, token).ConfigureAwait(false); failcounter = 0; } + catch (TaskCanceledException) { } catch (TimeoutException t) { OnDisconnecting(t); diff --git a/src/NmeaParser/SerialPortDevice.Desktop.cs b/src/NmeaParser/SerialPortDevice.Desktop.cs index ea855ba..6940993 100644 --- a/src/NmeaParser/SerialPortDevice.Desktop.cs +++ b/src/NmeaParser/SerialPortDevice.Desktop.cs @@ -19,9 +19,11 @@ using System.Linq; using System.Text; using System.IO; using System.Threading.Tasks; +using System.IO.Ports; +using System.Threading; namespace NmeaParser -{ +{ /// /// A Serial Port NMEA device /// @@ -42,6 +44,9 @@ namespace NmeaParser /// } /// /// +#if WINDOWS && NET6_0_OR_GREATER + [Obsolete("Use WinRTSerialDevice type instead, as the underlying SerialPort implementation comes with a range of issues")] +#endif public class SerialPortDevice : NmeaDevice { /// @@ -69,6 +74,26 @@ namespace NmeaParser return Task.FromResult(Port.BaseStream); } + //private class SerialPortStreamWrapper : Stream + //{ + // private SerialPort port; + // public SerialPortStreamWrapper(SerialPort port) + // { + // this.port = port; + // } + + // public override int Read(byte[] buffer, int offset, int count) => port.Read(buffer, offset, count); + + // public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + // { + + // return base.ReadAsync(buffer, offset, count, cancellationToken); + // } + // public override int ReadTimeout { get => port.ReadTimeout ; set => port.ReadTimeout = value; } + + // public override int WriteTimeout { get => port.WriteTimeout; set => port.WriteTimeout = value; } + //} + /// protected override Task CloseStreamAsync(System.IO.Stream stream) { diff --git a/src/NmeaParser/SerialPortDevice.UWP.cs b/src/NmeaParser/SerialPortDevice.UWP.cs index 8fe2131..a3b930d 100644 --- a/src/NmeaParser/SerialPortDevice.UWP.cs +++ b/src/NmeaParser/SerialPortDevice.UWP.cs @@ -60,6 +60,7 @@ namespace NmeaParser /// } /// /// + [Obsolete("Use WinRTSerialDevice type instead")] public class SerialPortDevice : NmeaDevice { private SerialDevice m_port; diff --git a/src/NmeaParser/WinRTSerialDevice.cs b/src/NmeaParser/WinRTSerialDevice.cs new file mode 100644 index 0000000..70aca0d --- /dev/null +++ b/src/NmeaParser/WinRTSerialDevice.cs @@ -0,0 +1,149 @@ + +// ******************************************************************************* +// * Licensed under the Apache License, Version 2.0 (the "License"); +// * you may not use this file except in compliance with the License. +// * You may obtain a copy of the License at +// * +// * http://www.apache.org/licenses/LICENSE-2.0 +// * +// * Unless required by applicable law or agreed to in writing, software +// * distributed under the License is distributed on an "AS IS" BASIS, +// * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// * See the License for the specific language governing permissions and +// * limitations under the License. +// ****************************************************************************** + +#if WINDOWS_UWP || NET6_0_OR_GREATER && WINDOWS +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; +using System.Threading.Tasks; +using Windows.Devices.SerialCommunication; +using System.Runtime.InteropServices.WindowsRuntime; + +namespace NmeaParser +{ + /// + /// A Serial Port NMEA device using Windows' WinRT APIs. + /// + /// + /// + /// To use the NMEA Parser against a serial device in a Windows 10 Universal app, ensure the serial device capability is enabled by opening package.appxmanifest in a text editor, and add the following to the <Capabilities> section: + /// + /// + /// <DeviceCapability Name="serialcommunication"> + /// <Device Id="any"> + /// <Function Type="name:serialPort" /> + /// </Device> + /// </DeviceCapability> + /// + /// + /// var selector = SerialDevice.GetDeviceSelector("COM3"); //Get the serial port on port '3' + /// var devices = await DeviceInformation.FindAllAsync(selector); + /// if(devices.Any()) //if the device is found + /// { + /// var deviceInfo = devices.First(); + /// var serialDevice = await SerialDevice.FromIdAsync(deviceInfo.Id); + /// //Set up serial device according to device specifications: + /// //This might differ from device to device + /// serialDevice.BaudRate = 4800; + /// serialDevice.DataBits = 8; + /// serialDevice.Parity = SerialParity.None; + /// var device = new NmeaParser.SerialPortDevice(serialDevice); + /// device.MessageReceived += device_NmeaMessageReceived; + /// } + /// ... + /// private void device_NmeaMessageReceived(NmeaParser.NmeaDevice sender, NmeaMessageReceivedEventArgs args) + /// { + /// // called when a message is received + /// } + /// + /// + public class WinRTSerialDevice : NmeaDevice + { + private SerialDevice m_port; + + /// + /// Gets a list of serial devices available. + /// + /// A set of serial devices available. + public static async Task> GetSerialDevicesAsync() + { + List services = new List(); + if(Windows.Foundation.Metadata.ApiInformation.IsMethodPresent("Windows.Devices.Enumeration.DeviceInformation", "FindAllAsync")) // Ensure we are on an OS where these WinRT APIs are supported + { + + if (Windows.Foundation.Metadata.ApiInformation.IsMethodPresent("Windows.Devices.SerialCommunication.SerialDevice", "GetDeviceSelector")) + { + var devices = await Windows.Devices.Enumeration.DeviceInformation.FindAllAsync(SerialDevice.GetDeviceSelector()); + foreach (var d in devices) + services.Add(await SerialDevice.FromIdAsync(d.Id)); + } + } + return services; + } + + /// + /// Initializes a new instance of the class. + /// + /// The serial port. + /// port + public WinRTSerialDevice(SerialDevice device) + { + if (device == null) + throw new ArgumentNullException("device"); + m_port = device; + } + + /// + /// Gets the active serial port. + /// + public SerialDevice SerialDevice + { + get + { + return m_port; + } + } + + /// + protected override Task OpenStreamAsync() + { + return Task.FromResult(m_port.InputStream.AsStreamForRead(0)); + } + + /// + protected override Task CloseStreamAsync(System.IO.Stream stream) + { + return Task.CompletedTask; + } + + /// + /// Writes data to the serial port (useful for RTCM/dGPS scenarios) + /// + /// The byte array that contains the data to write to the port. + /// The zero-based byte offset in the buffer parameter at which to begin copying + /// bytes to the port. + /// The number of bytes to write. + [Obsolete("Use WriteAsync")] + public void Write(byte[] buffer, int offset, int count) + { + m_port.OutputStream.AsStreamForWrite().Write(buffer, offset, count); + } + + /// + public override bool CanWrite => true; + + /// + public override Task WriteAsync(byte[] buffer, int offset, int length) + { + if (m_port == null) + throw new InvalidOperationException("Device not open"); + + return m_port.OutputStream.WriteAsync(buffer.AsBuffer(offset, length)).AsTask(); + } + } +} +#endif \ No newline at end of file diff --git a/src/SampleApp.WinDesktop/MainWindow.xaml.cs b/src/SampleApp.WinDesktop/MainWindow.xaml.cs index 639674f..123f97c 100644 --- a/src/SampleApp.WinDesktop/MainWindow.xaml.cs +++ b/src/SampleApp.WinDesktop/MainWindow.xaml.cs @@ -1,4 +1,5 @@ -using NmeaParser.Gnss; +using NmeaParser; +using NmeaParser.Gnss; using System; using System.Collections.Generic; using System.Linq; @@ -6,6 +7,8 @@ using System.Threading.Tasks; using System.Windows; using System.Windows.Controls; using System.Windows.Data; +using Windows.Devices.Enumeration; +using Windows.Devices.SerialCommunication; namespace SampleApp.WinDesktop { @@ -28,22 +31,32 @@ namespace SampleApp.WinDesktop public MainWindow() { InitializeComponent(); - - //Get list of serial ports for device tab - var availableSerialPorts = System.IO.Ports.SerialPort.GetPortNames().OrderBy(s => s); - serialPorts.ItemsSource = availableSerialPorts; - serialPorts.SelectedIndex = 0; - // Use serial portName: - //var comPort = availableSerialPorts.First(); - //var portName = new System.IO.Ports.SerialPort(comPort, 4800); - //var device = new NmeaParser.SerialPortDevice(portName); - //Use a log file for playing back logged data var device = new NmeaParser.NmeaFileDevice("NmeaSampleData.txt") { EmulatedBaudRate = 9600, BurstRate = TimeSpan.FromSeconds(1d) }; _ = StartDevice(device); + LoadSerialDevices(); LoadBluetoothDevices(); + } + //Get list of serial ports for device tab + private async void LoadSerialDevices() + { + var deviceList = new List(); + var devices = await WinRTSerialDevice.GetSerialDevicesAsync(); + foreach (var item in devices) + { + deviceList.Add(new DeviceInfo() + { + DisplayName = $"{item.PortName}", + CreateMethod = () => + { + return Task.FromResult(new WinRTSerialDevice(item)); + } + }); + } + serialPorts.ItemsSource = deviceList; + serialPorts.SelectedIndex = 0; } public class DeviceInfo { @@ -206,9 +219,10 @@ namespace SampleApp.WinDesktop { try { - var portName = serialPorts.Text as string; - var baudRate = int.Parse(baudRates.Text); - var device = new NmeaParser.SerialPortDevice(new System.IO.Ports.SerialPort(portName, baudRate)); + var portName = serialPorts.SelectedItem as DeviceInfo; + var baudRate = uint.Parse(baudRates.Text); + var device = await portName.CreateMethod() as WinRTSerialDevice; + device.SerialDevice.BaudRate = baudRate; try { await StartDevice(device);