diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1568766..75c46bc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -4,7 +4,7 @@ on: push: pull_request: workflow_dispatch: - + jobs: test: name: ${{ matrix.os }} / ${{ matrix.rust }} (${{ matrix.feature }}) @@ -19,18 +19,15 @@ jobs: RUST_BACKTRACE: full RUSTV: ${{ matrix.rust }} steps: - - uses: actions/checkout@v3 - + - name: Checkout repository + uses: actions/checkout@v4 - name: Install Rust ${{ matrix.rust }} - uses: actions-rs/toolchain@v1 + uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - profile: minimal - override: true components: rustfmt, clippy - - name: Cache cargo registry - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | ~/.cargo/bin/ @@ -65,4 +62,4 @@ jobs: uses: actions-rs/cargo@v1 with: command: test - args: --verbose --no-default-features --features "${{ matrix.feature }}" \ No newline at end of file + args: --verbose --no-default-features --features "${{ matrix.feature }}" diff --git a/Cargo.toml b/Cargo.toml index 7346754..c7630e0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "ais" version = "0.11.0" -authors = ["Kevin Rauwolf "] +authors = ["Kevin R "] description = "An AIS parser library" homepage = "https://github.com/squidpickles/ais" repository = "https://github.com/squidpickles/ais.git" diff --git a/src/messages/assignment_mode_command.rs b/src/messages/assignment_mode_command.rs new file mode 100644 index 0000000..3a5bff3 --- /dev/null +++ b/src/messages/assignment_mode_command.rs @@ -0,0 +1,145 @@ +//! Assignment Mode Command (type 16) + +use super::AisMessageType; +use crate::errors::Result; +use nom::bits::{bits, complete::take as take_bits}; +use nom::IResult; + +#[derive(Debug, PartialEq, Eq)] +pub struct AssignmentModeCommand { + pub message_type: u8, + pub repeat_indicator: u8, + pub mmsi: u32, + pub mmsi1: u32, + pub offset1: u16, + pub increment1: u16, + pub mmsi2: Option, + pub offset2: Option, + pub increment2: Option, +} + +impl<'a> AisMessageType<'a> for AssignmentModeCommand { + fn name(&self) -> &'static str { + "Assignment Mode Command" + } + + fn parse(data: &'a [u8]) -> Result { + let (_, report) = parse_base(data)?; + Ok(report) + } +} + +fn parse_base(data: &[u8]) -> IResult<&[u8], AssignmentModeCommand> { + bits(move |data| -> IResult<_, _> { + let (data, message_type) = take_bits(6u8)(data)?; + let (data, repeat_indicator) = take_bits(2u8)(data)?; + let (data, mmsi) = take_bits(30u32)(data)?; + let (data, _spare) = take_bits::<_, u8, _, _>(2u8)(data)?; + + let (data, mmsi1) = take_bits(30u32)(data)?; + let (data, offset1) = take_bits(12u16)(data)?; + let (data, increment1) = take_bits(10u16)(data)?; + + // Check for remaining bits, if there are enough bits for the second station + let remaining_bits = remaining_bits(data); + + if remaining_bits >= 52 { + let (data, mmsi2) = take_bits(30u32)(data)?; + let (data, offset2) = take_bits(12u16)(data)?; + let (data, increment2) = take_bits(10u16)(data)?; + + Ok(( + data, + AssignmentModeCommand { + message_type, + repeat_indicator, + mmsi, + mmsi1, + offset1, + increment1, + mmsi2: Some(mmsi2), + offset2: Some(offset2), + increment2: Some(increment2), + }, + )) + } else { + // Only one station assignment + Ok(( + data, + AssignmentModeCommand { + message_type, + repeat_indicator, + mmsi, + mmsi1, + offset1, + increment1, + mmsi2: None, + offset2: None, + increment2: None, + }, + )) + } + })(data) +} + +// Helper function to calculate remaining bits +fn remaining_bits(data: (&[u8], usize)) -> usize { + (data.0.len() * 8) - data.1 +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_type16_example_1_single_station() { + let bytestream = b"@01uEO@mMk7P Result { 15 => Ok(AisMessage::Interrogation( interrogation::Interrogation::parse(unarmored)?, )), + 16 => Ok(AisMessage::AssignmentModeCommand( + assignment_mode_command::AssignmentModeCommand::parse(unarmored)?, + )), 17 => Ok(AisMessage::DgnssBroadcastBinaryMessage( dgnss_broadcast_binary_message::DgnssBroadcastBinaryMessage::parse(unarmored)?, )), diff --git a/src/messages/types.rs b/src/messages/types.rs index 479d8bc..685afae 100644 --- a/src/messages/types.rs +++ b/src/messages/types.rs @@ -34,7 +34,6 @@ impl EpfdType { } } - #[derive(Debug, PartialEq, Eq, Copy, Clone)] pub enum ShipType { Reserved(u8), @@ -241,18 +240,13 @@ impl From for u8 { } } -#[derive(Debug, PartialEq, Eq)] +#[derive(Default, Debug, PartialEq, Eq)] pub enum Dte { Ready, + #[default] NotReady, } -impl Default for Dte { - fn default() -> Self { - Dte::NotReady - } -} - impl From for Dte { fn from(value: u8) -> Self { match value { diff --git a/src/sentence.rs b/src/sentence.rs index 03d6286..8ad2169 100644 --- a/src/sentence.rs +++ b/src/sentence.rs @@ -310,6 +310,9 @@ mod tests { fragment_number: 1, message_id: None, channel: Some('A'), + #[cfg(any(feature = "std", feature = "alloc"))] + data: GOOD_CHECKSUM[AIS_START_IDX..AIS_END_IDX].into(), + #[cfg(all(not(feature = "std"), not(feature = "alloc")))] data: GOOD_CHECKSUM[AIS_START_IDX..AIS_END_IDX] .try_into() .unwrap(),