diff --git a/CHANGELOG.md b/CHANGELOG.md index 5dde05b..fc59849 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,12 @@ Changelog ========= +Version 3.1.0 +------------- + +* Added `CustomChecksumSentence` and changed parent of `CustomSentence` to `NmeaSentence`. +* Added `NmeaSentenceType.custom` for custom sentences. + Version 3.0.0 ------------- diff --git a/example/example.dart b/example/example.dart index 0dca4af..dc6ba68 100644 --- a/example/example.dart +++ b/example/example.dart @@ -16,7 +16,7 @@ void main() { MsgSentence.id, (line) => MsgSentence(raw: line)) ..registerProprietarySentence(AcmeProprietarySentence.id, (line) => AcmeProprietarySentence(raw: line)) - ..registerCustomSentence(MyCustomSentence.id, + ..registerCustomChecksumSentence(MyCustomSentence.id, (line) => MyCustomSentence(raw: line, validateChecksums: false))) .listen((nmea.NmeaSentence sentence) { print("${sentence.raw} is a valid ${sentence.type.name} sentence"); @@ -25,7 +25,7 @@ void main() { // Output: // $--MSG,A,1,0,0,0,0*29 is a valid talker sentence // $PACME{'test':true} is a valid proprietary sentence - // $CST,first,second is a valid unknown sentence + // $CST,first,second is a valid custom sentence } class MsgSentence extends nmea.TalkerSentence { @@ -55,7 +55,7 @@ class AcmeProprietarySentence extends nmea.ProprietarySentence { dynamic get data => jsonDecode(json); } -class MyCustomSentence extends nmea.CustomSentence { +class MyCustomSentence extends nmea.CustomChecksumSentence { static const String id = "CST"; MyCustomSentence({required super.raw, super.validateChecksums = true}) diff --git a/lib/nmea.dart b/lib/nmea.dart index 3fc50b3..80dc7bb 100644 --- a/lib/nmea.dart +++ b/lib/nmea.dart @@ -12,6 +12,7 @@ export 'src/nmea_sentence.dart' show NmeaSentence, nmeaPrefix, nmeaSuffix, nmeaFieldSeparator; export 'src/proprietary_sentence.dart' show ProprietarySentence, nmeaProprietaryDenominator, nmeaProprietaryPrefix; +export 'src/custom_checksum_sentence.dart' show CustomChecksumSentence; export 'src/custom_sentence.dart' show CustomSentence; export 'src/talker_sentence.dart' show TalkerSentence; export 'src/multipart_sentence.dart' show MultipartSentence; diff --git a/lib/src/custom_checksum_sentence.dart b/lib/src/custom_checksum_sentence.dart new file mode 100644 index 0000000..1988c30 --- /dev/null +++ b/lib/src/custom_checksum_sentence.dart @@ -0,0 +1,26 @@ +import 'package:nmea/src/checksum_sentence.dart'; +import 'package:nmea/src/nmea_sentence_type.dart'; + +/// A [ChecksumSentence] where the data format is completely customizable, as +/// long as it fits in the general envelope of an NMEA0183 sentence (begins with +/// '$' and ends with windows-style newline characters) and has a checksum. +class CustomChecksumSentence extends ChecksumSentence { + /// Returns the identifier used to identify this sentence type + final String identifier; + + /// Decides whether or not to validate the checksum on this sentence + final bool validateChecksums; + + /// The [CustomChecksumSentence] constructor forwards all arguments to the + /// [ChecksumSentence] constructor, except the [type] parameter. + /// The [type] is always [NmeaSentenceType.custom]. + CustomChecksumSentence( + {required this.identifier, + required super.raw, + super.prefix, + this.validateChecksums = true}) + : super(type: NmeaSentenceType.custom); + + @override + bool get hasValidChecksum => super.hasValidChecksum || !validateChecksums; +} diff --git a/lib/src/custom_sentence.dart b/lib/src/custom_sentence.dart index 8e7b502..71e529a 100644 --- a/lib/src/custom_sentence.dart +++ b/lib/src/custom_sentence.dart @@ -1,21 +1,15 @@ -import 'package:nmea/src/checksum_sentence.dart'; -import 'package:nmea/src/nmea_sentence_type.dart'; +import 'package:nmea/nmea.dart'; -/// The data format is completely customizable, as long as it fits in the general -/// envelope of an NMEA0183 sentence (begins with '$' and ends with windows- -/// style newline characters). -class CustomSentence extends ChecksumSentence { +/// An [NmeaSentence] where the data format is completely customizable, as +/// long as it fits in the general envelope of an NMEA0183 sentence (begins with +/// '$' and ends with windows-style newline characters). +class CustomSentence extends NmeaSentence { /// Returns the identifier used to identify this sentence type final String identifier; - final bool validateChecksums; - CustomSentence( - {required this.identifier, - required super.raw, - super.prefix, - this.validateChecksums = true}) - : super(type: NmeaSentenceType.unknown); - - @override - bool get hasValidChecksum => super.hasValidChecksum || !validateChecksums; + /// The [CustomChecksumSentence] constructor forwards all arguments to the + /// [NmeaSentence] constructor, except the [type] parameter. + /// The [type] is always [NmeaSentenceType.custom]. + CustomSentence({required this.identifier, required super.raw, super.prefix}) + : super(type: NmeaSentenceType.custom); } diff --git a/lib/src/nmea_decoder.dart b/lib/src/nmea_decoder.dart index 4aecaed..a4ac4e5 100644 --- a/lib/src/nmea_decoder.dart +++ b/lib/src/nmea_decoder.dart @@ -1,6 +1,7 @@ import 'dart:async'; import 'dart:convert'; +import 'package:nmea/src/custom_checksum_sentence.dart'; import 'package:nmea/src/custom_sentence.dart'; import 'package:nmea/src/limited_size_queue.dart'; import 'package:nmea/src/multipart_sentence.dart'; @@ -13,6 +14,11 @@ import 'package:nmea/src/talker_sentence.dart'; /// identifier. typedef CustomSentenceFactory = CustomSentence Function(String line); +/// A function to create a [CustomChecksumSentence] from a raw string and an +/// identifier. +typedef CustomChecksumSentenceFactory = CustomChecksumSentence Function( + String line); + /// A function to create a [ProprietarySentence] from a raw string and a /// manufacturer id. typedef ProprietarySentenceFactory = ProprietarySentence Function(String line); @@ -46,6 +52,8 @@ typedef OnIncompleteMultipartSentence = MultipartSentence? Function( /// data it receives as a complete NMEA sentence. class NmeaDecoder extends StreamTransformerBase { final Map _customGenerators = {}; + final Map _customChecksumGenerators = + {}; final Map _proprietaryGenerators = {}; final Map _talkerGenerators = {}; @@ -95,6 +103,14 @@ class NmeaDecoder extends StreamTransformerBase { _customGenerators[identifier] = factory; } + /// Registers a [CustomChecksumSentenceFactory] for a given identifier. + void registerCustomChecksumSentence( + String identifier, + CustomChecksumSentenceFactory factory, + ) { + _customChecksumGenerators[identifier] = factory; + } + /// Registers a [ProprietarySentenceFactory] for a given manufacturer id. void registerProprietarySentence( String manufacturer, @@ -174,7 +190,9 @@ class NmeaDecoder extends StreamTransformerBase { return decodeQuery(line); } - return decodeTalker(line) ?? decodeCustom(line); + return decodeTalker(line) ?? + decodeCustomChecksum(line) ?? + decodeCustom(line); } /// Tries to decode the given line as a custom sentence. @@ -191,6 +209,20 @@ class NmeaDecoder extends StreamTransformerBase { return null; } + /// Tries to decode the given line as a custom sentence with a checksum. + /// The identifier is extracted from the line and the corresponding + /// [CustomChecksumSentenceFactory] is used to create the sentence. + /// If none is found `null` is returned. + CustomChecksumSentence? decodeCustomChecksum(String line) { + for (final identifier in _customChecksumGenerators.keys) { + if (line.startsWith(nmeaPrefix + identifier)) { + return _customChecksumGenerators[identifier]!(line); + } + } + + return null; + } + /// Tries to decode the given line as a proprietary sentence. /// The manufacturer id is extracted from the line and the corresponding /// [ProprietarySentenceFactory] is used to create the sentence. diff --git a/lib/src/nmea_sentence_type.dart b/lib/src/nmea_sentence_type.dart index b457e6e..db90181 100644 --- a/lib/src/nmea_sentence_type.dart +++ b/lib/src/nmea_sentence_type.dart @@ -19,4 +19,8 @@ enum NmeaSentenceType { /// This sentence is a proprietary sentence, whose format or function is not /// further specified (see [ProprietarySentence]). proprietary, + + /// This sentence is a completely custom sentence which might contain + /// unexpected data (see [CustomSentence] and [CustomChecksumSentence]). + custom, } diff --git a/pubspec.yaml b/pubspec.yaml index 3b9d29b..d8adc19 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -4,7 +4,7 @@ homepage: https://github.com/ricardoboss/dart_nmea repository: https://github.com/ricardoboss/dart_nmea issue_tracker: https://github.com/ricardoboss/dart_nmea/issues -version: 3.0.0 +version: 3.1.0 environment: sdk: ">=2.17.1 <3.0.0" diff --git a/test/nmea_decoder_test.dart b/test/nmea_decoder_test.dart index d20160a..ff9b6e1 100644 --- a/test/nmea_decoder_test.dart +++ b/test/nmea_decoder_test.dart @@ -21,9 +21,9 @@ void main() { test("decodes custom sentences with invalid checksums", () { final decoder = NmeaDecoder() - ..registerCustomSentence( + ..registerCustomChecksumSentence( TestCustomSentence.id, (line) => TestCustomSentence(raw: line)); - final decoded = decoder.decodeCustom("\$CST,123,345*56"); + final decoded = decoder.decodeCustomChecksum("\$CST,123,345*56"); expect(decoded, isNotNull); expect(decoded!.fields, equals(["CST", "123", "345"])); @@ -36,9 +36,9 @@ void main() { test("decodes custom sentences with valid checksums", () { final decoder = NmeaDecoder() - ..registerCustomSentence( + ..registerCustomChecksumSentence( TestCustomSentence.id, (line) => TestCustomSentence(raw: line)); - final decoded = decoder.decodeCustom("\$CST,123,345*46"); + final decoded = decoder.decodeCustomChecksum("\$CST,123,345*46"); expect(decoded, isNotNull); expect(decoded!.fields, equals(["CST", "123", "345"])); @@ -51,9 +51,9 @@ void main() { test("decodes custom sentences with skipped checksums", () { final decoder = NmeaDecoder() - ..registerCustomSentence(TestCustomSentence.id, + ..registerCustomChecksumSentence(TestCustomSentence.id, (line) => TestCustomSentence(raw: line, validateChecksums: false)); - final decoded = decoder.decodeCustom("\$CST,123,345*56"); + final decoded = decoder.decodeCustomChecksum("\$CST,123,345*56"); expect(decoded, isNotNull); expect(decoded!.fields, equals(["CST", "123", "345"])); @@ -67,9 +67,9 @@ void main() { test("decodes invalid custom sentences although checksum checks are skipped", () { final decoder = NmeaDecoder() - ..registerCustomSentence(TestCustomSentence.id, + ..registerCustomChecksumSentence(TestCustomSentence.id, (line) => TestCustomSentence(raw: line, validateChecksums: false)); - final decoded = decoder.decodeCustom("\$CST,123345*56"); + final decoded = decoder.decodeCustomChecksum("\$CST,123345*56"); expect(decoded, isNotNull); expect(decoded!.fields, equals(["CST", "123345"])); @@ -106,7 +106,7 @@ class TestTalkerSentence extends TalkerSentence { TestTalkerSentence({required super.raw}); } -class TestCustomSentence extends CustomSentence { +class TestCustomSentence extends CustomChecksumSentence { static const String id = "CST"; TestCustomSentence({required super.raw, super.validateChecksums = true}) : super(identifier: id);