From a4e297f0c0d20d368be33ed084029f782481bed4 Mon Sep 17 00:00:00 2001 From: Robin Mueller Date: Sat, 3 Sep 2022 18:47:59 +0200 Subject: [PATCH] Add new features - Basic ECSS enumeration support for u8, u16, u32 and u64 - Better names for generic error enums --- src/ecss.rs | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++- src/lib.rs | 16 +++--- src/tc.rs | 22 +++++--- src/time.rs | 14 ++--- src/tm.rs | 19 ++++--- 5 files changed, 196 insertions(+), 32 deletions(-) diff --git a/src/ecss.rs b/src/ecss.rs index b18d2e6..56befd4 100644 --- a/src/ecss.rs +++ b/src/ecss.rs @@ -1,6 +1,6 @@ //! Common definitions and helpers required to create PUS TMTC packets according to //! [ECSS-E-ST-70-41C](https://ecss.nl/standard/ecss-e-st-70-41c-space-engineering-telemetry-and-telecommand-packet-utilization-15-april-2016/) -use crate::{CcsdsPacket, PacketError}; +use crate::{ByteConversionError, CcsdsPacket, SizeMissmatch}; use core::mem::size_of; use crc::{Crc, CRC_16_IBM_3740}; use serde::{Deserialize, Serialize}; @@ -41,7 +41,7 @@ pub enum PusError { NoRawData, /// CRC16 needs to be calculated first CrcCalculationMissing, - PacketError(PacketError), + PacketError(ByteConversionError), } pub trait PusPacket: CcsdsPacket { @@ -135,3 +135,156 @@ macro_rules! sp_header_impls { pub(crate) use ccsds_impl; pub(crate) use sp_header_impls; + +pub trait EcssEnumeration { + fn pfc(&self) -> u8; + fn byte_width(&self) -> u8 { + self.pfc() / 8 + } + fn to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>; +} + +trait ToBeBytes { + type ByteArray: AsRef<[u8]>; + fn to_be_bytes(&self) -> Self::ByteArray; +} + +impl ToBeBytes for u8 { + type ByteArray = [u8; 1]; + + fn to_be_bytes(&self) -> Self::ByteArray { + u8::to_be_bytes(*self) + } +} + +impl ToBeBytes for u16 { + type ByteArray = [u8; 2]; + + fn to_be_bytes(&self) -> Self::ByteArray { + u16::to_be_bytes(*self) + } +} + +impl ToBeBytes for u32 { + type ByteArray = [u8; 4]; + + fn to_be_bytes(&self) -> Self::ByteArray { + u32::to_be_bytes(*self) + } +} + +impl ToBeBytes for u64 { + type ByteArray = [u8; 8]; + + fn to_be_bytes(&self) -> Self::ByteArray { + u64::to_be_bytes(*self) + } +} + +pub struct GenericEcssEnumWrapper { + val: TYPE, +} + +impl GenericEcssEnumWrapper { + pub fn new(val: TYPE) -> Self { + Self { val } + } +} + +impl EcssEnumeration for GenericEcssEnumWrapper { + fn pfc(&self) -> u8 { + size_of::() as u8 * 8_u8 + } + + fn to_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> { + if buf.len() < self.byte_width() as usize { + return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch { + found: buf.len(), + expected: self.byte_width() as usize, + })); + } + buf[0..self.byte_width() as usize].copy_from_slice(self.val.to_be_bytes().as_ref()); + Ok(()) + } +} + +pub type EcssEnumU8 = GenericEcssEnumWrapper; +pub type EcssEnumU16 = GenericEcssEnumWrapper; +pub type EcssEnumU32 = GenericEcssEnumWrapper; +pub type EcssEnumU64 = GenericEcssEnumWrapper; + +#[cfg(test)] +mod tests { + use crate::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration}; + use crate::ByteConversionError; + + #[test] + fn test_enum_u8() { + let mut buf = [0, 0, 0]; + let my_enum = EcssEnumU8::new(1); + my_enum + .to_bytes(&mut buf[1..2]) + .expect("To byte conversion of u8 failed"); + assert_eq!(buf[1], 1); + } + + #[test] + fn test_enum_u16() { + let mut buf = [0, 0, 0]; + let my_enum = EcssEnumU16::new(0x1f2f); + my_enum + .to_bytes(&mut buf[1..3]) + .expect("To byte conversion of u8 failed"); + assert_eq!(buf[1], 0x1f); + assert_eq!(buf[2], 0x2f); + } + + #[test] + fn test_slice_u16_too_small() { + let mut buf = [0]; + let my_enum = EcssEnumU16::new(0x1f2f); + let res = my_enum.to_bytes(&mut buf[0..1]); + assert!(res.is_err()); + let error = res.unwrap_err(); + match error { + ByteConversionError::ToSliceTooSmall(missmatch) => { + assert_eq!(missmatch.expected, 2); + assert_eq!(missmatch.found, 1); + } + _ => { + panic!("Unexpected error {:?}", error); + } + } + } + + #[test] + fn test_enum_u32() { + let mut buf = [0, 0, 0, 0, 0]; + let my_enum = EcssEnumU32::new(0x1f2f3f4f); + my_enum + .to_bytes(&mut buf[1..5]) + .expect("To byte conversion of u8 failed"); + assert_eq!(buf[1], 0x1f); + assert_eq!(buf[2], 0x2f); + assert_eq!(buf[3], 0x3f); + assert_eq!(buf[4], 0x4f); + } + + #[test] + fn test_slice_u32_too_small() { + let mut buf = [0, 0, 0, 0, 0]; + let my_enum = EcssEnumU32::new(0x1f2f3f4f); + let res = my_enum.to_bytes(&mut buf[0..3]); + assert!(res.is_err()); + let error = res.unwrap_err(); + match error { + ByteConversionError::ToSliceTooSmall(missmatch) => { + assert_eq!(missmatch.expected, 4); + assert_eq!(missmatch.found, 3); + } + _ => { + panic!("Unexpected error {:?}", error); + } + } + } +} diff --git a/src/lib.rs b/src/lib.rs index f6f9376..5cccb69 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -65,14 +65,14 @@ pub struct SizeMissmatch { pub expected: usize, } #[derive(Debug, Copy, Clone, PartialEq, Eq)] -pub enum PacketError { +pub enum ByteConversionError { /// The passed slice is too small. Returns the found and expected minimum size - ToBytesSliceTooSmall(SizeMissmatch), + ToSliceTooSmall(SizeMissmatch), /// The provider buffer it soo small. Returns the found and expected minimum size - FromBytesSliceTooSmall(SizeMissmatch), + FromSliceTooSmall(SizeMissmatch), /// The [zerocopy] library failed to write to bytes - ToBytesZeroCopyError, - FromBytesZeroCopyError, + ZeroCopyToError, + ZeroCopyFromError, } #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Copy, Clone)] @@ -391,15 +391,15 @@ impl SpHeader { self.packet_id.ptype = packet_type; } - pub fn from_raw_slice(buf: &[u8]) -> Result { + pub fn from_raw_slice(buf: &[u8]) -> Result { if buf.len() < CCSDS_HEADER_LEN + 1 { - return Err(PacketError::FromBytesSliceTooSmall(SizeMissmatch { + return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch { found: buf.len(), expected: CCSDS_HEADER_LEN + 1, })); } let zc_header = zc::SpHeader::from_bytes(&buf[0..CCSDS_HEADER_LEN]) - .ok_or(PacketError::FromBytesZeroCopyError)?; + .ok_or(ByteConversionError::ZeroCopyFromError)?; Ok(Self::from(zc_header)) } } diff --git a/src/tc.rs b/src/tc.rs index 6dbd0f8..185575f 100644 --- a/src/tc.rs +++ b/src/tc.rs @@ -36,7 +36,9 @@ use crate::ecss::{ verify_crc16_from_raw, CrcType, PusError, PusPacket, PusVersion, CRC_CCITT_FALSE, }; use crate::SpHeader; -use crate::{CcsdsPacket, PacketError, PacketType, SequenceFlags, SizeMissmatch, CCSDS_HEADER_LEN}; +use crate::{ + ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SizeMissmatch, CCSDS_HEADER_LEN, +}; use core::mem::size_of; use delegate::delegate; use serde::{Deserialize, Serialize}; @@ -334,7 +336,7 @@ impl<'slice> PusTc<'slice> { let tc_header_len = size_of::(); let total_size = self.len_packed(); if total_size > slice.len() { - return Err(PusError::PacketError(PacketError::ToBytesSliceTooSmall( + return Err(PusError::PacketError(ByteConversionError::ToSliceTooSmall( SizeMissmatch { found: slice.len(), expected: total_size, @@ -343,13 +345,13 @@ impl<'slice> PusTc<'slice> { } sph_zc .to_bytes(&mut slice[curr_idx..curr_idx + CCSDS_HEADER_LEN]) - .ok_or(PusError::PacketError(PacketError::ToBytesZeroCopyError))?; + .ok_or(PusError::PacketError(ByteConversionError::ZeroCopyToError))?; curr_idx += CCSDS_HEADER_LEN; let sec_header = zc::PusTcSecondaryHeader::try_from(self.sec_header).unwrap(); sec_header .to_bytes(&mut slice[curr_idx..curr_idx + tc_header_len]) - .ok_or(PusError::PacketError(PacketError::ToBytesZeroCopyError))?; + .ok_or(PusError::PacketError(ByteConversionError::ZeroCopyToError))?; curr_idx += tc_header_len; if let Some(app_data) = self.app_data { @@ -408,7 +410,9 @@ impl<'slice> PusTc<'slice> { let mut current_idx = 0; let sph = crate::zc::SpHeader::from_bytes(&slice[current_idx..current_idx + CCSDS_HEADER_LEN]) - .ok_or(PusError::PacketError(PacketError::FromBytesZeroCopyError))?; + .ok_or(PusError::PacketError( + ByteConversionError::ZeroCopyFromError, + ))?; current_idx += CCSDS_HEADER_LEN; let total_len = sph.total_len(); if raw_data_len < total_len || total_len < PUS_TC_MIN_LEN_WITHOUT_APP_DATA { @@ -417,7 +421,9 @@ impl<'slice> PusTc<'slice> { let sec_header = crate::tc::zc::PusTcSecondaryHeader::from_bytes( &slice[current_idx..current_idx + PUC_TC_SECONDARY_HEADER_LEN], ) - .ok_or(PusError::PacketError(PacketError::FromBytesZeroCopyError))?; + .ok_or(PusError::PacketError( + ByteConversionError::ZeroCopyFromError, + ))?; current_idx += PUC_TC_SECONDARY_HEADER_LEN; let raw_data = &slice[0..total_len]; let pus_tc = PusTc { @@ -476,8 +482,8 @@ mod tests { use crate::ecss::{PusError, PusPacket}; use crate::tc::ACK_ALL; use crate::tc::{PusTc, PusTcSecondaryHeader, PusTcSecondaryHeaderT}; + use crate::{ByteConversionError, SpHeader}; use crate::{CcsdsPacket, SequenceFlags}; - use crate::{PacketError, SpHeader}; use alloc::vec::Vec; fn base_ping_tc_full_ctor() -> PusTc<'static> { @@ -631,7 +637,7 @@ mod tests { let err = res.unwrap_err(); match err { PusError::PacketError(err) => match err { - PacketError::ToBytesSliceTooSmall(missmatch) => { + ByteConversionError::ToSliceTooSmall(missmatch) => { assert_eq!(missmatch.expected, pus_tc.len_packed()); assert_eq!(missmatch.found, 12); } diff --git a/src/time.rs b/src/time.rs index d1137d5..bd2d3c8 100644 --- a/src/time.rs +++ b/src/time.rs @@ -1,5 +1,5 @@ //! CCSDS Time Code Formats according to [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf) -use crate::{PacketError, SizeMissmatch}; +use crate::{ByteConversionError, SizeMissmatch}; use chrono::{DateTime, TimeZone, Utc}; #[allow(unused_imports)] @@ -43,7 +43,7 @@ pub enum TimestampError { /// Contains tuple where first value is the expected time code and the second /// value is the found raw value InvalidTimeCode(CcsdsTimeCodes, u8), - OtherPacketError(PacketError), + OtherPacketError(ByteConversionError), } #[cfg(feature = "std")] @@ -197,7 +197,7 @@ impl TimeWriter for CdsShortTimeProvider { fn write_to_bytes(&self, buf: &mut [u8]) -> Result<(), TimestampError> { if buf.len() < self.len_as_bytes() { return Err(TimestampError::OtherPacketError( - PacketError::ToBytesSliceTooSmall(SizeMissmatch { + ByteConversionError::ToSliceTooSmall(SizeMissmatch { expected: self.len_as_bytes(), found: buf.len(), }), @@ -214,7 +214,7 @@ impl TimeReader for CdsShortTimeProvider { fn from_bytes(buf: &[u8]) -> Result { if buf.len() < CDS_SHORT_LEN { return Err(TimestampError::OtherPacketError( - PacketError::FromBytesSliceTooSmall(SizeMissmatch { + ByteConversionError::FromSliceTooSmall(SizeMissmatch { expected: CDS_SHORT_LEN, found: buf.len(), }), @@ -248,7 +248,7 @@ impl TimeReader for CdsShortTimeProvider { mod tests { use super::*; use crate::time::TimestampError::{InvalidTimeCode, OtherPacketError}; - use crate::PacketError::{FromBytesSliceTooSmall, ToBytesSliceTooSmall}; + use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall}; use alloc::format; use chrono::{Datelike, Timelike}; @@ -336,7 +336,7 @@ mod tests { let res = time_stamper.write_to_bytes(&mut buf[0..i]); assert!(res.is_err()); match res.unwrap_err() { - OtherPacketError(ToBytesSliceTooSmall(missmatch)) => { + OtherPacketError(ToSliceTooSmall(missmatch)) => { assert_eq!(missmatch.found, i); assert_eq!(missmatch.expected, 7); } @@ -359,7 +359,7 @@ mod tests { panic!("Unexpected error"); } OtherPacketError(e) => match e { - FromBytesSliceTooSmall(missmatch) => { + FromSliceTooSmall(missmatch) => { assert_eq!(missmatch.found, i); assert_eq!(missmatch.expected, 7); } diff --git a/src/tm.rs b/src/tm.rs index 1212803..15a3bff 100644 --- a/src/tm.rs +++ b/src/tm.rs @@ -5,7 +5,8 @@ use crate::ecss::{ verify_crc16_from_raw, CrcType, PusError, PusPacket, PusVersion, CRC_CCITT_FALSE, }; use crate::{ - CcsdsPacket, PacketError, PacketType, SequenceFlags, SizeMissmatch, SpHeader, CCSDS_HEADER_LEN, + ByteConversionError, CcsdsPacket, PacketType, SequenceFlags, SizeMissmatch, SpHeader, + CCSDS_HEADER_LEN, }; use core::mem::size_of; use serde::{Deserialize, Serialize}; @@ -309,7 +310,7 @@ impl<'slice> PusTm<'slice> { let sph_zc = crate::zc::SpHeader::from(self.sp_header); let total_size = self.len_packed(); if total_size > slice.len() { - return Err(PusError::PacketError(PacketError::ToBytesSliceTooSmall( + return Err(PusError::PacketError(ByteConversionError::ToSliceTooSmall( SizeMissmatch { found: slice.len(), expected: total_size, @@ -318,14 +319,14 @@ impl<'slice> PusTm<'slice> { } sph_zc .to_bytes(&mut slice[curr_idx..curr_idx + CCSDS_HEADER_LEN]) - .ok_or(PusError::PacketError(PacketError::ToBytesZeroCopyError))?; + .ok_or(PusError::PacketError(ByteConversionError::ZeroCopyToError))?; curr_idx += CCSDS_HEADER_LEN; let sec_header_len = size_of::(); let sec_header = zc::PusTmSecHeaderWithoutTimestamp::try_from(self.sec_header).unwrap(); sec_header .to_bytes(&mut slice[curr_idx..curr_idx + sec_header_len]) - .ok_or(PusError::PacketError(PacketError::ToBytesZeroCopyError))?; + .ok_or(PusError::PacketError(ByteConversionError::ZeroCopyToError))?; curr_idx += sec_header_len; let timestamp_len = self.sec_header.time_stamp.len(); slice[curr_idx..curr_idx + timestamp_len].copy_from_slice(self.sec_header.time_stamp); @@ -394,7 +395,9 @@ impl<'slice> PusTm<'slice> { let mut current_idx = 0; let sph = crate::zc::SpHeader::from_bytes(&slice[current_idx..current_idx + CCSDS_HEADER_LEN]) - .ok_or(PusError::PacketError(PacketError::FromBytesZeroCopyError))?; + .ok_or(PusError::PacketError( + ByteConversionError::ZeroCopyFromError, + ))?; current_idx += 6; let total_len = sph.total_len(); if raw_data_len < total_len || total_len < PUS_TM_MIN_LEN_WITHOUT_SOURCE_DATA { @@ -403,7 +406,9 @@ impl<'slice> PusTm<'slice> { let sec_header_zc = zc::PusTmSecHeaderWithoutTimestamp::from_bytes( &slice[current_idx..current_idx + PUC_TM_MIN_SEC_HEADER_LEN], ) - .ok_or(PusError::PacketError(PacketError::FromBytesZeroCopyError))?; + .ok_or(PusError::PacketError( + ByteConversionError::ZeroCopyFromError, + ))?; current_idx += PUC_TM_MIN_SEC_HEADER_LEN; let zc_sec_header_wrapper = zc::PusTmSecHeader { zc_header: sec_header_zc, @@ -568,7 +573,7 @@ mod tests { assert!(matches!(error, PusError::PacketError { .. })); match error { PusError::PacketError(err) => match err { - PacketError::ToBytesSliceTooSmall(size_missmatch) => { + ByteConversionError::ToSliceTooSmall(size_missmatch) => { assert_eq!(size_missmatch.expected, 22); assert_eq!(size_missmatch.found, 16); }