diff --git a/Cargo.toml b/Cargo.toml index b7e251e..782e1b8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,6 +20,9 @@ default = ["std"] [dependencies] nom = { version = "7", default-features = false } heapless = { version = "0.7" } +serde = { version = "1.0", features = ["derive"] } +serde_json = "1.0" + [[bin]] name = "aisparser" diff --git a/src/bin/aisparser.rs b/src/bin/aisparser.rs index 614df94..fb3a949 100644 --- a/src/bin/aisparser.rs +++ b/src/bin/aisparser.rs @@ -1,35 +1,41 @@ use ais::lib; +use ais::sentence::{AisFragments, AisParser, AisSentence}; +use lib::std::io::{self, BufRead}; -use ais::sentence::{AisFragments, AisParser}; -use lib::std::io::BufRead; - -use lib::std::io; - -fn parse_nmea_line(parser: &mut AisParser, line: &[u8]) -> Result<(), ais::errors::Error> { +fn parse_nmea_line_to_json(parser: &mut AisParser, line: &[u8]) -> Result<(), ais::errors::Error> { let sentence = parser.parse(line, true)?; if let AisFragments::Complete(sentence) = sentence { - println!( - "{:?}\t{:?}", - lib::std::str::from_utf8(line).unwrap(), - sentence.message - ); + match serialize_to_json(&sentence) { + Ok(json) => println!("{}", json), + Err(err) => eprintln!("Error serializing to JSON: {}", err), + } } Ok(()) } +pub fn serialize_to_json(sentence: &AisSentence) -> serde_json::Result { + serde_json::to_string(sentence) +} + +pub fn deserialize_from_json(json_data: &str) -> serde_json::Result { + serde_json::from_str(json_data) +} + fn main() { let mut parser = AisParser::new(); let stdin = io::stdin(); - { - let handle = stdin.lock(); + let handle = stdin.lock(); - handle - .split(b'\n') - .map(|line| line.unwrap()) - .for_each(|line| { - parse_nmea_line(&mut parser, &line).unwrap_or_else(|err| { - eprintln!("{:?}\t{:?}", lib::std::str::from_utf8(&line).unwrap(), err); - }); + handle + .split(b'\n') + .map(|line| line.unwrap()) + .for_each(|line| { + parse_nmea_line_to_json(&mut parser, &line).unwrap_or_else(|err| { + eprintln!( + "Error parsing line: {:?}\t{:?}", + lib::std::str::from_utf8(&line).unwrap(), + err + ); }); - } + }); } diff --git a/src/lib.rs b/src/lib.rs index 1f6e576..2b35d66 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,39 +1,44 @@ -//! AIS parsing library, for reading AIS NMEA sentences +//! AIS parsing library for reading AIS NMEA sentences, with support for JSON serialization. //! -//! Given an NMEA stream, this library can extract various AIS message types in more detail. +//! This library parses NMEA AIS (Automatic Identification System) sentences and provides +//! structured representations of the data, allowing further processing or analysis. //! -//! # Example: +//! # Features +//! - Parses AIS NMEA sentences into structured types. +//! - Supports JSON serialization and deserialization for `AisSentence` objects. +//! +//! # Example //! ``` -//! use ais::{AisFragments, AisParser}; +//! use ais::{AisFragments, AisParser, serialize_to_json, deserialize_from_json}; //! use ais::messages::AisMessage; //! -//! // The line below is an NMEA sentence, much as you'd see coming out of an AIS decoder. //! let line = b"!AIVDM,1,1,,B,E>kb9O9aS@7PUh10dh19@;0Tah2cWrfP:l?M`00003vP100,0*01"; -//! //! let mut parser = AisParser::new(); -//! if let AisFragments::Complete(sentence) = parser.parse(line, true)? { -//! // This sentence is complete, ie unfragmented +//! +//! if let AisFragments::Complete(sentence) = parser.parse(line, true).unwrap() { //! assert_eq!(sentence.num_fragments, 1); -//! // The data was transmitted on AIS channel B //! assert_eq!(sentence.channel, Some('B')); //! -//! if let Some(message) = sentence.message { +//! if let Some(ref message) = sentence.message { //! match message { //! AisMessage::AidToNavigationReport(report) => { //! assert_eq!(report.mmsi, 993692028); //! assert_eq!(report.name, "SF OAK BAY BR VAIS E"); -//! // There are a ton more fields available here //! }, //! _ => panic!("Unexpected message type"), //! } //! } +//! +//! let json = serialize_to_json(&sentence).unwrap(); +//! let deserialized_sentence = deserialize_from_json(&json).unwrap(); +//! assert_eq!(sentence, deserialized_sentence); //! } -//! # Ok::<(), ais::errors::Error>(()) //! ``` + #![cfg_attr(not(feature = "std"), no_std)] #[doc(hidden)] -/// standard library stuff available crate-wide, regardless of `no_std` state +/// Standard library items, available crate-wide regardless of `no_std` state. pub mod lib { #[cfg(all(not(feature = "std"), not(feature = "alloc")))] pub mod std { @@ -80,6 +85,20 @@ pub mod sentence; pub use errors::Result; pub use sentence::{AisFragments, AisParser}; +use sentence::AisSentence; +use serde_json::Error as SerdeError; + +/// Serializes an `AisSentence` to JSON +#[cfg(any(feature = "std", feature = "alloc"))] +use lib::std::string::String; +pub fn serialize_to_json(sentence: &AisSentence) -> std::result::Result { + serde_json::to_string(sentence) +} +#[cfg(any(feature = "std", feature = "alloc"))] +pub fn deserialize_from_json(json_data: &str) -> std::result::Result { + serde_json::from_str(json_data) +} + #[cfg(test)] mod test_helpers { #[inline] @@ -94,6 +113,7 @@ mod test_helpers { #[cfg(test)] mod tests { use super::*; + use crate::sentence::{AisReportType, AisSentence, TalkerId}; const TEST_MESSAGES: [&[u8]; 8] = [ b"!AIVDM,1,1,,B,E>kb9O9aS@7PUh10dh19@;0Tah2cWrfP:l?M`00003vP100,0*01", @@ -108,9 +128,54 @@ mod tests { #[test] fn end_to_end() { - let mut parser = sentence::AisParser::new(); + let mut parser = AisParser::new(); for line in TEST_MESSAGES.iter() { parser.parse(line, true).unwrap(); } } + + #[test] + fn test_json_serialization() { + let mut parser = AisParser::new(); + let line = b"!AIVDM,1,1,,B,E>kb9O9aS@7PUh10dh19@;0Tah2cWrfP:l?M`00003vP100,0*01"; + + if let AisFragments::Complete(sentence) = parser.parse(line, true).unwrap() { + // Serialize the sentence to JSON + let json = serialize_to_json(&sentence).expect("Failed to serialize to JSON"); + println!("Serialized JSON: {}", json); + + // Deserialize back from JSON + let deserialized_sentence = + deserialize_from_json(&json).expect("Failed to deserialize from JSON"); + + assert_eq!(sentence, deserialized_sentence); + } + } + + #[test] + fn test_serialize_deserialize() { + // Create a sample AisSentence struct + let original_sentence = AisSentence { + message: None, + talker_id: TalkerId::AI, + report_type: AisReportType::VDM, + num_fragments: 1, + fragment_number: 1, + message_id: Some(123), + channel: Some('A'), + data: vec![69, 62, 107, 98, 57, 79], // sample data; replace with real data if needed + fill_bit_count: 0, + message_type: 1, + }; + + // Serialize to JSON + let json_data = serialize_to_json(&original_sentence).expect("Serialization failed"); + + // Deserialize back to an AisSentence + let deserialized_sentence: AisSentence = + deserialize_from_json(&json_data).expect("Deserialization failed"); + + // Check if the deserialized struct matches the original + assert_eq!(original_sentence, deserialized_sentence); + } } diff --git a/src/messages/addressed_safety_related.rs b/src/messages/addressed_safety_related.rs index 188de6e..6c4a243 100644 --- a/src/messages/addressed_safety_related.rs +++ b/src/messages/addressed_safety_related.rs @@ -5,8 +5,9 @@ use crate::errors::Result; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Eq)] pub struct AddressedSafetyRelatedMessage { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/aid_to_navigation_report.rs b/src/messages/aid_to_navigation_report.rs index eb3dd86..82ca126 100644 --- a/src/messages/aid_to_navigation_report.rs +++ b/src/messages/aid_to_navigation_report.rs @@ -7,8 +7,9 @@ use crate::errors::Result; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum NavaidType { ReferencePoint, Racon, @@ -84,7 +85,7 @@ impl NavaidType { } } -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct AidToNavigationReport { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/assignment_mode_command.rs b/src/messages/assignment_mode_command.rs index 00dfaf4..c19972d 100644 --- a/src/messages/assignment_mode_command.rs +++ b/src/messages/assignment_mode_command.rs @@ -5,8 +5,9 @@ use super::AisMessageType; use crate::errors::Result; use nom::bits::{bits, complete::take as take_bits}; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct AssignmentModeCommand { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/base_station_report.rs b/src/messages/base_station_report.rs index d76dc72..a5ad740 100644 --- a/src/messages/base_station_report.rs +++ b/src/messages/base_station_report.rs @@ -8,8 +8,9 @@ use crate::errors::Result; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct BaseStationReport { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/binary_acknowledge.rs b/src/messages/binary_acknowledge.rs index a2fe0e9..5eca9f0 100644 --- a/src/messages/binary_acknowledge.rs +++ b/src/messages/binary_acknowledge.rs @@ -1,15 +1,16 @@ //! Binary Acknowledge (type 7) -#[cfg(all(not(feature = "std"), not(feature = "alloc")))] - use super::nom_noalloc::many_m_n; +#[cfg(all(not(feature = "std"), not(feature = "alloc")))] +use super::nom_noalloc::many_m_n; use super::AisMessageType; use crate::errors::Result; use crate::lib; use nom::bits::{bits, complete::take as take_bits}; -#[cfg(any(feature = "std", feature = "alloc"))] +#[cfg(any(feature = "std", feature = "alloc"))] use nom::multi::many_m_n; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Acknowledgement { pub mmsi: u32, pub seq_num: u8, @@ -28,7 +29,7 @@ pub type AcknowledgementList = lib::std::vec::Vec; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] pub type AcknowledgementList = lib::std::vec::Vec; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct BinaryAcknowledge { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/binary_addressed.rs b/src/messages/binary_addressed.rs index 03da4aa..c3ca503 100644 --- a/src/messages/binary_addressed.rs +++ b/src/messages/binary_addressed.rs @@ -6,6 +6,7 @@ use crate::lib; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] const MAX_DATA_SIZE_BYTES: usize = 119; @@ -15,7 +16,7 @@ pub type MessageData = lib::std::vec::Vec; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] pub type MessageData = lib::std::vec::Vec; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Eq)] pub struct BinaryAddressedMessage { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/binary_broadcast_message.rs b/src/messages/binary_broadcast_message.rs index 3e8af47..97a86db 100644 --- a/src/messages/binary_broadcast_message.rs +++ b/src/messages/binary_broadcast_message.rs @@ -4,6 +4,7 @@ use crate::errors::Result; use crate::lib; use nom::bits::{bits, complete::take as take_bits}; use nom::IResult; +use serde::{Deserialize, Serialize}; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] const MAX_DATA_SIZE_BYTES: usize = 119; @@ -13,7 +14,7 @@ pub type MessageData = lib::std::vec::Vec; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] pub type MessageData = lib::std::vec::Vec; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct BinaryBroadcastMessage { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/data_link_management_message.rs b/src/messages/data_link_management_message.rs index 0bad919..d6b35d7 100644 --- a/src/messages/data_link_management_message.rs +++ b/src/messages/data_link_management_message.rs @@ -8,8 +8,9 @@ use nom::bits::{bits, complete::take as take_bits}; #[cfg(any(feature = "std", feature = "alloc"))] use nom::multi::many_m_n; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SlotReservation { pub offset: u16, pub num_slots: u8, @@ -40,7 +41,7 @@ pub type SlotReservationList = lib::std::vec::Vec; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] pub type SlotReservationList = lib::std::vec::Vec; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct DataLinkManagementMessage { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/dgnss_broadcast_binary_message.rs b/src/messages/dgnss_broadcast_binary_message.rs index 2ceb346..fc099af 100644 --- a/src/messages/dgnss_broadcast_binary_message.rs +++ b/src/messages/dgnss_broadcast_binary_message.rs @@ -6,6 +6,7 @@ use crate::lib; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] const MAX_DATA_SIZE_BYTES: usize = 119; @@ -15,7 +16,7 @@ pub type CorrectionData = lib::std::vec::Vec; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] pub type CorrectionData = lib::std::vec::Vec; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct DgnssBroadcastBinaryMessage { pub message_type: u8, pub repeat_indicator: u8, @@ -25,7 +26,7 @@ pub struct DgnssBroadcastBinaryMessage { pub payload: DifferentialCorrectionData, } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct DifferentialCorrectionData { pub message_type: u8, pub station_id: u16, diff --git a/src/messages/extended_class_b_position_report.rs b/src/messages/extended_class_b_position_report.rs index d7bd68c..d8ba304 100644 --- a/src/messages/extended_class_b_position_report.rs +++ b/src/messages/extended_class_b_position_report.rs @@ -10,8 +10,9 @@ use crate::messages::types::ShipType; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct ExtendedClassBPositionReport { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/interrogation.rs b/src/messages/interrogation.rs index 6644512..dfdf028 100644 --- a/src/messages/interrogation.rs +++ b/src/messages/interrogation.rs @@ -6,8 +6,9 @@ use crate::errors::Result; use crate::lib; use nom::bits::{bits, complete::take as take_bits}; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Message { pub message_type: u8, pub slot_offset: Option, @@ -41,7 +42,7 @@ pub type MessageList = lib::std::vec::Vec; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] pub type MessageList = lib::std::vec::Vec; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Station { pub mmsi: u32, pub messages: MessageList, @@ -73,7 +74,7 @@ pub type StationList = lib::std::vec::Vec; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] pub type StationList = lib::std::vec::Vec; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Interrogation { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/long_range_ais_broadcast.rs b/src/messages/long_range_ais_broadcast.rs index dccafe3..6988b94 100644 --- a/src/messages/long_range_ais_broadcast.rs +++ b/src/messages/long_range_ais_broadcast.rs @@ -7,8 +7,9 @@ use crate::errors::Result; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct LongRangeAisBroadcastMessage { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/mod.rs b/src/messages/mod.rs index f7cf43b..fca0529 100644 --- a/src/messages/mod.rs +++ b/src/messages/mod.rs @@ -2,11 +2,13 @@ use crate::errors::Result; use crate::lib; use crate::sentence::AisRawData; - +use serde::{Deserialize, Serialize}; +#[cfg(feature = "std")] pub mod addressed_safety_related; pub mod aid_to_navigation_report; pub mod assignment_mode_command; pub mod base_station_report; +pub mod binary_acknowledge; pub mod binary_addressed; pub mod binary_broadcast_message; pub mod data_link_management_message; @@ -29,7 +31,6 @@ pub mod static_data_report; pub mod types; pub mod utc_date_inquiry; pub mod utc_date_response; -pub mod binary_acknowledge; pub use parsers::message_type; @@ -37,7 +38,7 @@ pub use parsers::message_type; use crate::lib::std::{format, vec, vec::Vec}; /// Contains all structured messages recognized by this crate -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub enum AisMessage { PositionReport(position_report::PositionReport), BaseStationReport(base_station_report::BaseStationReport), diff --git a/src/messages/navigation.rs b/src/messages/navigation.rs index 4c9439c..932eef5 100644 --- a/src/messages/navigation.rs +++ b/src/messages/navigation.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + pub fn parse_speed_over_ground(data: u16) -> Option { match data { 1023 => None, @@ -33,7 +35,7 @@ pub fn parse_heading(data: u16) -> Option { } } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub enum Accuracy { Unaugmented, Dgps, @@ -49,7 +51,7 @@ impl Accuracy { } } -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct RateOfTurn { raw: i8, } @@ -88,7 +90,7 @@ impl RateOfTurn { } } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub enum ManeuverIndicator { NoSpecialManeuver, SpecialManeuver, diff --git a/src/messages/position_report.rs b/src/messages/position_report.rs index e6d4f80..591d76b 100644 --- a/src/messages/position_report.rs +++ b/src/messages/position_report.rs @@ -7,8 +7,9 @@ use crate::errors::Result; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct PositionReport { pub message_type: u8, pub repeat_indicator: u8, @@ -79,7 +80,7 @@ fn parse_base(data: &[u8]) -> IResult<&[u8], PositionReport> { })(data) } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub enum NavigationStatus { UnderWayUsingEngine, AtAnchor, diff --git a/src/messages/radio_status.rs b/src/messages/radio_status.rs index 129897a..09cb6f9 100644 --- a/src/messages/radio_status.rs +++ b/src/messages/radio_status.rs @@ -3,14 +3,15 @@ use nom::bits::complete::take as take_bits; use nom::combinator::map; use nom::error::ErrorKind; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum RadioStatus { Sotdma(SotdmaMessage), Itdma(ItdmaMessage), } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub enum SyncState { UtcDirect, UtcIndirect, @@ -32,7 +33,7 @@ impl SyncState { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum SubMessage { SlotOffset(i16), UtcHourAndMinute(u8, u8), @@ -75,7 +76,7 @@ impl SubMessage { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SotdmaMessage { pub sync_state: SyncState, pub slot_timeout: u8, @@ -98,7 +99,7 @@ impl SotdmaMessage { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct ItdmaMessage { pub sync_state: SyncState, pub slot_increment: i16, diff --git a/src/messages/safety_related_acknowledgment.rs b/src/messages/safety_related_acknowledgment.rs index 6602008..a54b946 100644 --- a/src/messages/safety_related_acknowledgment.rs +++ b/src/messages/safety_related_acknowledgment.rs @@ -8,8 +8,9 @@ use nom::bits::{bits, complete::take as take_bits}; #[cfg(any(feature = "std", feature = "alloc"))] use nom::multi::many_m_n; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct Acknowledgement { pub mmsi: u32, pub seq_num: u8, @@ -28,7 +29,7 @@ pub type AcknowledgementList = lib::std::vec::Vec; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] pub type AcknowledgementList = lib::std::vec::Vec; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SafetyRelatedAcknowledge { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/safety_related_broadcast.rs b/src/messages/safety_related_broadcast.rs index 979b8ea..0cac1c6 100644 --- a/src/messages/safety_related_broadcast.rs +++ b/src/messages/safety_related_broadcast.rs @@ -5,8 +5,9 @@ use super::AisMessageType; use crate::errors::Result; use nom::bits::{bits, complete::take as take_bits}; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize, Eq)] pub struct SafetyRelatedBroadcastMessage { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/standard_aircraft_position_report.rs b/src/messages/standard_aircraft_position_report.rs index b023e89..396f633 100644 --- a/src/messages/standard_aircraft_position_report.rs +++ b/src/messages/standard_aircraft_position_report.rs @@ -10,8 +10,9 @@ use crate::errors::Result; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct SARPositionReport { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/standard_class_b_position_report.rs b/src/messages/standard_class_b_position_report.rs index 6352afc..ccd0e1a 100644 --- a/src/messages/standard_class_b_position_report.rs +++ b/src/messages/standard_class_b_position_report.rs @@ -8,8 +8,9 @@ use crate::errors::Result; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct StandardClassBPositionReport { pub message_type: u8, pub repeat_indicator: u8, @@ -42,7 +43,7 @@ impl<'a> AisMessageType<'a> for StandardClassBPositionReport { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum CarrierSense { /// Class B SOTDMA unit Sotdma, diff --git a/src/messages/static_and_voyage_related_data.rs b/src/messages/static_and_voyage_related_data.rs index bbe692d..e277e3d 100644 --- a/src/messages/static_and_voyage_related_data.rs +++ b/src/messages/static_and_voyage_related_data.rs @@ -7,8 +7,9 @@ use crate::lib; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct StaticAndVoyageRelatedData { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/static_data_report.rs b/src/messages/static_data_report.rs index 06f3def..af7dc15 100644 --- a/src/messages/static_data_report.rs +++ b/src/messages/static_data_report.rs @@ -7,8 +7,9 @@ use crate::lib; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct StaticDataReport { pub message_type: u8, pub repeat_indicator: u8, @@ -27,7 +28,7 @@ impl<'a> AisMessageType<'a> for StaticDataReport { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] /// Static Data Report messages have two different sub-message types. /// The idea is that both get broadcast periodically. pub enum MessagePart { diff --git a/src/messages/types.rs b/src/messages/types.rs index 685afae..e23f2c8 100644 --- a/src/messages/types.rs +++ b/src/messages/types.rs @@ -1,9 +1,11 @@ //! Common data types +use serde::{Deserialize, Serialize}; + /// Electronic Position Fixing Device type. This is the /// type of device used for determining the object's /// position. -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub enum EpfdType { Gps, Glonass, @@ -34,7 +36,7 @@ impl EpfdType { } } -#[derive(Debug, PartialEq, Eq, Copy, Clone)] +#[derive(Debug, PartialEq, Eq, Copy, Clone, Serialize, Deserialize)] pub enum ShipType { Reserved(u8), WingInGround, @@ -240,7 +242,7 @@ impl From for u8 { } } -#[derive(Default, Debug, PartialEq, Eq)] +#[derive(Default, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum Dte { Ready, #[default] @@ -257,7 +259,7 @@ impl From for Dte { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum AssignedMode { Autonomous, Assigned, diff --git a/src/messages/utc_date_inquiry.rs b/src/messages/utc_date_inquiry.rs index c3c155a..89fc1ca 100644 --- a/src/messages/utc_date_inquiry.rs +++ b/src/messages/utc_date_inquiry.rs @@ -3,8 +3,9 @@ use super::AisMessageType; use crate::errors::Result; use nom::bits::{bits, complete::take as take_bits}; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq, Eq)] +#[derive(Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct UtcDateInquiry { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/messages/utc_date_response.rs b/src/messages/utc_date_response.rs index 4ad9f44..00cf65c 100644 --- a/src/messages/utc_date_response.rs +++ b/src/messages/utc_date_response.rs @@ -8,8 +8,9 @@ use crate::errors::Result; use nom::bits::{bits, complete::take as take_bits}; use nom::combinator::map; use nom::IResult; +use serde::{Deserialize, Serialize}; -#[derive(Debug, PartialEq)] +#[derive(Debug, PartialEq, Serialize, Deserialize)] pub struct UtcDateResponse { pub message_type: u8, pub repeat_indicator: u8, diff --git a/src/sentence.rs b/src/sentence.rs index 8ad2169..d56b20f 100644 --- a/src/sentence.rs +++ b/src/sentence.rs @@ -11,6 +11,7 @@ use nom::combinator::{map, map_res, opt, peek, verify}; use nom::number::complete::hex_u32; use nom::sequence::{delimited, terminated}; use nom::IResult; +use serde::{Deserialize, Serialize}; pub const MAX_SENTENCE_SIZE_BYTES: usize = 384; @@ -19,7 +20,7 @@ pub type AisRawData = lib::std::vec::Vec; #[cfg(all(not(feature = "std"), not(feature = "alloc")))] pub type AisRawData = lib::std::vec::Vec; -#[derive(PartialEq, Eq, Debug)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] /// Represents the NMEA sentence type of an AIS message pub enum AisReportType { /// Report from another ship @@ -41,7 +42,7 @@ impl<'a> From<&'a [u8]> for AisReportType { } /// Talker ID for the AIS station -#[derive(PartialEq, Eq, Debug)] +#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] pub enum TalkerId { /// NMEA 4.0 Base AIS station AB, @@ -84,7 +85,7 @@ impl<'a> From<&'a [u8]> for TalkerId { } } -#[derive(Debug, PartialEq)] +#[derive(Serialize, Deserialize, PartialEq, Debug)] pub enum AisFragments { Complete(AisSentence), Incomplete(AisSentence), @@ -108,7 +109,7 @@ impl From for Result { } } -#[derive(Debug, Default)] +#[derive(Serialize, Deserialize, PartialEq, Debug)] pub struct AisParser { message_id: Option, fragment_number: u8, @@ -118,7 +119,11 @@ pub struct AisParser { impl AisParser { /// Creates a new `AisParser` instance pub fn new() -> Self { - Self::default() + Self { + message_id: None, + fragment_number: 0, + data: AisRawData::default(), + } } /// Parses `line` as an NMEA sentence, checking the checksum and returning an @@ -185,7 +190,13 @@ impl AisParser { } } -#[derive(Debug, PartialEq)] +impl Default for AisParser { + fn default() -> Self { + Self::new() + } +} + +#[derive(Serialize, Deserialize, PartialEq, Debug)] /// Represents an NMEA sentence parsed as AIS pub struct AisSentence { pub talker_id: TalkerId,