mirror of
https://github.com/dotMorten/NmeaParser.git
synced 2025-12-06 07:12:04 +01:00
Initial stab at merging multi-sentence messages into a single easier-to-use message
This commit is contained in:
parent
4e8ad28503
commit
be31b94f9c
|
|
@ -18,19 +18,14 @@ using System.Text;
|
|||
|
||||
namespace NmeaParser
|
||||
{
|
||||
interface IMultiPartMessage : System.Collections.IEnumerable
|
||||
public interface IMultiSentenceMessage : System.Collections.IEnumerable
|
||||
{
|
||||
/// <summary>
|
||||
/// Total number of messages of this type in this cycle
|
||||
/// </summary>
|
||||
int TotalMessages { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Message number
|
||||
/// </summary>
|
||||
int MessageNumber { get; }
|
||||
bool TryAppend(string[] values);
|
||||
bool IsComplete { get; }
|
||||
IEnumerable<string> SerializeParts();
|
||||
}
|
||||
interface IMultiPartMessage<T> : IMultiPartMessage, IEnumerable<T>
|
||||
{
|
||||
|
||||
public interface IMultiSentenceMessage<T> : IMultiSentenceMessage, IEnumerable<T>
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
@ -24,8 +24,13 @@ namespace NmeaParser.Nmea
|
|||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gsv")]
|
||||
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
|
||||
[NmeaMessageType("--GSV")]
|
||||
public class Gsv : NmeaMessage, IMultiPartMessage<SatelliteVehicle>
|
||||
public class Gsv : NmeaMessage, IMultiSentenceMessage<SatelliteVehicle>
|
||||
{
|
||||
private readonly List<SatelliteVehicle> svs = new List<SatelliteVehicle>();
|
||||
private int lastMessageNumber = 0;
|
||||
private int totalMessages;
|
||||
private readonly int firstMessageNumber;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="Gsv"/> class.
|
||||
/// </summary>
|
||||
|
|
@ -36,9 +41,10 @@ namespace NmeaParser.Nmea
|
|||
if (message == null || message.Length < 3)
|
||||
throw new ArgumentException("Invalid GSV", "message");
|
||||
|
||||
TotalMessages = int.Parse(message[0], CultureInfo.InvariantCulture);
|
||||
MessageNumber = int.Parse(message[1], CultureInfo.InvariantCulture);
|
||||
totalMessages = int.Parse(message[0], CultureInfo.InvariantCulture);
|
||||
firstMessageNumber = int.Parse(message[1], CultureInfo.InvariantCulture);
|
||||
SVsInView = int.Parse(message[2], CultureInfo.InvariantCulture);
|
||||
AppendParts(message);
|
||||
|
||||
List<SatelliteVehicle> svs = new List<SatelliteVehicle>();
|
||||
for (int i = 3; i < message.Length - 3; i += 4)
|
||||
|
|
@ -48,18 +54,37 @@ namespace NmeaParser.Nmea
|
|||
else
|
||||
svs.Add(new SatelliteVehicle(message, i));
|
||||
}
|
||||
this.SVs = svs.ToArray();
|
||||
this.SVs = svs.AsReadOnly();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Total number of messages of this type in this cycle
|
||||
/// </summary>
|
||||
public int TotalMessages { get; }
|
||||
bool IMultiSentenceMessage.TryAppend(string[] values) => AppendParts(values);
|
||||
bool IMultiSentenceMessage.IsComplete => firstMessageNumber == 1 && lastMessageNumber == totalMessages;
|
||||
|
||||
/// <summary>
|
||||
/// Message number
|
||||
/// </summary>
|
||||
public int MessageNumber { get; }
|
||||
private bool AppendParts(string[] message)
|
||||
{
|
||||
int msgCount = int.Parse(message[0], CultureInfo.InvariantCulture);
|
||||
int msgNumber = int.Parse(message[1], CultureInfo.InvariantCulture);
|
||||
var satellites = int.Parse(message[2], CultureInfo.InvariantCulture);
|
||||
|
||||
if (msgCount != totalMessages || msgNumber != lastMessageNumber + 1 || satellites != SVsInView)
|
||||
return false; // Messages do not match
|
||||
|
||||
for (int i = 3; i < message.Length - 3; i += 4)
|
||||
{
|
||||
if (message[i].Length == 0)
|
||||
continue;
|
||||
else
|
||||
svs.Add(new SatelliteVehicle(message, i));
|
||||
}
|
||||
lastMessageNumber = msgNumber;
|
||||
this.SVs = svs.AsReadOnly();
|
||||
return true;
|
||||
}
|
||||
|
||||
IEnumerable<string> IMultiSentenceMessage.SerializeParts()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Total number of SVs in view
|
||||
|
|
@ -69,7 +94,7 @@ namespace NmeaParser.Nmea
|
|||
/// <summary>
|
||||
/// Satellite vehicles in this message part.
|
||||
/// </summary>
|
||||
public IReadOnlyList<SatelliteVehicle> SVs { get; }
|
||||
public IReadOnlyList<SatelliteVehicle> SVs { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns an enumerator that iterates through the collection.
|
||||
|
|
|
|||
|
|
@ -92,10 +92,10 @@ namespace NmeaParser.Nmea
|
|||
/// Invalid nmea message: Missing starting character '$'
|
||||
/// or checksum failure
|
||||
/// </exception>
|
||||
public static NmeaMessage Parse(string message)
|
||||
public static NmeaMessage Parse(string message, IMultiSentenceMessage? previousSentence = null)
|
||||
{
|
||||
if (string.IsNullOrEmpty(message))
|
||||
throw new ArgumentNullException("message");
|
||||
throw new ArgumentNullException(nameof(message));
|
||||
|
||||
int checksum = -1;
|
||||
if (message[0] != '$')
|
||||
|
|
@ -120,6 +120,13 @@ namespace NmeaParser.Nmea
|
|||
string[] parts = message.Split(new char[] { ',' });
|
||||
string MessageType = parts[0].Substring(1);
|
||||
string[] MessageParts = parts.Skip(1).ToArray();
|
||||
if(previousSentence is NmeaMessage pmsg && pmsg.MessageType == MessageType)
|
||||
{
|
||||
if (previousSentence.TryAppend(MessageParts))
|
||||
{
|
||||
return pmsg;
|
||||
}
|
||||
}
|
||||
if (messageTypes.ContainsKey(MessageType))
|
||||
{
|
||||
return (NmeaMessage)messageTypes[MessageType].Invoke(new object[] { MessageType, MessageParts });
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ namespace NmeaParser
|
|||
m_cts = new CancellationTokenSource();
|
||||
m_stream = await OpenStreamAsync();
|
||||
StartParser(m_cts.Token);
|
||||
MultiPartMessageCache.Clear();
|
||||
lock (m_lockObject)
|
||||
_lastMultiMessage = null;
|
||||
lock (m_lockObject)
|
||||
{
|
||||
IsOpen = true;
|
||||
m_isOpening = false;
|
||||
|
|
@ -132,7 +132,7 @@ namespace NmeaParser
|
|||
await m_ParserTask;
|
||||
if (m_stream != null)
|
||||
await CloseStreamAsync(m_stream);
|
||||
MultiPartMessageCache.Clear();
|
||||
_lastMultiMessage = null;
|
||||
m_stream = null;
|
||||
lock (m_lockObject)
|
||||
{
|
||||
|
|
@ -167,58 +167,41 @@ namespace NmeaParser
|
|||
}
|
||||
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 = NmeaParser.Nmea.NmeaMessage.Parse(p);
|
||||
var msg = NmeaParser.Nmea.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 { }
|
||||
}
|
||||
|
||||
private void OnMessageReceived(Nmea.NmeaMessage msg)
|
||||
private void OnMessageReceived(Nmea.NmeaMessage msg)
|
||||
{
|
||||
if (msg == null)
|
||||
return;
|
||||
Nmea.NmeaMessage[]? messageParts = null;
|
||||
if (msg is IMultiPartMessage multi)
|
||||
{
|
||||
string messageType = msg.MessageType.Substring(2); //We don't care about the two first characters. Ie GPGSV, GLGSV, GAGSV etc are all part of the same multi-part message
|
||||
if (MultiPartMessageCache.ContainsKey(messageType))
|
||||
{
|
||||
var dic = MultiPartMessageCache[messageType];
|
||||
if (dic.ContainsKey(multi.MessageNumber - 1) && !dic.ContainsKey(multi.MessageNumber))
|
||||
{
|
||||
dic[multi.MessageNumber] = msg;
|
||||
}
|
||||
else //Something is out of order. Clear cache
|
||||
MultiPartMessageCache.Remove(messageType);
|
||||
}
|
||||
else if (multi.MessageNumber == 1)
|
||||
{
|
||||
MultiPartMessageCache[messageType] = new Dictionary<int, Nmea.NmeaMessage>(multi.TotalMessages);
|
||||
MultiPartMessageCache[messageType][1] = msg;
|
||||
}
|
||||
if (MultiPartMessageCache.ContainsKey(messageType))
|
||||
{
|
||||
var dic = MultiPartMessageCache[messageType];
|
||||
if (dic.Count == multi.TotalMessages) //We have a full list
|
||||
{
|
||||
MultiPartMessageCache.Remove(messageType);
|
||||
messageParts = dic.Values.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MessageReceived?.Invoke(this, new NmeaMessageReceivedEventArgs(msg, messageParts));
|
||||
|
||||
MessageReceived?.Invoke(this, new NmeaMessageReceivedEventArgs(msg));
|
||||
}
|
||||
|
||||
private readonly Dictionary<string, Dictionary<int, Nmea.NmeaMessage>> MultiPartMessageCache = new Dictionary<string,Dictionary<int,Nmea.NmeaMessage>>();
|
||||
//private readonly Dictionary<string, Dictionary<int, Nmea.NmeaMessage>> MultiPartMessageCache = new Dictionary<string,Dictionary<int,Nmea.NmeaMessage>>();
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when an NMEA message is received.
|
||||
|
|
@ -289,10 +272,9 @@ namespace NmeaParser
|
|||
/// </summary>
|
||||
public sealed class NmeaMessageReceivedEventArgs : EventArgs
|
||||
{
|
||||
internal NmeaMessageReceivedEventArgs(Nmea.NmeaMessage message, IReadOnlyList<Nmea.NmeaMessage>? messageParts)
|
||||
internal NmeaMessageReceivedEventArgs(Nmea.NmeaMessage message)
|
||||
{
|
||||
Message = message;
|
||||
MessageParts = messageParts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -309,7 +291,7 @@ namespace NmeaParser
|
|||
/// <value>
|
||||
/// <c>true</c> if this instance is multi part; otherwise, <c>false</c>.
|
||||
/// </value>
|
||||
public bool IsMultipart => Message is IMultiPartMessage;
|
||||
public bool IsMultipart => Message is IMultiSentenceMessage;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the message parts if this is a multi-part message and all message parts has been received.
|
||||
|
|
|
|||
Loading…
Reference in a new issue