Add WinRT Serial Device

Fixes #111 and #113
This commit is contained in:
Morten Nielsen 2024-11-19 22:17:05 -08:00
parent 0f0403d879
commit 0be7e552d5
5 changed files with 205 additions and 15 deletions

View file

@ -80,6 +80,7 @@ namespace NmeaParser
readCount = await ReadAsync(buffer, 0, 1024, token).ConfigureAwait(false);
failcounter = 0;
}
catch (TaskCanceledException) { }
catch (TimeoutException t)
{
OnDisconnecting(t);

View file

@ -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
{
{
/// <summary>
/// A Serial Port NMEA device
/// </summary>
@ -42,6 +44,9 @@ namespace NmeaParser
/// }
/// </code>
/// </remarks>
#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
{
/// <summary>
@ -69,6 +74,26 @@ namespace NmeaParser
return Task.FromResult<System.IO.Stream>(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<int> 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; }
//}
/// <inheritdoc />
protected override Task CloseStreamAsync(System.IO.Stream stream)
{

View file

@ -60,6 +60,7 @@ namespace NmeaParser
/// }
/// </code>
/// </remarks>
[Obsolete("Use WinRTSerialDevice type instead")]
public class SerialPortDevice : NmeaDevice
{
private SerialDevice m_port;

View file

@ -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
{
/// <summary>
/// A Serial Port NMEA device using Windows' WinRT APIs.
/// </summary>
/// <remarks>
/// <para>
/// 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 <c>&lt;Capabilities></c> section:
/// </para>
/// <code lang="xml">
/// &lt;DeviceCapability Name="serialcommunication">
/// &lt;Device Id="any">
/// &lt;Function Type="name:serialPort" />
/// &lt;/Device>
/// &lt;/DeviceCapability>
/// </code>
/// <code lang="cs">
/// 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
/// }
/// </code>
/// </remarks>
public class WinRTSerialDevice : NmeaDevice
{
private SerialDevice m_port;
/// <summary>
/// Gets a list of serial devices available.
/// </summary>
/// <returns>A set of serial devices available.</returns>
public static async Task<IEnumerable<SerialDevice>> GetSerialDevicesAsync()
{
List<SerialDevice> services = new List<SerialDevice>();
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;
}
/// <summary>
/// Initializes a new instance of the <see cref="SerialPortDevice" /> class.
/// </summary>
/// <param name="device">The serial port.</param>
/// <exception cref="ArgumentNullException">port</exception>
public WinRTSerialDevice(SerialDevice device)
{
if (device == null)
throw new ArgumentNullException("device");
m_port = device;
}
/// <summary>
/// Gets the active serial port.
/// </summary>
public SerialDevice SerialDevice
{
get
{
return m_port;
}
}
/// <inheritdoc />
protected override Task<System.IO.Stream> OpenStreamAsync()
{
return Task.FromResult<System.IO.Stream>(m_port.InputStream.AsStreamForRead(0));
}
/// <inheritdoc />
protected override Task CloseStreamAsync(System.IO.Stream stream)
{
return Task.CompletedTask;
}
/// <summary>
/// Writes data to the serial port (useful for RTCM/dGPS scenarios)
/// </summary>
/// <param name="buffer">The byte array that contains the data to write to the port.</param>
/// <param name="offset">The zero-based byte offset in the buffer parameter at which to begin copying
/// bytes to the port.</param>
/// <param name="count">The number of bytes to write.</param>
[Obsolete("Use WriteAsync")]
public void Write(byte[] buffer, int offset, int count)
{
m_port.OutputStream.AsStreamForWrite().Write(buffer, offset, count);
}
/// <inheritdoc />
public override bool CanWrite => true;
/// <inheritdoc />
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

View file

@ -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<DeviceInfo>();
var devices = await WinRTSerialDevice.GetSerialDevicesAsync();
foreach (var item in devices)
{
deviceList.Add(new DeviceInfo()
{
DisplayName = $"{item.PortName}",
CreateMethod = () =>
{
return Task.FromResult<NmeaParser.NmeaDevice>(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);