Added support for writing to the devices - useful for transmitting DGPS corrections (For instance RTCM from NTRIP etc) to the device

This commit is contained in:
Morten Nielsen 2018-07-25 20:48:11 -07:00
parent 3e26a65a7b
commit 4d55b6d5e5
6 changed files with 216 additions and 55 deletions

View file

@ -1,18 +1,18 @@
//
// Copyright (c) 2014 Morten Nielsen
//
// Licensed under the Microsoft Public License (Ms-PL) (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://opensource.org/licenses/Ms-PL.html
//
// 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.
//
//
// Copyright (c) 2014 Morten Nielsen
//
// Licensed under the Microsoft Public License (Ms-PL) (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://opensource.org/licenses/Ms-PL.html
//
// 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 NETFX_CORE
using System;
using System.Collections.Generic;
@ -23,7 +23,8 @@ using System.Threading.Tasks;
using Windows.Networking.Sockets;
using Windows.Devices.Bluetooth.Rfcomm;
using System.Runtime.InteropServices.WindowsRuntime;
using Windows.Foundation;
using System.Threading;
using Windows.Devices.Enumeration;
#if WINDOWS_UWP
using Windows.Networking.Proximity;
#endif
@ -39,16 +40,33 @@ namespace NmeaParser
#if WINDOWS_UWP
private Windows.Networking.Proximity.PeerInformation m_devicePeer;
#endif
private StreamSocket m_socket;
/// <summary>
/// Initializes a new instance of the <see cref="BluetoothDevice"/> class.
/// </summary>
/// <param name="service">The RF Comm Device service.</param>
public BluetoothDevice(Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService service)
private StreamSocket m_socket;
private bool m_disposeService;
/// <summary>
/// Gets a list of bluetooth devices that supports serial communication
/// </summary>
/// <returns></returns>
public static async Task<IEnumerable<RfcommDeviceService>> GetBluetoothSerialDevicesAsync()
{
string serialDeviceType = RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort);
var devices = await DeviceInformation.FindAllAsync(serialDeviceType);
List<RfcommDeviceService> services = new List<RfcommDeviceService>();
foreach(var d in devices)
services.Add(await RfcommDeviceService.FromIdAsync(d.Id));
return services;
}
/// <summary>
/// Initializes a new instance of the <see cref="BluetoothDevice"/> class.
/// </summary>
/// <param name="service">The RF Comm Device service.</param>
/// <param name="disposeService">Whether this devicee should also dispose the RfcommDeviceService provided when this device disposes.</param>
public BluetoothDevice(RfcommDeviceService service, bool disposeService = false)
{
m_deviceService = service;
}
m_disposeService = disposeService;
}
#if WINDOWS_UWP
@ -62,6 +80,16 @@ namespace NmeaParser
}
#endif
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
if (m_disposeService && m_deviceService != null)
m_deviceService.Dispose();
m_deviceService = null;
m_devicePeer = null;
base.Dispose(disposing);
}
/// <summary>
/// Creates the stream the NmeaDevice is working on top off.
/// </summary>
@ -69,6 +97,7 @@ namespace NmeaParser
protected override async Task<System.IO.Stream> OpenStreamAsync()
{
var socket = new Windows.Networking.Sockets.StreamSocket();
socket.Control.KeepAlive = true;
#if WINDOWS_UWP
if (m_devicePeer != null)
{
@ -80,7 +109,7 @@ namespace NmeaParser
await socket.ConnectAsync(m_deviceService.ConnectionHostName, m_deviceService.ConnectionServiceName);
}
m_socket = socket;
return socket.InputStream.AsStreamForRead();
return null; //We're going to use WinRT buffers instead and will handle read/write, so no reason to return a stream
}
/// <summary>
@ -90,13 +119,52 @@ namespace NmeaParser
/// <returns></returns>
protected override Task CloseStreamAsync(System.IO.Stream stream)
{
if (stream == null)
throw new ArgumentNullException("stream");
stream.Dispose();
if(m_socket == null)
throw new InvalidOperationException("No connection to close");
m_socket.Dispose();
m_socket = null;
return Task.FromResult(true);
}
private static SemaphoreSlim semaphoreSlim = new SemaphoreSlim(1, 1);
/// <inheritdoc />
protected override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
// 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 semaphoreSlim.WaitAsync().ConfigureAwait(false);
try
{
var r = await m_socket.InputStream.ReadAsync(buffer.AsBuffer(), (uint)count, Windows.Storage.Streams.InputStreamOptions.None);
return (int)r.Length;
}
finally
{
semaphoreSlim.Release();
}
}
/// <inheritdoc />
public override bool CanWrite => true;
/// <inheritdoc />
public override async Task WriteAsync(byte[] buffer, int offset, int length)
{
if (m_socket == null)
throw new InvalidOperationException("Device not open");
// 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 semaphoreSlim.WaitAsync().ConfigureAwait(false);
try
{
await m_socket.OutputStream.WriteAsync(buffer.AsBuffer(offset, length)).AsTask().ConfigureAwait(false);
}
finally
{
semaphoreSlim.Release();
}
}
}
}
#endif

View file

@ -20,6 +20,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO;
using System.Threading;
namespace NmeaParser
{
@ -40,6 +41,8 @@ namespace NmeaParser
protected NmeaDevice()
{
}
bool isOpening;
/// <summary>
/// Opens the device connection.
/// </summary>
@ -48,13 +51,18 @@ namespace NmeaParser
{
lock (m_lockObject)
{
if (IsOpen) return;
IsOpen = true;
if (IsOpen || isOpening) return;
isOpening = true;
}
m_cts = new System.Threading.CancellationTokenSource();
m_stream = await OpenStreamAsync();
StartParser();
MultiPartMessageCache.Clear();
lock (m_lockObject)
{
IsOpen = true;
isOpening = false;
}
}
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "_")]
@ -64,14 +72,13 @@ namespace NmeaParser
System.Diagnostics.Debug.WriteLine("Starting parser...");
var _ = Task.Run(async () =>
{
var stream = m_stream;
byte[] buffer = new byte[1024];
while (!token.IsCancellationRequested)
{
int readCount = 0;
try
{
readCount = await stream.ReadAsync(buffer, 0, 1024, token).ConfigureAwait(false);
readCount = await ReadAsync(buffer, 0, 1024, token).ConfigureAwait(false);
}
catch { }
if (token.IsCancellationRequested)
@ -87,11 +94,33 @@ namespace NmeaParser
});
}
/// <summary>
/// Performs a read operation of the stream
/// </summary>
/// <param name="buffer">The buffer to write the data into.</param>
/// <param name="offset">The byte offset in buffer at which to begin writing data from the stream.</param>
/// <param name="count">The maximum number of bytes to read.</param>
/// <param name="cancellationToken">The token to monitor for cancellation requests. The default value is System.Threading.CancellationToken.None.</param>
/// <returns>
/// A task that represents the asynchronous read operation. The value of the TResult
/// parameter contains the total number of bytes read into the buffer. The result
/// value can be less than the number of bytes requested if the number of bytes currently
/// available is less than the requested number, or it can be 0 (zero) if the end
/// of the stream has been reached.
/// </returns>
protected virtual Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
if (m_stream == null)
return Task.FromResult(0);
return m_stream.ReadAsync(buffer, 0, 1024, cancellationToken);
}
/// <summary>
/// Creates the stream the NmeaDevice is working on top off.
/// </summary>
/// <returns></returns>
protected abstract Task<Stream> OpenStreamAsync();
/// <summary>
/// Closes the device.
/// </summary>
@ -110,7 +139,10 @@ namespace NmeaParser
MultiPartMessageCache.Clear();
m_stream = null;
lock (m_lockObject)
{
isOpening = false;
IsOpen = false;
}
}
/// <summary>
/// Closes the stream the NmeaDevice is working on top off.
@ -235,6 +267,27 @@ namespace NmeaParser
/// <c>true</c> if this instance is open; otherwise, <c>false</c>.
/// </value>
public bool IsOpen { get; private set; }
/// <summary>
/// Gets a value indicating whether this device supports writing
/// </summary>
/// <seealso cref="WriteAsync(byte[], int, int)"/>
public virtual bool CanWrite { get => false; }
/// <summary>
/// Writes to the device stream. Useful for transmitting RTCM corrections to the device
/// Check the <see cref="CanWrite"/> property before calling this method.
/// </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="length">The number of bytes to write.</param>
/// <returns>Task</returns>
/// <seealso cref="CanWrite"/>
public virtual Task WriteAsync(byte[] buffer, int offset, int length)
{
throw new NotSupportedException();
}
}
/// <summary>

View file

@ -11,7 +11,7 @@
<Description>An NMEA stream parser for serial port, bluetooth and file-based nmea simulation.</Description>
<PackageTags>nmea winrt wpf uwp xamarin gps serialport bluetooth</PackageTags>
<PackageId>SharpGIS.NmeaParser</PackageId>
<Version>1.10.1</Version>
<Version>1.11</Version>
<PackageLicenseUrl>http://opensource.org/licenses/ms-pl.html</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/dotMorten/NmeaParser</PackageProjectUrl>
<RepositoryUrl>https://github.com/dotMorten/NmeaParser</RepositoryUrl>

View file

@ -81,10 +81,24 @@ namespace NmeaParser
/// <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.Write(buffer, offset, count);
}
}
m_port.Write(buffer, offset, count);
}
/// <inheritdoc />
public override bool CanWrite => true;
/// <inheritdoc />
public override Task WriteAsync(byte[] buffer, int offset, int length)
{
if (!m_port.IsOpen)
throw new InvalidOperationException("Device not open");
m_port.Write(buffer, offset, length);
return Task.FromResult<object>(null);
}
}
}
#endif

View file

@ -1,18 +1,18 @@
//
// Copyright (c) 2014 Morten Nielsen
//
// Licensed under the Microsoft Public License (Ms-PL) (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://opensource.org/licenses/Ms-PL.html
//
// 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.
//
//
// Copyright (c) 2014 Morten Nielsen
//
// Licensed under the Microsoft Public License (Ms-PL) (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://opensource.org/licenses/Ms-PL.html
//
// 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
using System;
using System.Collections.Generic;
@ -21,7 +21,8 @@ using System.Text;
using System.IO;
using System.Threading.Tasks;
using Windows.Devices.SerialCommunication;
using System.Runtime.InteropServices.WindowsRuntime;
namespace NmeaParser
{
/// <summary>
@ -80,10 +81,23 @@ namespace NmeaParser
/// <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

@ -66,6 +66,18 @@ namespace NmeaParser
if (m_stream != null)
m_stream.Dispose();
m_stream = null;
}
}
/// <inheritdoc />
public override bool CanWrite => m_stream?.CanWrite == true;
/// <inheritdoc />
public override Task WriteAsync(byte[] buffer, int offset, int length)
{
if (m_stream == null)
throw new InvalidOperationException("Device not open");
return m_stream.WriteAsync(buffer, offset, length);
}
}
}