/// <summary> /// A standard filter rule. When the filter matches /// </summary> /// <param name="sourceName">Name of the source (Nmea stream name) for which the filter applies or * for all</param> /// <param name="talkerId">TalkerId for which the rule applies or <see cref="TalkerId.Any"/></param> /// <param name="sentenceId">SentenceId for which the rule applies or <see cref="SentenceId.Any"/></param> /// <param name="sinks">Where to send the message when the filter matches</param> /// <param name="rawMessagesOnly">The filter matches raw messages only. This is the default, because otherwise known message /// types would be implicitly duplicated on forwarding</param> /// <param name="continueAfterMatch">True to continue processing after a match, false to stop processing this message</param> public FilterRule(string sourceName, TalkerId talkerId, SentenceId sentenceId, IEnumerable <string> sinks, bool rawMessagesOnly = true, bool continueAfterMatch = false) { _rawMessagesOnly = rawMessagesOnly; SourceName = sourceName; TalkerId = talkerId; SentenceId = sentenceId; Sinks = sinks; ContinueAfterMatch = continueAfterMatch; }
/// <summary> /// Reads NMEA0183 query sentence from provided string /// </summary> /// <param name="sentence">NMEA0183 query sentence</param> /// <returns>QuerySentence instance</returns> /// <remarks><paramref name="sentence"/> does not include new line characters</remarks> public static QuerySentence FromSentenceString(string sentence) { // http://www.tronico.fi/OH6NT/docs/NMEA0183.pdf // page 3 // $XXYYY ... const int SentenceLength = 6; if (sentence == null) { throw new ArgumentNullException(nameof(sentence)); } if (sentence.Length != SentenceLength) { throw new ArgumentException($"Sentence length must be exactly {SentenceLength} characters", nameof(sentence)); } if (sentence[0] != '$') { throw new ArgumentException("Sentence must start with '$'", nameof(sentence)); } TalkerId requesterId = new TalkerId(sentence[1], sentence[2]); TalkerId deviceId = new TalkerId(sentence[3], sentence[4]); if (sentence[5] != 'Q') { throw new ArgumentException("Valid query sentence must have character 'Q' at index 5"); } if (sentence[6] != ',') { throw new ArgumentException("Valid query sentence must have character ',' at index 6"); } SentenceId requestedSentence = new SentenceId(sentence[7], sentence[8], sentence[9]); return(new QuerySentence(requesterId, deviceId, requestedSentence)); }
/// <summary> /// Constructs NMEA0183 talker identifier /// </summary> /// <param name="talkerId">NMEA0183 talker identifier of the device sending the sentence</param> /// <param name="sentenceId">NMEA0183 sentence identifier</param> /// <param name="fields">fields related to the sentence</param> public TalkerSentence(TalkerId talkerId, SentenceId sentenceId, IEnumerable <string> fields) { TalkerId = talkerId; Id = sentenceId; _fields = fields.ToArray(); }
/// <summary> /// Reads NMEA0183 talker sentence from provided string /// </summary> /// <param name="sentence">NMEA0183 talker sentence</param> /// <param name="expectedTalkerId">If this is not TalkerId.Any, only messages with this talker id are parsed, /// all others are ignored. This reduces workload if a source acts as repeater, but the repeated messages are not needed.</param> /// <param name="errorCode">Returns an error code, if the parsing failed</param> /// <returns>TalkerSentence instance, or null in case of an error</returns> /// <remarks><paramref name="sentence"/> does not include new line characters</remarks> public static TalkerSentence?FromSentenceString(string sentence, TalkerId expectedTalkerId, out NmeaError errorCode) { // $XXY, ... const int sentenceHeaderMinLength = 4; // http://www.tronico.fi/OH6NT/docs/NMEA0183.pdf page 2 // defines this as 80 + 1 (for $), but we don't really care if it is something within a reasonable limit. const int MaxSentenceLength = 256; if (sentence == null) { throw new ArgumentNullException(nameof(sentence)); } if (sentence.Length < sentenceHeaderMinLength) { errorCode = NmeaError.MessageToShort; return(null); } if (sentence.Length > MaxSentenceLength) { errorCode = NmeaError.MessageToLong; return(null); } if (sentence[0] != '$' && sentence[0] != '!') { // Valid sentences start with $ or ! (for the AIS sentences) errorCode = NmeaError.NoSyncByte; return(null); } TalkerId talkerId = new TalkerId(sentence[1], sentence[2]); if (expectedTalkerId != TalkerId.Any && expectedTalkerId != talkerId) { errorCode = NmeaError.None; return(null); } int firstComma = sentence.IndexOf(',', 1); if (firstComma == -1) { errorCode = NmeaError.MessageToShort; return(null); } string sentenceIdString = sentence.Substring(3, firstComma - 3); SentenceId sentenceId = new SentenceId(sentenceIdString); string[] fields = sentence.Substring(firstComma + 1).Split(','); int lastFieldIdx = fields.Length - 1; // This returns null as the checksum if there was none, or a very big number if the checksum couldn't be parsed (int?checksum, string lastField) = GetChecksumAndLastField(fields[lastFieldIdx]); fields[lastFieldIdx] = lastField; TalkerSentence result = new TalkerSentence(talkerId, sentenceId, fields); if (checksum.HasValue) { byte realChecksum = CalculateChecksumFromSentenceString(sentence.AsSpan()); if (realChecksum != checksum.Value) { errorCode = NmeaError.InvalidChecksum; return(null); } } errorCode = NmeaError.None; return(result); }
/// <summary> /// Constructs NMEA0183 query sentence /// </summary> /// <param name="requesterId">Talker identifier of the requester</param> /// <param name="deviceId">Talker identifier of the device</param> /// <param name="requestedSentence">Requested sentence</param> public QuerySentence(TalkerId requesterId, TalkerId deviceId, SentenceId requestedSentence) { RequesterId = requesterId; DeviceId = deviceId; RequestedSentence = requestedSentence; }