From bf009ee9c81d09dd10c6e4c23c7917c03134a5f7 Mon Sep 17 00:00:00 2001 From: Morten Nielsen Date: Wed, 29 Jul 2020 12:16:44 -0700 Subject: [PATCH] Add extra message check before sending to the parser --- src/NmeaParser/NmeaDevice.cs | 422 ++++++++++++++++++----------------- 1 file changed, 212 insertions(+), 210 deletions(-) diff --git a/src/NmeaParser/NmeaDevice.cs b/src/NmeaParser/NmeaDevice.cs index 3d7acd2..f45a348 100644 --- a/src/NmeaParser/NmeaDevice.cs +++ b/src/NmeaParser/NmeaDevice.cs @@ -11,52 +11,52 @@ // * See the License for the specific language governing permissions and // * limitations under the License. // ****************************************************************************** - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using System.IO; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.IO; using System.Threading; using NmeaParser.Messages; -namespace NmeaParser -{ - /// - /// A generic abstract NMEA device - /// - public abstract class NmeaDevice : IDisposable - { - private readonly object m_lockObject = new object(); - private string m_message = ""; - private Stream? m_stream; - private CancellationTokenSource? m_cts; +namespace NmeaParser +{ + /// + /// A generic abstract NMEA device + /// + public abstract class NmeaDevice : IDisposable + { + private readonly object m_lockObject = new object(); + private string m_message = ""; + private Stream? m_stream; + private CancellationTokenSource? m_cts; private bool m_isOpening; private Task? m_ParserTask; - /// - /// Initializes a new instance of the class. - /// - protected NmeaDevice() - { - } - + /// + /// Initializes a new instance of the class. + /// + protected NmeaDevice() + { + } + /// /// Creates and opens the stream the will be working on top off. /// - /// A task that represents the asynchronous action. - public async Task OpenAsync() - { - lock (m_lockObject) - { - if (IsOpen || m_isOpening) return; + /// A task that represents the asynchronous action. + public async Task OpenAsync() + { + lock (m_lockObject) + { + if (IsOpen || m_isOpening) return; m_isOpening = true; - } - m_cts = new CancellationTokenSource(); - m_stream = await OpenStreamAsync(); - StartParser(m_cts.Token); - _lastMultiMessage = null; + } + m_cts = new CancellationTokenSource(); + m_stream = await OpenStreamAsync(); + StartParser(m_cts.Token); + _lastMultiMessage = null; lock (m_lockObject) { IsOpen = true; @@ -64,32 +64,32 @@ namespace NmeaParser } } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "_")] - private void StartParser(CancellationToken token) - { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "_")] + private void StartParser(CancellationToken token) + { System.Diagnostics.Debug.WriteLine("Starting parser..."); - m_ParserTask = Task.Run(async () => - { - byte[] buffer = new byte[1024]; - while (!token.IsCancellationRequested) - { - int readCount = 0; - try - { - readCount = await ReadAsync(buffer, 0, 1024, token).ConfigureAwait(false); - } - catch { } - if (token.IsCancellationRequested) - break; - if (readCount > 0) - { - OnData(buffer, readCount); - } - await Task.Yield(); - } - }); - } - + m_ParserTask = Task.Run(async () => + { + byte[] buffer = new byte[1024]; + while (!token.IsCancellationRequested) + { + int readCount = 0; + try + { + readCount = await ReadAsync(buffer, 0, 1024, token).ConfigureAwait(false); + } + catch { } + if (token.IsCancellationRequested) + break; + if (readCount > 0) + { + OnData(buffer, readCount); + } + await Task.Yield(); + } + }); + } + /// /// Performs a read operation of the stream /// @@ -97,13 +97,13 @@ namespace NmeaParser /// 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. - /// + /// + /// 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) @@ -111,180 +111,182 @@ namespace NmeaParser return m_stream.ReadAsync(buffer, 0, 1024, cancellationToken); } - /// - /// Creates and opens the stream the NmeaDevice is working on top off. - /// - /// The opened data stream. - /// - protected abstract Task OpenStreamAsync(); - - /// - /// Closes the device. - /// - /// A task that represents the asynchronous action. - public async Task CloseAsync() - { - if (m_cts != null) - { - if (m_cts != null) - m_cts.Cancel(); - m_cts = null; - } + /// + /// Creates and opens the stream the NmeaDevice is working on top off. + /// + /// The opened data stream. + /// + protected abstract Task OpenStreamAsync(); + + /// + /// Closes the device. + /// + /// A task that represents the asynchronous action. + public async Task CloseAsync() + { + if (m_cts != null) + { + if (m_cts != null) + m_cts.Cancel(); + m_cts = null; + } if (m_ParserTask != null) - await m_ParserTask; + await m_ParserTask; if (m_stream != null) - await CloseStreamAsync(m_stream); - _lastMultiMessage = null; + await CloseStreamAsync(m_stream); + _lastMultiMessage = null; m_stream = null; lock (m_lockObject) { m_isOpening = false; IsOpen = false; - } - } - /// - /// Closes the stream the NmeaDevice is working on top off. - /// - /// The stream to be closed. + } + } + /// + /// Closes the stream the NmeaDevice is working on top off. + /// + /// The stream to be closed. /// A task that represents the asynchronous action. /// - protected abstract Task CloseStreamAsync(Stream stream); - - private void OnData(byte[] data, int count) - { - var nmea = System.Text.Encoding.UTF8.GetString(data, 0, count); - List lines = new List(); - lock (m_lockObject) - { - m_message += nmea; - - var lineEnd = m_message.IndexOf("\n", StringComparison.Ordinal); - while (lineEnd > -1) - { - string line = m_message.Substring(0, lineEnd).Trim(); - m_message = m_message.Substring(lineEnd + 1); - if (!string.IsNullOrEmpty(line)) - lines.Add(line); - lineEnd = m_message.IndexOf("\n", StringComparison.Ordinal); - } - } - foreach(var line in lines) - ProcessMessage(line); + protected abstract Task CloseStreamAsync(Stream stream); + + private void OnData(byte[] data, int count) + { + var nmea = System.Text.Encoding.UTF8.GetString(data, 0, count); + List lines = new List(); + lock (m_lockObject) + { + m_message += nmea; + + var lineEnd = m_message.IndexOf("\n", StringComparison.Ordinal); + while (lineEnd > -1) + { + string line = m_message.Substring(0, lineEnd).Trim(); + m_message = m_message.Substring(lineEnd + 1); + if (!string.IsNullOrEmpty(line)) + lines.Add(line); + lineEnd = m_message.IndexOf("\n", StringComparison.Ordinal); + } + } + foreach(var line in lines) + ProcessMessage(line); } - private IMultiSentenceMessage? _lastMultiMessage; - - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification="Must silently handle invalid/corrupt input")] - private void ProcessMessage(string p) - { - try - { - var msg = NmeaMessage.Parse(p, _lastMultiMessage); + private IMultiSentenceMessage? _lastMultiMessage; + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification="Must silently handle invalid/corrupt input")] + private void ProcessMessage(string p) + { + try + { + if (p.Length == 0 || p[0] != '$') + return; + var msg = NmeaMessage.Parse(p, _lastMultiMessage); if(msg is IMultiSentenceMessage multi) { if (!multi.IsComplete) { _lastMultiMessage = multi; //Keep it around until next time return; - } + } } _lastMultiMessage = null; - if (msg != null) - OnMessageReceived(msg); - } - catch { } + if (msg != null) + OnMessageReceived(msg); + } + catch { } } private void OnMessageReceived(NmeaMessage msg) - { - if (msg == null) - return; + { + if (msg == null) + return; MessageReceived?.Invoke(this, new NmeaMessageReceivedEventArgs(msg)); - } - - //private readonly Dictionary> MultiPartMessageCache = new Dictionary>(); - - /// - /// Occurs when an NMEA message is received. - /// - public event EventHandler? MessageReceived; - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Releases unmanaged and - optionally - managed resources. - /// - /// true to release both managed and unmanaged resources; false to release only unmanaged resources. - protected virtual void Dispose(bool disposing) - { - if (m_stream != null) - { - if (m_cts != null) - { - m_cts.Cancel(); - m_cts = null; - } - CloseStreamAsync(m_stream); - if (disposing && m_stream != null) - m_stream.Dispose(); - m_stream = null; - } - } - - /// - /// Gets a value indicating whether this device is open. - /// - /// - /// true if this instance is open; otherwise, false. - /// - public bool IsOpen { get; private set; } - + } + + //private readonly Dictionary> MultiPartMessageCache = new Dictionary>(); + + /// + /// Occurs when an NMEA message is received. + /// + public event EventHandler? MessageReceived; + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases unmanaged and - optionally - managed resources. + /// + /// true to release both managed and unmanaged resources; false to release only unmanaged resources. + protected virtual void Dispose(bool disposing) + { + if (m_stream != null) + { + if (m_cts != null) + { + m_cts.Cancel(); + m_cts = null; + } + CloseStreamAsync(m_stream); + if (disposing && m_stream != null) + m_stream.Dispose(); + m_stream = null; + } + } + + /// + /// Gets a value indicating whether this device is open. + /// + /// + /// 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; } - + /// + /// + 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 - /// + /// 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(); - } - } - - /// - /// Event argument for the - /// - public sealed class NmeaMessageReceivedEventArgs : EventArgs - { + } + } + + /// + /// Event argument for the + /// + public sealed class NmeaMessageReceivedEventArgs : EventArgs + { internal NmeaMessageReceivedEventArgs(NmeaMessage message) - { - Message = message; - } - - /// - /// Gets the nmea message. - /// - /// - /// The nmea message. - /// + { + Message = message; + } + + /// + /// Gets the nmea message. + /// + /// + /// The nmea message. + /// public NmeaMessage Message { get; } - } -} + } +}