diff --git a/src/NmeaParser/BluetoothDevice.WinStore.cs b/src/NmeaParser/BluetoothDevice.WinStore.cs
index efb359c..0bf62aa 100644
--- a/src/NmeaParser/BluetoothDevice.WinStore.cs
+++ b/src/NmeaParser/BluetoothDevice.WinStore.cs
@@ -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;
-
- ///
- /// Initializes a new instance of the class.
- ///
- /// The RF Comm Device service.
- public BluetoothDevice(Windows.Devices.Bluetooth.Rfcomm.RfcommDeviceService service)
+ private StreamSocket m_socket;
+ private bool m_disposeService;
+
+ ///
+ /// Gets a list of bluetooth devices that supports serial communication
+ ///
+ ///
+ public static async Task> GetBluetoothSerialDevicesAsync()
+ {
+ string serialDeviceType = RfcommDeviceService.GetDeviceSelector(RfcommServiceId.SerialPort);
+ var devices = await DeviceInformation.FindAllAsync(serialDeviceType);
+ List services = new List();
+ foreach(var d in devices)
+ services.Add(await RfcommDeviceService.FromIdAsync(d.Id));
+ return services;
+ }
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// The RF Comm Device service.
+ /// Whether this devicee should also dispose the RfcommDeviceService provided when this device disposes.
+ public BluetoothDevice(RfcommDeviceService service, bool disposeService = false)
{
m_deviceService = service;
- }
+ m_disposeService = disposeService;
+ }
#if WINDOWS_UWP
@@ -62,6 +80,16 @@ namespace NmeaParser
}
#endif
+ ///
+ protected override void Dispose(bool disposing)
+ {
+ if (m_disposeService && m_deviceService != null)
+ m_deviceService.Dispose();
+ m_deviceService = null;
+ m_devicePeer = null;
+ base.Dispose(disposing);
+ }
+
///
/// Creates the stream the NmeaDevice is working on top off.
///
@@ -69,6 +97,7 @@ namespace NmeaParser
protected override async Task 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
}
///
@@ -90,13 +119,52 @@ namespace NmeaParser
///
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);
+
+ ///
+ protected override async Task 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();
+ }
+ }
+
+ ///
+ public override bool CanWrite => true;
+
+ ///
+ 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
\ No newline at end of file
diff --git a/src/NmeaParser/NmeaDevice.cs b/src/NmeaParser/NmeaDevice.cs
index 5f7c60a..bb58aaf 100644
--- a/src/NmeaParser/NmeaDevice.cs
+++ b/src/NmeaParser/NmeaDevice.cs
@@ -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;
///
/// Opens the device connection.
///
@@ -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
});
}
+ ///
+ /// Performs a read operation of the stream
+ ///
+ /// The buffer to write the data into.
+ /// The byte offset in buffer at which to begin writing data from the stream.
+ /// The maximum number of bytes to read.
+ /// The token to monitor for cancellation requests. The default value is System.Threading.CancellationToken.None.
+ ///
+ /// 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.
+ ///
+ protected virtual Task 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);
+ }
+
///
/// Creates the stream the NmeaDevice is working on top off.
///
///
protected abstract Task OpenStreamAsync();
+
///
/// Closes the device.
///
@@ -110,7 +139,10 @@ namespace NmeaParser
MultiPartMessageCache.Clear();
m_stream = null;
lock (m_lockObject)
+ {
+ isOpening = false;
IsOpen = false;
+ }
}
///
/// Closes the stream the NmeaDevice is working on top off.
@@ -235,6 +267,27 @@ namespace NmeaParser
/// true if this instance is open; otherwise, false.
///
public bool IsOpen { get; private set; }
+
+ ///
+ /// Gets a value indicating whether this device supports writing
+ ///
+ ///
+ public virtual bool CanWrite { get => false; }
+
+ ///
+ /// Writes to the device stream. Useful for transmitting RTCM corrections to the device
+ /// Check the property before calling this method.
+ ///
+ /// 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.
+ /// Task
+ ///
+ public virtual Task WriteAsync(byte[] buffer, int offset, int length)
+ {
+ throw new NotSupportedException();
+ }
}
///
diff --git a/src/NmeaParser/NmeaParser.csproj b/src/NmeaParser/NmeaParser.csproj
index ac4f364..4fa8287 100644
--- a/src/NmeaParser/NmeaParser.csproj
+++ b/src/NmeaParser/NmeaParser.csproj
@@ -11,7 +11,7 @@
An NMEA stream parser for serial port, bluetooth and file-based nmea simulation.nmea winrt wpf uwp xamarin gps serialport bluetoothSharpGIS.NmeaParser
- 1.10.1
+ 1.11http://opensource.org/licenses/ms-pl.htmlhttps://github.com/dotMorten/NmeaParserhttps://github.com/dotMorten/NmeaParser
diff --git a/src/NmeaParser/SerialPortDevice.Desktop.cs b/src/NmeaParser/SerialPortDevice.Desktop.cs
index ed080d1..1fc845f 100644
--- a/src/NmeaParser/SerialPortDevice.Desktop.cs
+++ b/src/NmeaParser/SerialPortDevice.Desktop.cs
@@ -81,10 +81,24 @@ namespace NmeaParser
/// 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.Write(buffer, offset, count);
- }
- }
+ m_port.Write(buffer, offset, count);
+ }
+
+ ///
+ public override bool CanWrite => true;
+
+ ///
+ 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