From a142c9b0da93683f7b4a562a5c6ef06aa004d5fa Mon Sep 17 00:00:00 2001 From: Morten Nielsen Date: Wed, 15 Jan 2020 23:41:51 -0800 Subject: [PATCH] Improved multi-message handling and updated all code necessary --- src/NmeaParser/IMultiSentenceMessage.cs | 25 +- src/NmeaParser/Nmea/Gsv.cs | 61 +- src/NmeaParser/Nmea/NmeaMessage.cs | 42 +- .../Nmea/NmeaMultiSentenceMessage.cs | 116 ++++ src/NmeaParser/Nmea/Rte.cs | 27 +- src/NmeaParser/Nmea/Talker.cs | 6 +- src/NmeaParser/NmeaDevice.cs | 20 +- .../Resources/Resource.Designer.cs | 526 +++++++++--------- src/SampleApp.WinDesktop/MainWindow.xaml.cs | 39 +- src/UnitTests/NmeaParser.Tests/DeviceTests.cs | 91 +-- .../NmeaParser.Tests/NmeaMessages.cs | 92 ++- 11 files changed, 587 insertions(+), 458 deletions(-) create mode 100644 src/NmeaParser/Nmea/NmeaMultiSentenceMessage.cs diff --git a/src/NmeaParser/IMultiSentenceMessage.cs b/src/NmeaParser/IMultiSentenceMessage.cs index b825393..6948d04 100644 --- a/src/NmeaParser/IMultiSentenceMessage.cs +++ b/src/NmeaParser/IMultiSentenceMessage.cs @@ -18,14 +18,25 @@ using System.Text; namespace NmeaParser { - public interface IMultiSentenceMessage : System.Collections.IEnumerable + /// + /// Interface used for NMEA messages that span multiple sentences + /// + public interface IMultiSentenceMessage { - bool TryAppend(string[] values); - bool IsComplete { get; } - IEnumerable SerializeParts(); - } + /// + /// Attempts to append one message to an existing one + /// + /// + /// This method should return false if the message being appended isn't the next message in line, and various indicators show this is a different message than the previous one. It should also return false if you append to a message that didn't start with the first message. + /// + /// + /// + /// True is the message was successfully appended, False is the message couldn't be appended. + bool TryAppend(string messageType, string[] values); - public interface IMultiSentenceMessage : IMultiSentenceMessage, IEnumerable - { + /// + /// Gets a value indicating whether this message is fully loaded from all its sentences + /// + bool IsComplete { get; } } } diff --git a/src/NmeaParser/Nmea/Gsv.cs b/src/NmeaParser/Nmea/Gsv.cs index 50ad9fd..db1d5ec 100644 --- a/src/NmeaParser/Nmea/Gsv.cs +++ b/src/NmeaParser/Nmea/Gsv.cs @@ -24,12 +24,9 @@ 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, IMultiSentenceMessage + public class Gsv : NmeaMultiSentenceMessage, IEnumerable { private readonly List svs = new List(); - private int lastMessageNumber = 0; - private int totalMessages; - private readonly int firstMessageNumber; /// /// Initializes a new instance of the class. @@ -40,33 +37,22 @@ namespace NmeaParser.Nmea { if (message == null || message.Length < 3) throw new ArgumentException("Invalid GSV", "message"); - - totalMessages = int.Parse(message[0], CultureInfo.InvariantCulture); - firstMessageNumber = int.Parse(message[1], CultureInfo.InvariantCulture); - SVsInView = int.Parse(message[2], CultureInfo.InvariantCulture); - AppendParts(message); - - List svs = new List(); - for (int i = 3; i < message.Length - 3; i += 4) - { - if (message[i].Length == 0) - continue; - else - svs.Add(new SatelliteVehicle(message, i)); - } - this.SVs = svs.AsReadOnly(); } - bool IMultiSentenceMessage.TryAppend(string[] values) => AppendParts(values); - bool IMultiSentenceMessage.IsComplete => firstMessageNumber == 1 && lastMessageNumber == totalMessages; + /// + protected override int MessageCountIndex => 0; - private bool AppendParts(string[] message) + /// + protected override int MessageNumberIndex => 1; + + /// + protected override bool ParseSentences(Talker talkerType, 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) + if (SVsInView == -1) + SVsInView = satellites; + else if ( satellites != SVsInView) return false; // Messages do not match for (int i = 3; i < message.Length - 3; i += 4) @@ -74,27 +60,20 @@ namespace NmeaParser.Nmea if (message[i].Length == 0) continue; else - svs.Add(new SatelliteVehicle(message, i)); + svs.Add(new SatelliteVehicle(talkerType, message, i)); } - lastMessageNumber = msgNumber; - this.SVs = svs.AsReadOnly(); return true; } - IEnumerable IMultiSentenceMessage.SerializeParts() - { - throw new NotImplementedException(); - } - /// /// Total number of SVs in view /// - public int SVsInView { get; } + public int SVsInView { get; private set; } = -1; /// /// Satellite vehicles in this message part. /// - public IReadOnlyList SVs { get; private set; } + public IReadOnlyList SVs => svs.AsReadOnly(); /// /// Returns an enumerator that iterates through the collection. @@ -120,7 +99,7 @@ namespace NmeaParser.Nmea /// public sealed class SatelliteVehicle { - internal SatelliteVehicle(string[] message, int startIndex) + internal SatelliteVehicle(Talker talker, string[] message, int startIndex) { PrnNumber = int.Parse(message[startIndex], CultureInfo.InvariantCulture); if (double.TryParse(message[startIndex + 1], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out double e)) @@ -130,19 +109,29 @@ namespace NmeaParser.Nmea int snr = -1; if (int.TryParse(message[startIndex + 3], out snr)) SignalToNoiseRatio = snr; + TalkerId = talker; } + + /// + /// Gets the talker ID for this vehicle + /// + public Talker TalkerId { get; } + /// /// SV PRN number /// public int PrnNumber { get; } + /// /// Elevation in degrees, 90 maximum /// public double Elevation { get; } = double.NaN; + /// /// Azimuth, degrees from true north, 000 to 359 /// public double Azimuth { get; } = double.NaN; + /// /// Signal-to-Noise ratio, 0-99 dB (-1 when not tracking) /// diff --git a/src/NmeaParser/Nmea/NmeaMessage.cs b/src/NmeaParser/Nmea/NmeaMessage.cs index 52ebbaf..076b484 100644 --- a/src/NmeaParser/Nmea/NmeaMessage.cs +++ b/src/NmeaParser/Nmea/NmeaMessage.cs @@ -87,6 +87,7 @@ namespace NmeaParser.Nmea /// Parses the specified NMEA message. /// /// The NMEA message string. + /// The previously received message (only used if parsing multi-sentence messages) /// /// /// Invalid nmea message: Missing starting character '$' @@ -120,9 +121,9 @@ 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 is NmeaMessage pmsg && pmsg.MessageType.Substring(2) == MessageType.Substring(2)) { - if (previousSentence.TryAppend(MessageParts)) + if (previousSentence.TryAppend(MessageType, MessageParts)) { return pmsg; } @@ -154,7 +155,7 @@ namespace NmeaParser.Nmea /// /// Gets the talker ID for this message ( /// - public Talker TalkerId => TalkerHelper.GetTalker(MessageType); + public virtual Talker TalkerId => TalkerHelper.GetTalker(MessageType); /// /// Gets a value indicating whether this message type is proprietary @@ -175,24 +176,23 @@ namespace NmeaParser.Nmea /// /// Gets the checksum value of the message. /// - public byte Checksum - { - get - { - int checksumTest = 0; - for (int j = -1; j < MessageParts.Count; j++) - { - string message = j < 0 ? MessageType : MessageParts[j]; - if (j >= 0) - checksumTest ^= 0x2C; //Comma separator - for (int i = 0; i < message.Length; i++) - { - checksumTest ^= Convert.ToByte(message[i]); - } - } - return Convert.ToByte(checksumTest); - } - } + public byte Checksum => GetChecksum(MessageType, MessageParts); + + internal static byte GetChecksum(string messageType, IReadOnlyList messageParts) + { + int checksumTest = 0; + for (int j = -1; j < messageParts.Count; j++) + { + string message = j < 0 ? messageType : messageParts[j]; + if (j >= 0) + checksumTest ^= 0x2C; //Comma separator + for (int i = 0; i < message.Length; i++) + { + checksumTest ^= Convert.ToByte(message[i]); + } + } + return Convert.ToByte(checksumTest); + } internal static double StringToLatitude(string value, string ns) { diff --git a/src/NmeaParser/Nmea/NmeaMultiSentenceMessage.cs b/src/NmeaParser/Nmea/NmeaMultiSentenceMessage.cs new file mode 100644 index 0000000..a646a36 --- /dev/null +++ b/src/NmeaParser/Nmea/NmeaMultiSentenceMessage.cs @@ -0,0 +1,116 @@ +// ******************************************************************************* +// * 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. +// ****************************************************************************** + +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace NmeaParser.Nmea +{ + /// + /// Base class for easily creating message that are spread across multiple sentences + /// + public abstract class NmeaMultiSentenceMessage : NmeaMessage, IMultiSentenceMessage + { + private int lastMessageNumber = 0; + private int totalMessages; + private readonly int firstMessageNumber; + private readonly List messages = new List(); + private readonly bool initialized; + + /// + /// Initializes an instance of the class. + /// + /// Type + /// Message values + protected NmeaMultiSentenceMessage(string messageType, string[] messageParts) : base(messageType, messageParts) + { + totalMessages = int.Parse(messageParts[MessageCountIndex], CultureInfo.InvariantCulture); + firstMessageNumber = int.Parse(messageParts[MessageNumberIndex], CultureInfo.InvariantCulture); + talkerId = base.TalkerId; + if (!((IMultiSentenceMessage)this).TryAppend(messageType, messageParts)) + throw new ArgumentException("Failed to parse message"); + initialized = true; + } + + /// + /// Gets the index in the where the total count of messages is listed. + /// + protected virtual int MessageCountIndex { get; } = 0; + + /// + /// Gets the index in the where the message number is listed. + /// + protected virtual int MessageNumberIndex { get; } = 1; + + bool IMultiSentenceMessage.IsComplete => firstMessageNumber == 1 && lastMessageNumber == totalMessages; + + /// + public override string ToString() + { + System.Text.StringBuilder sb = new System.Text.StringBuilder(); + foreach (var msg in messages) + { + if (sb.Length > 0) + sb.Append("\r\n"); + sb.AppendFormat(CultureInfo.InvariantCulture, "${0},{1}*{2:X2}", MessageType, string.Join(",", msg), Checksum); + } + return sb.ToString(); + } + + bool IMultiSentenceMessage.TryAppend(string messageType, string[] message) + { + if (message == null || message.Length < Math.Max(MessageCountIndex, MessageNumberIndex)) + throw new ArgumentException("Invalid message", "message"); + + + int msgCount = int.Parse(message[0], CultureInfo.InvariantCulture); + int msgNumber = int.Parse(message[1], CultureInfo.InvariantCulture); + + if (initialized) + { + if (firstMessageNumber != 1) //We can only append to message who has message number 1 + return false; + if (msgCount != totalMessages || msgNumber != lastMessageNumber + 1) + return false; // Messages do not match + } + + var talker = TalkerHelper.GetTalker(messageType); + if (talkerId != talker) + talkerId = Talker.Multiple; + if (ParseSentences(talker, message)) + { + lastMessageNumber = msgNumber; + messages.Add(message); + return true; + } + return false; + } + + /// + /// Parses the messages or any message being appended. False should be returned if it's a message being appended doesn't appear to match what has already been loded. + /// + /// + /// + /// True if the message could succesfully be appended. + protected abstract bool ParseSentences(Talker talkerType, string[] message); + + private Talker talkerId; + + + /// + public override Talker TalkerId => talkerId; + + } +} diff --git a/src/NmeaParser/Nmea/Rte.cs b/src/NmeaParser/Nmea/Rte.cs index 0ae1af8..8dcbbc3 100644 --- a/src/NmeaParser/Nmea/Rte.cs +++ b/src/NmeaParser/Nmea/Rte.cs @@ -32,8 +32,10 @@ namespace NmeaParser.Nmea [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Gprte")] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] [NmeaMessageType("--RTE")] - public sealed class Rte : NmeaMessage, IMultiPartMessage + public sealed class Rte : NmeaMultiSentenceMessage, IEnumerable { + private readonly List _waypoints = new List(); + /// /// Waypoint tpe /// @@ -58,23 +60,18 @@ namespace NmeaParser.Nmea { if (message == null || message.Length < 4) throw new ArgumentException("Invalid GPRTE", "message"); - - TotalMessages = int.Parse(message[0], CultureInfo.InvariantCulture); - MessageNumber = int.Parse(message[1], CultureInfo.InvariantCulture); ListType = message[2] == "c" ? WaypointListType.CompleteWaypointsList : WaypointListType.RemainingWaypointsList; RouteId = message[3]; - Waypoints = message.Skip(4).ToArray(); } - /// - /// Total number of messages of this type in this cycle - /// - public int TotalMessages { get; } - - /// - /// Message number - /// - public int MessageNumber { get; } + /// + protected override bool ParseSentences(Talker talker, string[] message) + { + if (MessageParts[2] != message[2] || MessageParts[3] != message[3]) + return false; + _waypoints.AddRange(message.Skip(4)); + return true; + } /// /// Gets the type of the list. @@ -89,7 +86,7 @@ namespace NmeaParser.Nmea /// /// Waypoints /// - public IReadOnlyList Waypoints { get; } + public IReadOnlyList Waypoints => _waypoints.AsReadOnly(); /// /// Returns an enumerator that iterates through the collection. diff --git a/src/NmeaParser/Nmea/Talker.cs b/src/NmeaParser/Nmea/Talker.cs index 134494f..48e99e7 100644 --- a/src/NmeaParser/Nmea/Talker.cs +++ b/src/NmeaParser/Nmea/Talker.cs @@ -114,10 +114,14 @@ namespace NmeaParser.Nmea /// public enum Talker { + /// + /// Multiple talker IDs sometimes seen in + /// + Multiple = -2, /// /// Unrecognized Talker ID /// - Unknown, + Unknown = -1, /// Independent AIS Base Station IndependentAISBaseStation, // = AB /// Dependent AIS Base Station diff --git a/src/NmeaParser/NmeaDevice.cs b/src/NmeaParser/NmeaDevice.cs index 992cccb..b0b1ed4 100644 --- a/src/NmeaParser/NmeaDevice.cs +++ b/src/NmeaParser/NmeaDevice.cs @@ -184,9 +184,9 @@ namespace NmeaParser { _lastMultiMessage = multi; //Keep it around until next time return; - } - _lastMultiMessage = null; + } } + _lastMultiMessage = null; if (msg != null) OnMessageReceived(msg); } @@ -284,21 +284,5 @@ namespace NmeaParser /// The nmea message. /// public Nmea.NmeaMessage Message { get; } - - /// - /// Gets a value indicating whether this instance is a multi part message. - /// - /// - /// true if this instance is multi part; otherwise, false. - /// - public bool IsMultipart => Message is IMultiSentenceMessage; - - /// - /// Gets the message parts if this is a multi-part message and all message parts has been received. - /// - /// - /// The message parts. - /// - public IReadOnlyList? MessageParts { get; } } } diff --git a/src/SampleApp.Droid/Resources/Resource.Designer.cs b/src/SampleApp.Droid/Resources/Resource.Designer.cs index c146640..1084f06 100644 --- a/src/SampleApp.Droid/Resources/Resource.Designer.cs +++ b/src/SampleApp.Droid/Resources/Resource.Designer.cs @@ -31,31 +31,31 @@ namespace SampleApp.Droid public partial class Attribute { - // aapt resource value: 0x7f010007 - public const int font = 2130771975; + // aapt resource value: 0x7F010000 + public const int font = 2130771968; - // aapt resource value: 0x7f010000 - public const int fontProviderAuthority = 2130771968; + // aapt resource value: 0x7F010001 + public const int fontProviderAuthority = 2130771969; - // aapt resource value: 0x7f010003 - public const int fontProviderCerts = 2130771971; + // aapt resource value: 0x7F010002 + public const int fontProviderCerts = 2130771970; - // aapt resource value: 0x7f010004 - public const int fontProviderFetchStrategy = 2130771972; + // aapt resource value: 0x7F010003 + public const int fontProviderFetchStrategy = 2130771971; - // aapt resource value: 0x7f010005 - public const int fontProviderFetchTimeout = 2130771973; + // aapt resource value: 0x7F010004 + public const int fontProviderFetchTimeout = 2130771972; - // aapt resource value: 0x7f010001 - public const int fontProviderPackage = 2130771969; + // aapt resource value: 0x7F010005 + public const int fontProviderPackage = 2130771973; - // aapt resource value: 0x7f010002 - public const int fontProviderQuery = 2130771970; + // aapt resource value: 0x7F010006 + public const int fontProviderQuery = 2130771974; - // aapt resource value: 0x7f010006 - public const int fontStyle = 2130771974; + // aapt resource value: 0x7F010007 + public const int fontStyle = 2130771975; - // aapt resource value: 0x7f010008 + // aapt resource value: 0x7F010008 public const int fontWeight = 2130771976; static Attribute() @@ -71,8 +71,8 @@ namespace SampleApp.Droid public partial class Boolean { - // aapt resource value: 0x7f080000 - public const int abc_action_bar_embed_tabs = 2131230720; + // aapt resource value: 0x7F020000 + public const int abc_action_bar_embed_tabs = 2130837504; static Boolean() { @@ -87,26 +87,26 @@ namespace SampleApp.Droid public partial class Color { - // aapt resource value: 0x7f050003 - public const int notification_action_color_filter = 2131034115; + // aapt resource value: 0x7F030000 + public const int notification_action_color_filter = 2130903040; - // aapt resource value: 0x7f050004 - public const int notification_icon_bg_color = 2131034116; + // aapt resource value: 0x7F030001 + public const int notification_icon_bg_color = 2130903041; - // aapt resource value: 0x7f050000 - public const int notification_material_background_media_default_color = 2131034112; + // aapt resource value: 0x7F030002 + public const int notification_material_background_media_default_color = 2130903042; - // aapt resource value: 0x7f050001 - public const int primary_text_default_material_dark = 2131034113; + // aapt resource value: 0x7F030003 + public const int primary_text_default_material_dark = 2130903043; - // aapt resource value: 0x7f050005 - public const int ripple_material_light = 2131034117; + // aapt resource value: 0x7F030004 + public const int ripple_material_light = 2130903044; - // aapt resource value: 0x7f050002 - public const int secondary_text_default_material_dark = 2131034114; + // aapt resource value: 0x7F030005 + public const int secondary_text_default_material_dark = 2130903045; - // aapt resource value: 0x7f050006 - public const int secondary_text_default_material_light = 2131034118; + // aapt resource value: 0x7F030006 + public const int secondary_text_default_material_light = 2130903046; static Color() { @@ -121,65 +121,65 @@ namespace SampleApp.Droid public partial class Dimension { - // aapt resource value: 0x7f090004 - public const int compat_button_inset_horizontal_material = 2131296260; + // aapt resource value: 0x7F040000 + public const int compat_button_inset_horizontal_material = 2130968576; - // aapt resource value: 0x7f090005 - public const int compat_button_inset_vertical_material = 2131296261; + // aapt resource value: 0x7F040001 + public const int compat_button_inset_vertical_material = 2130968577; - // aapt resource value: 0x7f090006 - public const int compat_button_padding_horizontal_material = 2131296262; + // aapt resource value: 0x7F040002 + public const int compat_button_padding_horizontal_material = 2130968578; - // aapt resource value: 0x7f090007 - public const int compat_button_padding_vertical_material = 2131296263; + // aapt resource value: 0x7F040003 + public const int compat_button_padding_vertical_material = 2130968579; - // aapt resource value: 0x7f090008 - public const int compat_control_corner_material = 2131296264; + // aapt resource value: 0x7F040004 + public const int compat_control_corner_material = 2130968580; - // aapt resource value: 0x7f090009 - public const int notification_action_icon_size = 2131296265; + // aapt resource value: 0x7F040005 + public const int notification_action_icon_size = 2130968581; - // aapt resource value: 0x7f09000a - public const int notification_action_text_size = 2131296266; + // aapt resource value: 0x7F040006 + public const int notification_action_text_size = 2130968582; - // aapt resource value: 0x7f09000b - public const int notification_big_circle_margin = 2131296267; + // aapt resource value: 0x7F040007 + public const int notification_big_circle_margin = 2130968583; - // aapt resource value: 0x7f090001 - public const int notification_content_margin_start = 2131296257; + // aapt resource value: 0x7F040008 + public const int notification_content_margin_start = 2130968584; - // aapt resource value: 0x7f09000c - public const int notification_large_icon_height = 2131296268; + // aapt resource value: 0x7F040009 + public const int notification_large_icon_height = 2130968585; - // aapt resource value: 0x7f09000d - public const int notification_large_icon_width = 2131296269; + // aapt resource value: 0x7F04000A + public const int notification_large_icon_width = 2130968586; - // aapt resource value: 0x7f090002 - public const int notification_main_column_padding_top = 2131296258; + // aapt resource value: 0x7F04000B + public const int notification_main_column_padding_top = 2130968587; - // aapt resource value: 0x7f090003 - public const int notification_media_narrow_margin = 2131296259; + // aapt resource value: 0x7F04000C + public const int notification_media_narrow_margin = 2130968588; - // aapt resource value: 0x7f09000e - public const int notification_right_icon_size = 2131296270; + // aapt resource value: 0x7F04000D + public const int notification_right_icon_size = 2130968589; - // aapt resource value: 0x7f090000 - public const int notification_right_side_padding_top = 2131296256; + // aapt resource value: 0x7F04000E + public const int notification_right_side_padding_top = 2130968590; - // aapt resource value: 0x7f09000f - public const int notification_small_icon_background_padding = 2131296271; + // aapt resource value: 0x7F04000F + public const int notification_small_icon_background_padding = 2130968591; - // aapt resource value: 0x7f090010 - public const int notification_small_icon_size_as_large = 2131296272; + // aapt resource value: 0x7F040010 + public const int notification_small_icon_size_as_large = 2130968592; - // aapt resource value: 0x7f090011 - public const int notification_subtext_size = 2131296273; + // aapt resource value: 0x7F040011 + public const int notification_subtext_size = 2130968593; - // aapt resource value: 0x7f090012 - public const int notification_top_pad = 2131296274; + // aapt resource value: 0x7F040012 + public const int notification_top_pad = 2130968594; - // aapt resource value: 0x7f090013 - public const int notification_top_pad_large_text = 2131296275; + // aapt resource value: 0x7F040013 + public const int notification_top_pad_large_text = 2130968595; static Dimension() { @@ -194,41 +194,41 @@ namespace SampleApp.Droid public partial class Drawable { - // aapt resource value: 0x7f020000 - public const int notification_action_background = 2130837504; + // aapt resource value: 0x7F050000 + public const int notification_action_background = 2131034112; - // aapt resource value: 0x7f020001 - public const int notification_bg = 2130837505; + // aapt resource value: 0x7F050001 + public const int notification_bg = 2131034113; - // aapt resource value: 0x7f020002 - public const int notification_bg_low = 2130837506; + // aapt resource value: 0x7F050002 + public const int notification_bg_low = 2131034114; - // aapt resource value: 0x7f020003 - public const int notification_bg_low_normal = 2130837507; + // aapt resource value: 0x7F050003 + public const int notification_bg_low_normal = 2131034115; - // aapt resource value: 0x7f020004 - public const int notification_bg_low_pressed = 2130837508; + // aapt resource value: 0x7F050004 + public const int notification_bg_low_pressed = 2131034116; - // aapt resource value: 0x7f020005 - public const int notification_bg_normal = 2130837509; + // aapt resource value: 0x7F050005 + public const int notification_bg_normal = 2131034117; - // aapt resource value: 0x7f020006 - public const int notification_bg_normal_pressed = 2130837510; + // aapt resource value: 0x7F050006 + public const int notification_bg_normal_pressed = 2131034118; - // aapt resource value: 0x7f020007 - public const int notification_icon_background = 2130837511; + // aapt resource value: 0x7F050007 + public const int notification_icon_background = 2131034119; - // aapt resource value: 0x7f02000a - public const int notification_template_icon_bg = 2130837514; + // aapt resource value: 0x7F050008 + public const int notification_template_icon_bg = 2131034120; - // aapt resource value: 0x7f02000b - public const int notification_template_icon_low_bg = 2130837515; + // aapt resource value: 0x7F050009 + public const int notification_template_icon_low_bg = 2131034121; - // aapt resource value: 0x7f020008 - public const int notification_tile_bg = 2130837512; + // aapt resource value: 0x7F05000A + public const int notification_tile_bg = 2131034122; - // aapt resource value: 0x7f020009 - public const int notify_panel_notification_icon_bg = 2130837513; + // aapt resource value: 0x7F05000B + public const int notify_panel_notification_icon_bg = 2131034123; static Drawable() { @@ -243,119 +243,119 @@ namespace SampleApp.Droid public partial class Id { - // aapt resource value: 0x7f0a0015 - public const int action0 = 2131361813; + // aapt resource value: 0x7F060000 + public const int action0 = 2131099648; - // aapt resource value: 0x7f0a0012 - public const int action_container = 2131361810; + // aapt resource value: 0x7F060005 + public const int actions = 2131099653; - // aapt resource value: 0x7f0a0019 - public const int action_divider = 2131361817; + // aapt resource value: 0x7F060001 + public const int action_container = 2131099649; - // aapt resource value: 0x7f0a0013 - public const int action_image = 2131361811; + // aapt resource value: 0x7F060002 + public const int action_divider = 2131099650; - // aapt resource value: 0x7f0a0014 - public const int action_text = 2131361812; + // aapt resource value: 0x7F060003 + public const int action_image = 2131099651; - // aapt resource value: 0x7f0a0023 - public const int actions = 2131361827; + // aapt resource value: 0x7F060004 + public const int action_text = 2131099652; - // aapt resource value: 0x7f0a000d - public const int altitude = 2131361805; + // aapt resource value: 0x7F060006 + public const int altitude = 2131099654; - // aapt resource value: 0x7f0a0006 - public const int async = 2131361798; + // aapt resource value: 0x7F060007 + public const int async = 2131099655; - // aapt resource value: 0x7f0a0007 - public const int blocking = 2131361799; + // aapt resource value: 0x7F060008 + public const int blocking = 2131099656; - // aapt resource value: 0x7f0a0016 - public const int cancel_action = 2131361814; + // aapt resource value: 0x7F060009 + public const int cancel_action = 2131099657; - // aapt resource value: 0x7f0a001e - public const int chronometer = 2131361822; + // aapt resource value: 0x7F06000A + public const int chronometer = 2131099658; - // aapt resource value: 0x7f0a000e - public const int device_picker = 2131361806; + // aapt resource value: 0x7F06000B + public const int device_picker = 2131099659; - // aapt resource value: 0x7f0a0025 - public const int end_padder = 2131361829; + // aapt resource value: 0x7F06000C + public const int end_padder = 2131099660; - // aapt resource value: 0x7f0a0008 - public const int forever = 2131361800; + // aapt resource value: 0x7F06000D + public const int forever = 2131099661; - // aapt resource value: 0x7f0a0020 - public const int icon = 2131361824; + // aapt resource value: 0x7F06000E + public const int icon = 2131099662; - // aapt resource value: 0x7f0a0024 - public const int icon_group = 2131361828; + // aapt resource value: 0x7F06000F + public const int icon_group = 2131099663; - // aapt resource value: 0x7f0a001f - public const int info = 2131361823; + // aapt resource value: 0x7F060010 + public const int info = 2131099664; - // aapt resource value: 0x7f0a0009 - public const int italic = 2131361801; + // aapt resource value: 0x7F060011 + public const int italic = 2131099665; - // aapt resource value: 0x7f0a000c - public const int latitude = 2131361804; + // aapt resource value: 0x7F060012 + public const int latitude = 2131099666; - // aapt resource value: 0x7f0a0000 - public const int line1 = 2131361792; + // aapt resource value: 0x7F060013 + public const int line1 = 2131099667; - // aapt resource value: 0x7f0a0001 - public const int line3 = 2131361793; + // aapt resource value: 0x7F060014 + public const int line3 = 2131099668; - // aapt resource value: 0x7f0a000b - public const int longitude = 2131361803; + // aapt resource value: 0x7F060015 + public const int longitude = 2131099669; - // aapt resource value: 0x7f0a0018 - public const int media_actions = 2131361816; + // aapt resource value: 0x7F060016 + public const int media_actions = 2131099670; - // aapt resource value: 0x7f0a000a - public const int normal = 2131361802; + // aapt resource value: 0x7F060017 + public const int normal = 2131099671; - // aapt resource value: 0x7f0a0022 - public const int notification_background = 2131361826; + // aapt resource value: 0x7F060018 + public const int notification_background = 2131099672; - // aapt resource value: 0x7f0a001b - public const int notification_main_column = 2131361819; + // aapt resource value: 0x7F060019 + public const int notification_main_column = 2131099673; - // aapt resource value: 0x7f0a001a - public const int notification_main_column_container = 2131361818; + // aapt resource value: 0x7F06001A + public const int notification_main_column_container = 2131099674; - // aapt resource value: 0x7f0a0011 - public const int output = 2131361809; + // aapt resource value: 0x7F06001B + public const int output = 2131099675; - // aapt resource value: 0x7f0a0021 - public const int right_icon = 2131361825; + // aapt resource value: 0x7F06001C + public const int right_icon = 2131099676; - // aapt resource value: 0x7f0a001c - public const int right_side = 2131361820; + // aapt resource value: 0x7F06001D + public const int right_side = 2131099677; - // aapt resource value: 0x7f0a000f - public const int startButton = 2131361807; + // aapt resource value: 0x7F06001E + public const int startButton = 2131099678; - // aapt resource value: 0x7f0a0017 - public const int status_bar_latest_event_content = 2131361815; + // aapt resource value: 0x7F06001F + public const int status_bar_latest_event_content = 2131099679; - // aapt resource value: 0x7f0a0010 - public const int stopButton = 2131361808; + // aapt resource value: 0x7F060020 + public const int stopButton = 2131099680; - // aapt resource value: 0x7f0a0002 - public const int tag_transition_group = 2131361794; + // aapt resource value: 0x7F060021 + public const int tag_transition_group = 2131099681; - // aapt resource value: 0x7f0a0003 - public const int text = 2131361795; + // aapt resource value: 0x7F060022 + public const int text = 2131099682; - // aapt resource value: 0x7f0a0004 - public const int text2 = 2131361796; + // aapt resource value: 0x7F060023 + public const int text2 = 2131099683; - // aapt resource value: 0x7f0a001d - public const int time = 2131361821; + // aapt resource value: 0x7F060024 + public const int time = 2131099684; - // aapt resource value: 0x7f0a0005 - public const int title = 2131361797; + // aapt resource value: 0x7F060025 + public const int title = 2131099685; static Id() { @@ -370,11 +370,11 @@ namespace SampleApp.Droid public partial class Integer { - // aapt resource value: 0x7f060000 - public const int cancel_button_image_alpha = 2131099648; + // aapt resource value: 0x7F070000 + public const int cancel_button_image_alpha = 2131165184; - // aapt resource value: 0x7f060001 - public const int status_bar_notification_info_maxnum = 2131099649; + // aapt resource value: 0x7F070001 + public const int status_bar_notification_info_maxnum = 2131165185; static Integer() { @@ -389,53 +389,53 @@ namespace SampleApp.Droid public partial class Layout { - // aapt resource value: 0x7f030000 - public const int Main = 2130903040; + // aapt resource value: 0x7F080000 + public const int Main = 2131230720; - // aapt resource value: 0x7f030001 - public const int notification_action = 2130903041; + // aapt resource value: 0x7F080001 + public const int notification_action = 2131230721; - // aapt resource value: 0x7f030002 - public const int notification_action_tombstone = 2130903042; + // aapt resource value: 0x7F080002 + public const int notification_action_tombstone = 2131230722; - // aapt resource value: 0x7f030003 - public const int notification_media_action = 2130903043; + // aapt resource value: 0x7F080003 + public const int notification_media_action = 2131230723; - // aapt resource value: 0x7f030004 - public const int notification_media_cancel_action = 2130903044; + // aapt resource value: 0x7F080004 + public const int notification_media_cancel_action = 2131230724; - // aapt resource value: 0x7f030005 - public const int notification_template_big_media = 2130903045; + // aapt resource value: 0x7F080005 + public const int notification_template_big_media = 2131230725; - // aapt resource value: 0x7f030006 - public const int notification_template_big_media_custom = 2130903046; + // aapt resource value: 0x7F080006 + public const int notification_template_big_media_custom = 2131230726; - // aapt resource value: 0x7f030007 - public const int notification_template_big_media_narrow = 2130903047; + // aapt resource value: 0x7F080007 + public const int notification_template_big_media_narrow = 2131230727; - // aapt resource value: 0x7f030008 - public const int notification_template_big_media_narrow_custom = 2130903048; + // aapt resource value: 0x7F080008 + public const int notification_template_big_media_narrow_custom = 2131230728; - // aapt resource value: 0x7f030009 - public const int notification_template_custom_big = 2130903049; + // aapt resource value: 0x7F080009 + public const int notification_template_custom_big = 2131230729; - // aapt resource value: 0x7f03000a - public const int notification_template_icon_group = 2130903050; + // aapt resource value: 0x7F08000A + public const int notification_template_icon_group = 2131230730; - // aapt resource value: 0x7f03000b - public const int notification_template_lines_media = 2130903051; + // aapt resource value: 0x7F08000B + public const int notification_template_lines_media = 2131230731; - // aapt resource value: 0x7f03000c - public const int notification_template_media = 2130903052; + // aapt resource value: 0x7F08000C + public const int notification_template_media = 2131230732; - // aapt resource value: 0x7f03000d - public const int notification_template_media_custom = 2130903053; + // aapt resource value: 0x7F08000D + public const int notification_template_media_custom = 2131230733; - // aapt resource value: 0x7f03000e - public const int notification_template_part_chronometer = 2130903054; + // aapt resource value: 0x7F08000E + public const int notification_template_part_chronometer = 2131230734; - // aapt resource value: 0x7f03000f - public const int notification_template_part_time = 2130903055; + // aapt resource value: 0x7F08000F + public const int notification_template_part_time = 2131230735; static Layout() { @@ -450,11 +450,11 @@ namespace SampleApp.Droid public partial class String { - // aapt resource value: 0x7f070001 - public const int app_name = 2131165185; + // aapt resource value: 0x7F090000 + public const int app_name = 2131296256; - // aapt resource value: 0x7f070000 - public const int status_bar_notification_info_overflow = 2131165184; + // aapt resource value: 0x7F090001 + public const int status_bar_notification_info_overflow = 2131296257; static String() { @@ -469,41 +469,41 @@ namespace SampleApp.Droid public partial class Style { - // aapt resource value: 0x7f040005 - public const int TextAppearance_Compat_Notification = 2130968581; + // aapt resource value: 0x7F0A0000 + public const int TextAppearance_Compat_Notification = 2131361792; - // aapt resource value: 0x7f040006 - public const int TextAppearance_Compat_Notification_Info = 2130968582; + // aapt resource value: 0x7F0A0001 + public const int TextAppearance_Compat_Notification_Info = 2131361793; - // aapt resource value: 0x7f040000 - public const int TextAppearance_Compat_Notification_Info_Media = 2130968576; + // aapt resource value: 0x7F0A0002 + public const int TextAppearance_Compat_Notification_Info_Media = 2131361794; - // aapt resource value: 0x7f04000b - public const int TextAppearance_Compat_Notification_Line2 = 2130968587; + // aapt resource value: 0x7F0A0003 + public const int TextAppearance_Compat_Notification_Line2 = 2131361795; - // aapt resource value: 0x7f040004 - public const int TextAppearance_Compat_Notification_Line2_Media = 2130968580; + // aapt resource value: 0x7F0A0004 + public const int TextAppearance_Compat_Notification_Line2_Media = 2131361796; - // aapt resource value: 0x7f040001 - public const int TextAppearance_Compat_Notification_Media = 2130968577; + // aapt resource value: 0x7F0A0005 + public const int TextAppearance_Compat_Notification_Media = 2131361797; - // aapt resource value: 0x7f040007 - public const int TextAppearance_Compat_Notification_Time = 2130968583; + // aapt resource value: 0x7F0A0006 + public const int TextAppearance_Compat_Notification_Time = 2131361798; - // aapt resource value: 0x7f040002 - public const int TextAppearance_Compat_Notification_Time_Media = 2130968578; + // aapt resource value: 0x7F0A0007 + public const int TextAppearance_Compat_Notification_Time_Media = 2131361799; - // aapt resource value: 0x7f040008 - public const int TextAppearance_Compat_Notification_Title = 2130968584; + // aapt resource value: 0x7F0A0008 + public const int TextAppearance_Compat_Notification_Title = 2131361800; - // aapt resource value: 0x7f040003 - public const int TextAppearance_Compat_Notification_Title_Media = 2130968579; + // aapt resource value: 0x7F0A0009 + public const int TextAppearance_Compat_Notification_Title_Media = 2131361801; - // aapt resource value: 0x7f040009 - public const int Widget_Compat_NotificationActionContainer = 2130968585; + // aapt resource value: 0x7F0A000A + public const int Widget_Compat_NotificationActionContainer = 2131361802; - // aapt resource value: 0x7f04000a - public const int Widget_Compat_NotificationActionText = 2130968586; + // aapt resource value: 0x7F0A000B + public const int Widget_Compat_NotificationActionText = 2131361803; static Style() { @@ -518,37 +518,21 @@ namespace SampleApp.Droid public partial class Styleable { + // aapt resource value: { 0x7F010001,0x7F010002,0x7F010003,0x7F010004,0x7F010005,0x7F010006 } public static int[] FontFamily = new int[] { - 2130771968, 2130771969, 2130771970, 2130771971, 2130771972, - 2130771973}; - - // aapt resource value: 0 - public const int FontFamily_fontProviderAuthority = 0; - - // aapt resource value: 3 - public const int FontFamily_fontProviderCerts = 3; - - // aapt resource value: 4 - public const int FontFamily_fontProviderFetchStrategy = 4; - - // aapt resource value: 5 - public const int FontFamily_fontProviderFetchTimeout = 5; - - // aapt resource value: 1 - public const int FontFamily_fontProviderPackage = 1; - - // aapt resource value: 2 - public const int FontFamily_fontProviderQuery = 2; + 2130771973, + 2130771974}; + // aapt resource value: { 0x1010532,0x1010533,0x101053F,0x7F010000,0x7F010007,0x7F010008 } public static int[] FontFamilyFont = new int[] { 16844082, 16844083, 16844095, - 2130771974, + 2130771968, 2130771975, 2130771976}; @@ -561,15 +545,33 @@ namespace SampleApp.Droid // aapt resource value: 1 public const int FontFamilyFont_android_fontWeight = 1; - // aapt resource value: 4 - public const int FontFamilyFont_font = 4; - // aapt resource value: 3 - public const int FontFamilyFont_fontStyle = 3; + public const int FontFamilyFont_font = 3; + + // aapt resource value: 4 + public const int FontFamilyFont_fontStyle = 4; // aapt resource value: 5 public const int FontFamilyFont_fontWeight = 5; + // aapt resource value: 0 + public const int FontFamily_fontProviderAuthority = 0; + + // aapt resource value: 1 + public const int FontFamily_fontProviderCerts = 1; + + // aapt resource value: 2 + public const int FontFamily_fontProviderFetchStrategy = 2; + + // aapt resource value: 3 + public const int FontFamily_fontProviderFetchTimeout = 3; + + // aapt resource value: 4 + public const int FontFamily_fontProviderPackage = 4; + + // aapt resource value: 5 + public const int FontFamily_fontProviderQuery = 5; + static Styleable() { global::Android.Runtime.ResourceIdManager.UpdateIdValues(); diff --git a/src/SampleApp.WinDesktop/MainWindow.xaml.cs b/src/SampleApp.WinDesktop/MainWindow.xaml.cs index 71e24b9..126ffdb 100644 --- a/src/SampleApp.WinDesktop/MainWindow.xaml.cs +++ b/src/SampleApp.WinDesktop/MainWindow.xaml.cs @@ -83,24 +83,21 @@ namespace SampleApp.WinDesktop ((NmeaParser.SerialPortDevice)device).Port.BaudRate); } } - Dictionary> gsvMessages = new Dictionary>(); + Dictionary gsvMessages = new Dictionary(); private void device_MessageReceived(object sender, NmeaParser.NmeaMessageReceivedEventArgs args) { Dispatcher.BeginInvoke((Action) delegate() { - messages.Enqueue(args.Message.MessageType + ": " + args.Message.ToString()); + messages.Enqueue(args.Message.ToString()); if (messages.Count > 100) messages.Dequeue(); //Keep message queue at 100 output.Text = string.Join("\n", messages.ToArray()); output.Select(output.Text.Length - 1, 0); //scroll to bottom - if(args.Message is NmeaParser.Nmea.Gsv gpgsv) + if (args.Message is NmeaParser.Nmea.Gsv gpgsv) { - if (args.IsMultipart && args.MessageParts != null) - { - gsvMessages[args.Message.MessageType] = args.MessageParts.OfType().ToList(); - satView.GsvMessages = gsvMessages.SelectMany(m=>m.Value); - } + gsvMessages[gpgsv.TalkerId] = gpgsv; + satView.GsvMessages = gsvMessages.Values; } else if (args.Message is NmeaParser.Nmea.Rmc) gprmcView.Message = args.Message as NmeaParser.Nmea.Rmc; @@ -112,19 +109,19 @@ namespace SampleApp.WinDesktop gpgllView.Message = args.Message as NmeaParser.Nmea.Gll; else if (args.Message is NmeaParser.Nmea.Garmin.Pgrme) pgrmeView.Message = args.Message as NmeaParser.Nmea.Garmin.Pgrme; - else - { - var ctrl = MessagePanel.Children.OfType().Where(c => c.Message.MessageType == args.Message.MessageType).FirstOrDefault(); - if(ctrl == null) - { - ctrl = new UnknownMessageControl() - { - Style = this.Resources["card"] as Style - }; - MessagePanel.Children.Add(ctrl); - } - ctrl.Message = args.Message; - } + else + { + var ctrl = MessagePanel.Children.OfType().Where(c => c.Message.MessageType == args.Message.MessageType).FirstOrDefault(); + if (ctrl == null) + { + ctrl = new UnknownMessageControl() + { + Style = this.Resources["card"] as Style + }; + MessagePanel.Children.Add(ctrl); + } + ctrl.Message = args.Message; + } }); } diff --git a/src/UnitTests/NmeaParser.Tests/DeviceTests.cs b/src/UnitTests/NmeaParser.Tests/DeviceTests.cs index ae045ea..28be409 100644 --- a/src/UnitTests/NmeaParser.Tests/DeviceTests.cs +++ b/src/UnitTests/NmeaParser.Tests/DeviceTests.cs @@ -25,19 +25,15 @@ namespace NmeaParser.Tests count++; try { - Assert.IsTrue(e.IsMultipart, "IsMultiPart"); - Assert.IsInstanceOfType(e.Message, typeof(NmeaParser.Nmea.Gsv)); + Assert.IsInstanceOfType(e.Message, typeof(Gsv)); var msg = (NmeaParser.Nmea.Gsv)e.Message; - if (msg.TotalMessages == msg.MessageNumber) - { - Assert.IsNotNull(e.MessageParts); - Assert.AreEqual(e.MessageParts.Count, 3, "MessageParts.Length"); - tcs.SetResult(true); - } - else - Assert.IsNull(e.MessageParts); - if (count > 3) + Assert.IsTrue(((IMultiSentenceMessage)e.Message).IsComplete); + Assert.AreEqual(9, msg.SVsInView); + Assert.AreEqual(9, msg.SVs.Count); + + if (count > 1) Assert.Fail(); + tcs.SetResult(true); } catch(System.Exception ex) { @@ -61,40 +57,24 @@ $GLGSV,4,3,14,73,52,022,47,74,62,248,47,72,44,331,42,71,78,111,49*6A $GAGSV,4,4,14,19,82,349,40,1,44,220,40,4,24,314,38*5F"; NmeaDevice dev = new BufferedStringDevice(message); TaskCompletionSource tcs = new TaskCompletionSource(); - int count = 0; - dev.MessageReceived += (s, e) => - { - count++; - try - { - Assert.IsTrue(e.IsMultipart, "IsMultiPart"); - Assert.IsInstanceOfType(e.Message, typeof(NmeaParser.Nmea.Gsv)); - var msg = (NmeaParser.Nmea.Gsv)e.Message; - if (msg.TotalMessages == msg.MessageNumber) - { - Assert.IsNotNull(e.MessageParts); - Assert.AreEqual(e.MessageParts.Count, 4, "MessageParts.Length"); - Assert.IsInstanceOfType(e.MessageParts[0], typeof(NmeaParser.Nmea.Gsv)); - Assert.IsInstanceOfType(e.MessageParts[1], typeof(NmeaParser.Nmea.Gsv)); - Assert.IsInstanceOfType(e.MessageParts[2], typeof(NmeaParser.Nmea.Gsv)); - Assert.IsInstanceOfType(e.MessageParts[3], typeof(NmeaParser.Nmea.Gsv)); - Assert.AreEqual(Talker.GlobalPositioningSystem, e.MessageParts[0].TalkerId); - Assert.AreEqual(Talker.GlobalPositioningSystem, e.MessageParts[1].TalkerId); - Assert.AreEqual(Talker.GlonassReceiver, e.MessageParts[2].TalkerId); - Assert.AreEqual(Talker.GalileoPositioningSystem, e.MessageParts[3].TalkerId); - - tcs.SetResult(true); - } - else - Assert.IsNull(e.MessageParts); - if (count > 3) - Assert.Fail(); - } - catch (System.Exception ex) - { - tcs.SetException(ex); - } - }; + dev.MessageReceived += (s, e) => + { + try + { + Assert.IsInstanceOfType(e.Message, typeof(NmeaParser.Nmea.Gsv)); + Assert.AreEqual(Talker.Multiple, e.Message.TalkerId); + var msg = (NmeaParser.Nmea.Gsv)e.Message; + Assert.AreEqual(Talker.GlobalPositioningSystem, msg.SVs[0].TalkerId); + Assert.AreEqual(Talker.GlobalPositioningSystem, msg.SVs[4].TalkerId); + Assert.AreEqual(Talker.GlonassReceiver, msg.SVs[8].TalkerId); + Assert.AreEqual(Talker.GalileoPositioningSystem, msg.SVs[12].TalkerId); + tcs.SetResult(true); + } + catch (System.Exception ex) + { + tcs.SetException(ex); + } + }; await dev.OpenAsync(); await tcs.Task; var _ = dev.CloseAsync(); @@ -108,27 +88,16 @@ $GAGSV,4,4,14,19,82,349,40,1,44,220,40,4,24,314,38*5F"; var message = "$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4D\n$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F\n$GPGSV,3,3,9,32,10,037,00,,,,,,,,,,,,*74"; NmeaDevice dev = new BufferedStringDevice(message); TaskCompletionSource tcs = new TaskCompletionSource(); - int count = 0; + bool messageRecieved = false; dev.MessageReceived += (s, e) => { - count++; - try - { - Assert.IsTrue(e.IsMultipart, "IsMultiPart"); - Assert.IsInstanceOfType(e.Message, typeof(NmeaParser.Nmea.Gsv)); - var msg = e.Message as NmeaParser.Nmea.Gsv; - Assert.IsNull(e.MessageParts); - if (count > 6) - tcs.SetResult(true); - } - catch (System.Exception ex) - { - tcs.SetException(ex); - } + messageRecieved = true; }; await dev.OpenAsync(); - await tcs.Task; + await Task.Delay(100); var _ = dev.CloseAsync(); + if (messageRecieved) + Assert.Fail("Event shouldn't be raised for incomplete messages"); } } } diff --git a/src/UnitTests/NmeaParser.Tests/NmeaMessages.cs b/src/UnitTests/NmeaParser.Tests/NmeaMessages.cs index 5a46943..eafcaeb 100644 --- a/src/UnitTests/NmeaParser.Tests/NmeaMessages.cs +++ b/src/UnitTests/NmeaParser.Tests/NmeaMessages.cs @@ -302,8 +302,8 @@ namespace NmeaParser.Tests var msg = NmeaMessage.Parse(input); Assert.IsInstanceOfType(msg, typeof(Gsv)); Gsv gsv = (Gsv)msg; - Assert.AreEqual(3, gsv.TotalMessages); - Assert.AreEqual(3, gsv.MessageNumber); + Assert.IsInstanceOfType(msg, typeof(IMultiSentenceMessage)); + Assert.IsFalse(((IMultiSentenceMessage)msg).IsComplete); Assert.AreEqual(11, gsv.SVsInView); Assert.IsNotNull(gsv.SVs); Assert.AreEqual(3, gsv.SVs.Count); @@ -336,13 +336,75 @@ namespace NmeaParser.Tests var msg = NmeaMessage.Parse(input); Assert.IsInstanceOfType(msg, typeof(Gsv)); Gsv gsv = (Gsv)msg; - Assert.AreEqual(1, gsv.TotalMessages); - Assert.AreEqual(1, gsv.MessageNumber); + Assert.IsTrue(((IMultiSentenceMessage)gsv).IsComplete); Assert.AreEqual(0, gsv.SVsInView); Assert.IsNotNull(gsv.SVs); Assert.AreEqual(0, gsv.SVs.Count); } + [TestMethod] + public void TestGpgsv_Multi() + { + var input1 = "$GPGSV,3,1,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4C"; + var input2 = "$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F"; + var input3 = "$GPGSV,3,3,9,32,10,037,00,,,,,,,,,,,,*74"; + var msg1 = NmeaMessage.Parse(input1); + Assert.IsFalse(((IMultiSentenceMessage)msg1).IsComplete); + var msg2 = NmeaMessage.Parse(input2, msg1 as IMultiSentenceMessage); + Assert.IsFalse(((IMultiSentenceMessage)msg2).IsComplete); + var msg3 = NmeaMessage.Parse(input3, msg2 as IMultiSentenceMessage); + Assert.IsTrue(((IMultiSentenceMessage)msg3).IsComplete); + Assert.IsInstanceOfType(msg1, typeof(Gsv)); + Assert.AreSame(msg1, msg2); + Assert.AreSame(msg1, msg3); + Gsv gsv = (Gsv)msg1; + Assert.AreEqual(9, gsv.SVsInView); + Assert.IsNotNull(gsv.SVs); + Assert.AreEqual(9, gsv.SVs.Count); + } + + [TestMethod] + public void TestGpgsv_MultiMissing() + { + var input1 = "$GPGSV,2,1,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4D"; + var input2 = "$GPGSV,2,2,8,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F"; //Satellite count doesn't match, so append will fail + var msg1 = NmeaMessage.Parse(input1); + Assert.IsFalse(((IMultiSentenceMessage)msg1).IsComplete); + var msg2 = NmeaMessage.Parse(input2, msg1 as IMultiSentenceMessage); + Assert.IsFalse(((IMultiSentenceMessage)msg2).IsComplete); + Assert.IsInstanceOfType(msg2, typeof(Gsv)); + Assert.AreNotSame(msg1, msg2); + Gsv gsv1 = (Gsv)msg1; + Assert.AreEqual(9, gsv1.SVsInView); + Assert.IsNotNull(gsv1.SVs); + Assert.AreEqual(4, gsv1.SVs.Count); + Gsv gsv2 = (Gsv)msg2; + Assert.AreEqual(8, gsv2.SVsInView); + Assert.IsNotNull(gsv2.SVs); + Assert.AreEqual(4, gsv2.SVs.Count); + } + + + [TestMethod] + public void TestGpgsv_MultiNotMatching() + { + var input2 = "$GPGSV,3,2,9,00,30,055,48,00,19,281,00,27,19,275,00,12,16,319,00*4F"; + var input3 = "$GPGSV,3,3,9,32,10,037,00,,,,,,,,,,,,*74"; + var msg2 = NmeaMessage.Parse(input2); + Assert.IsFalse(((IMultiSentenceMessage)msg2).IsComplete); + var msg3 = NmeaMessage.Parse(input3, msg2 as IMultiSentenceMessage); + Assert.IsFalse(((IMultiSentenceMessage)msg3).IsComplete); + Assert.IsInstanceOfType(msg2, typeof(Gsv)); + Assert.AreNotSame(msg2, msg3); + Gsv gsv2 = (Gsv)msg2; + Assert.AreEqual(9, gsv2.SVsInView); + Assert.IsNotNull(gsv2.SVs); + Assert.AreEqual(4, gsv2.SVs.Count); + Gsv gsv3 = (Gsv)msg3; + Assert.AreEqual(9, gsv3.SVsInView); + Assert.IsNotNull(gsv3.SVs); + Assert.AreEqual(1, gsv3.SVs.Count); + } [TestMethod] [WorkItem(53)] @@ -352,8 +414,7 @@ namespace NmeaParser.Tests var msg = NmeaMessage.Parse(msgstr); Assert.IsInstanceOfType(msg, typeof(Gsv)); Gsv gsv = (Gsv)msg; - Assert.AreEqual(3, gsv.TotalMessages); - Assert.AreEqual(1, gsv.MessageNumber); + Assert.IsFalse(((IMultiSentenceMessage)gsv).IsComplete); Assert.AreEqual(12, gsv.SVsInView); Assert.IsNotNull(gsv.SVs); Assert.AreEqual(4, gsv.SVs.Count); @@ -581,16 +642,15 @@ namespace NmeaParser.Tests string input = "$GPRTE,2,1,c,0,W3IWI,DRIVWY,32CEDR,32-29,32BKLD,32-I95,32-US1,BW-32,BW-198*69"; var msg = NmeaMessage.Parse(input); Assert.IsInstanceOfType(msg, typeof(Rte)); - Rte gsv = (Rte)msg; - Assert.AreEqual(2, gsv.TotalMessages); - Assert.AreEqual(1, gsv.MessageNumber); - Assert.AreEqual(Rte.WaypointListType.CompleteWaypointsList, gsv.ListType); - Assert.AreEqual("0", gsv.RouteId); - Assert.AreEqual("0", gsv.RouteId); - Assert.AreEqual(9, gsv.Waypoints.Count); - Assert.AreEqual("W3IWI", gsv.Waypoints[0]); - Assert.AreEqual("32BKLD", gsv.Waypoints[4]); - Assert.AreEqual("BW-198", gsv.Waypoints[8]); + Rte rte = (Rte)msg; + Assert.IsFalse(((IMultiSentenceMessage)rte).IsComplete); + Assert.AreEqual(Rte.WaypointListType.CompleteWaypointsList, rte.ListType); + Assert.AreEqual("0", rte.RouteId); + Assert.AreEqual("0", rte.RouteId); + Assert.AreEqual(9, rte.Waypoints.Count); + Assert.AreEqual("W3IWI", rte.Waypoints[0]); + Assert.AreEqual("32BKLD", rte.Waypoints[4]); + Assert.AreEqual("BW-198", rte.Waypoints[8]); } [TestMethod]