2022-07-31 13:31:14 +02:00
|
|
|
//! 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/)
|
2023-01-22 18:26:38 +01:00
|
|
|
//!
|
2023-07-09 12:33:34 +02:00
|
|
|
//! You can find the PUS telecommand definitions in the [tc] module and ithe PUS telemetry definitions
|
|
|
|
//! inside the [tm] module.
|
2023-05-29 13:46:19 +02:00
|
|
|
use crate::{ByteConversionError, CcsdsPacket, CRC_CCITT_FALSE};
|
2023-09-26 16:55:07 +02:00
|
|
|
#[cfg(feature = "alloc")]
|
|
|
|
use alloc::vec::Vec;
|
2022-12-07 08:14:55 +01:00
|
|
|
use core::fmt::{Debug, Display, Formatter};
|
2022-06-18 22:48:51 +02:00
|
|
|
use core::mem::size_of;
|
2023-02-04 15:13:59 +01:00
|
|
|
use num_enum::{IntoPrimitive, TryFromPrimitive};
|
2022-12-04 12:17:36 +01:00
|
|
|
#[cfg(feature = "serde")]
|
2022-06-18 22:48:51 +02:00
|
|
|
use serde::{Deserialize, Serialize};
|
2022-12-07 08:14:55 +01:00
|
|
|
#[cfg(feature = "std")]
|
|
|
|
use std::error::Error;
|
2022-06-18 22:48:51 +02:00
|
|
|
|
2023-02-04 15:13:59 +01:00
|
|
|
pub mod event;
|
2023-02-04 15:16:57 +01:00
|
|
|
pub mod hk;
|
2023-02-04 15:45:23 +01:00
|
|
|
pub mod scheduling;
|
2023-07-09 12:27:44 +02:00
|
|
|
pub mod tc;
|
|
|
|
pub mod tm;
|
2023-02-04 15:45:23 +01:00
|
|
|
pub mod verification;
|
2023-02-04 14:41:59 +01:00
|
|
|
|
2022-07-31 02:27:27 +02:00
|
|
|
pub type CrcType = u16;
|
2022-06-18 22:48:51 +02:00
|
|
|
pub const CCSDS_HEADER_LEN: usize = size_of::<crate::zc::SpHeader>();
|
|
|
|
|
2023-02-04 15:13:59 +01:00
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq, IntoPrimitive, TryFromPrimitive)]
|
2022-12-19 16:36:18 +01:00
|
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
2023-02-04 15:13:59 +01:00
|
|
|
#[repr(u8)]
|
|
|
|
#[non_exhaustive]
|
2022-12-19 16:36:18 +01:00
|
|
|
pub enum PusServiceId {
|
2023-01-09 23:59:55 +01:00
|
|
|
/// Service 1
|
2022-12-19 16:36:18 +01:00
|
|
|
Verification = 1,
|
2023-02-04 15:13:59 +01:00
|
|
|
/// Service 2
|
|
|
|
DeviceAccess = 2,
|
2023-01-09 23:59:55 +01:00
|
|
|
/// Service 3
|
2022-12-19 16:36:18 +01:00
|
|
|
Housekeeping = 3,
|
2023-02-04 15:13:59 +01:00
|
|
|
/// Service 4
|
|
|
|
ParameterStatistics = 4,
|
2023-01-09 23:59:55 +01:00
|
|
|
/// Service 5
|
2022-12-19 16:36:18 +01:00
|
|
|
Event = 5,
|
2023-02-04 15:13:59 +01:00
|
|
|
/// Service 6
|
|
|
|
MemoryManagement = 6,
|
2023-01-09 23:59:55 +01:00
|
|
|
/// Service 8
|
2022-12-19 16:36:18 +01:00
|
|
|
Action = 8,
|
2023-02-04 15:13:59 +01:00
|
|
|
/// Service 9
|
|
|
|
TimeManagement = 9,
|
2023-02-04 14:41:59 +01:00
|
|
|
/// Service 11
|
|
|
|
Scheduling = 11,
|
2023-02-04 15:13:59 +01:00
|
|
|
/// Service 12
|
|
|
|
OnBoardMonitoring = 12,
|
|
|
|
/// Service 13
|
|
|
|
LargePacketTransfer = 13,
|
|
|
|
/// Service 14
|
|
|
|
RealTimeForwardingControl = 14,
|
|
|
|
/// Service 15
|
|
|
|
StorageAndRetrival = 15,
|
2023-01-09 23:59:55 +01:00
|
|
|
/// Service 17
|
2022-12-19 17:02:19 +01:00
|
|
|
Test = 17,
|
2023-02-04 15:13:59 +01:00
|
|
|
/// Service 18
|
|
|
|
OpsAndProcedures = 18,
|
|
|
|
/// Service 19
|
|
|
|
EventAction = 19,
|
|
|
|
/// Service 20
|
|
|
|
Parameter = 20,
|
|
|
|
/// Service 21
|
|
|
|
RequestSequencing = 21,
|
|
|
|
/// Service 22
|
|
|
|
PositionBasedScheduling = 22,
|
|
|
|
/// Service 23
|
2023-02-04 15:45:23 +01:00
|
|
|
FileManagement = 23,
|
2022-12-19 16:36:18 +01:00
|
|
|
}
|
|
|
|
|
2022-07-31 13:32:11 +02:00
|
|
|
/// All PUS versions. Only PUS C is supported by this library.
|
2022-12-04 12:17:36 +01:00
|
|
|
#[derive(PartialEq, Eq, Copy, Clone, Debug)]
|
|
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
2022-06-18 22:48:51 +02:00
|
|
|
pub enum PusVersion {
|
|
|
|
EsaPus = 0,
|
|
|
|
PusA = 1,
|
|
|
|
PusC = 2,
|
2022-07-31 02:27:27 +02:00
|
|
|
Invalid = 0b1111,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl TryFrom<u8> for PusVersion {
|
|
|
|
type Error = ();
|
|
|
|
|
|
|
|
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
|
|
|
match value {
|
|
|
|
x if x == PusVersion::EsaPus as u8 => Ok(PusVersion::EsaPus),
|
|
|
|
x if x == PusVersion::PusA as u8 => Ok(PusVersion::PusA),
|
|
|
|
x if x == PusVersion::PusC as u8 => Ok(PusVersion::PusC),
|
|
|
|
_ => Err(()),
|
|
|
|
}
|
|
|
|
}
|
2022-06-18 22:48:51 +02:00
|
|
|
}
|
|
|
|
|
2023-01-09 23:59:55 +01:00
|
|
|
/// ECSS Packet Type Codes (PTC)s.
|
2022-09-13 10:18:21 +02:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
2022-12-04 17:11:44 +01:00
|
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
2022-09-13 10:18:21 +02:00
|
|
|
pub enum PacketTypeCodes {
|
|
|
|
Boolean = 1,
|
|
|
|
Enumerated = 2,
|
|
|
|
UnsignedInt = 3,
|
|
|
|
SignedInt = 4,
|
|
|
|
Real = 5,
|
|
|
|
BitString = 6,
|
|
|
|
OctetString = 7,
|
|
|
|
CharString = 8,
|
|
|
|
AbsoluteTime = 9,
|
|
|
|
RelativeTime = 10,
|
|
|
|
Deduced = 11,
|
|
|
|
Packet = 12,
|
|
|
|
}
|
|
|
|
|
2022-12-18 16:21:20 +01:00
|
|
|
pub type Ptc = PacketTypeCodes;
|
|
|
|
|
2023-01-09 23:59:55 +01:00
|
|
|
/// ECSS Packet Field Codes (PFC)s for the unsigned [Ptc].
|
2022-12-18 16:21:20 +01:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
|
|
pub enum UnsignedPfc {
|
|
|
|
OneByte = 4,
|
|
|
|
TwelveBits = 8,
|
|
|
|
TwoBytes = 12,
|
|
|
|
ThreeBytes = 13,
|
|
|
|
FourBytes = 14,
|
|
|
|
SixBytes = 15,
|
|
|
|
EightBytes = 16,
|
|
|
|
OneBit = 17,
|
|
|
|
TwoBits = 18,
|
|
|
|
ThreeBits = 19,
|
|
|
|
}
|
|
|
|
|
2023-01-09 23:59:55 +01:00
|
|
|
/// ECSS Packet Field Codes (PFC)s for the real (floating point) [Ptc].
|
2022-12-18 16:21:20 +01:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
|
|
pub enum RealPfc {
|
|
|
|
/// 4 octets simple precision format (IEEE)
|
|
|
|
Float = 1,
|
|
|
|
/// 8 octets simple precision format (IEEE)
|
|
|
|
Double = 2,
|
|
|
|
/// 4 octets simple precision format (MIL-STD)
|
|
|
|
FloatMilStd = 3,
|
|
|
|
/// 8 octets simple precision format (MIL-STD)
|
|
|
|
DoubleMilStd = 4,
|
|
|
|
}
|
|
|
|
|
2022-08-16 11:12:47 +02:00
|
|
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
2022-12-04 17:11:44 +01:00
|
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
2022-06-18 22:48:51 +02:00
|
|
|
pub enum PusError {
|
|
|
|
VersionNotSupported(PusVersion),
|
|
|
|
IncorrectCrc(u16),
|
|
|
|
RawDataTooShort(usize),
|
|
|
|
NoRawData,
|
|
|
|
/// CRC16 needs to be calculated first
|
|
|
|
CrcCalculationMissing,
|
2023-07-09 19:31:32 +02:00
|
|
|
ByteConversion(ByteConversionError),
|
2022-09-13 09:52:59 +02:00
|
|
|
}
|
|
|
|
|
2022-12-07 08:14:55 +01:00
|
|
|
impl Display for PusError {
|
|
|
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
|
|
|
match self {
|
|
|
|
PusError::VersionNotSupported(v) => {
|
2023-01-26 21:57:45 +01:00
|
|
|
write!(f, "PUS version {v:?} not supported")
|
2022-12-07 08:14:55 +01:00
|
|
|
}
|
|
|
|
PusError::IncorrectCrc(crc) => {
|
2023-01-26 21:57:45 +01:00
|
|
|
write!(f, "crc16 {crc:#04x} is incorrect")
|
2022-12-07 08:14:55 +01:00
|
|
|
}
|
|
|
|
PusError::RawDataTooShort(size) => {
|
|
|
|
write!(
|
|
|
|
f,
|
2023-01-26 21:57:45 +01:00
|
|
|
"deserialization error, provided raw data with size {size} too short"
|
2022-12-07 08:14:55 +01:00
|
|
|
)
|
|
|
|
}
|
|
|
|
PusError::NoRawData => {
|
|
|
|
write!(f, "no raw data provided")
|
|
|
|
}
|
|
|
|
PusError::CrcCalculationMissing => {
|
|
|
|
write!(f, "crc16 was not calculated")
|
|
|
|
}
|
2023-07-09 19:31:32 +02:00
|
|
|
PusError::ByteConversion(e) => {
|
2023-01-26 21:57:45 +01:00
|
|
|
write!(f, "low level byte conversion error: {e}")
|
2022-12-07 08:14:55 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(feature = "std")]
|
|
|
|
impl Error for PusError {
|
|
|
|
fn source(&self) -> Option<&(dyn Error + 'static)> {
|
2023-07-09 19:31:32 +02:00
|
|
|
if let PusError::ByteConversion(e) = self {
|
2022-12-07 08:14:55 +01:00
|
|
|
return Some(e);
|
|
|
|
}
|
|
|
|
None
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-13 09:52:59 +02:00
|
|
|
impl From<ByteConversionError> for PusError {
|
|
|
|
fn from(e: ByteConversionError) -> Self {
|
2023-07-09 19:31:32 +02:00
|
|
|
PusError::ByteConversion(e)
|
2022-09-13 09:52:59 +02:00
|
|
|
}
|
2022-06-18 22:48:51 +02:00
|
|
|
}
|
|
|
|
|
2023-01-09 23:59:55 +01:00
|
|
|
/// Generic trait to describe common attributes for both PUS Telecommands (TC) and PUS Telemetry
|
|
|
|
/// (TM) packets. All PUS packets are also a special type of [CcsdsPacket]s.
|
2022-06-18 22:48:51 +02:00
|
|
|
pub trait PusPacket: CcsdsPacket {
|
|
|
|
const PUS_VERSION: PusVersion = PusVersion::PusC;
|
|
|
|
|
2022-07-31 02:27:27 +02:00
|
|
|
fn pus_version(&self) -> PusVersion;
|
2022-06-18 22:48:51 +02:00
|
|
|
fn service(&self) -> u8;
|
|
|
|
fn subservice(&self) -> u8;
|
2023-07-11 00:03:07 +02:00
|
|
|
fn user_data(&self) -> &[u8];
|
2022-06-18 22:48:51 +02:00
|
|
|
fn crc16(&self) -> Option<u16>;
|
|
|
|
}
|
2022-07-31 02:27:27 +02:00
|
|
|
|
2022-07-31 13:31:14 +02:00
|
|
|
pub(crate) fn crc_from_raw_data(raw_data: &[u8]) -> Result<u16, PusError> {
|
|
|
|
if raw_data.len() < 2 {
|
|
|
|
return Err(PusError::RawDataTooShort(raw_data.len()));
|
2022-07-31 02:27:27 +02:00
|
|
|
}
|
2022-07-31 13:31:14 +02:00
|
|
|
Ok(u16::from_be_bytes(
|
|
|
|
raw_data[raw_data.len() - 2..raw_data.len()]
|
|
|
|
.try_into()
|
|
|
|
.unwrap(),
|
|
|
|
))
|
2022-07-31 02:27:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn calc_pus_crc16(bytes: &[u8]) -> u16 {
|
|
|
|
let mut digest = CRC_CCITT_FALSE.digest();
|
|
|
|
digest.update(bytes);
|
|
|
|
digest.finalize()
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn crc_procedure(
|
|
|
|
calc_on_serialization: bool,
|
|
|
|
cached_crc16: &Option<u16>,
|
2022-08-01 01:08:06 +02:00
|
|
|
start_idx: usize,
|
2022-07-31 02:27:27 +02:00
|
|
|
curr_idx: usize,
|
|
|
|
slice: &[u8],
|
|
|
|
) -> Result<u16, PusError> {
|
|
|
|
let crc16;
|
|
|
|
if calc_on_serialization {
|
2022-08-01 01:08:06 +02:00
|
|
|
crc16 = calc_pus_crc16(&slice[start_idx..curr_idx])
|
2022-07-31 02:27:27 +02:00
|
|
|
} else if cached_crc16.is_none() {
|
|
|
|
return Err(PusError::CrcCalculationMissing);
|
|
|
|
} else {
|
|
|
|
crc16 = cached_crc16.unwrap();
|
|
|
|
}
|
|
|
|
Ok(crc16)
|
|
|
|
}
|
2022-07-31 13:31:14 +02:00
|
|
|
|
|
|
|
pub(crate) fn user_data_from_raw(
|
|
|
|
current_idx: usize,
|
|
|
|
total_len: usize,
|
|
|
|
raw_data_len: usize,
|
|
|
|
slice: &[u8],
|
2023-07-11 00:03:07 +02:00
|
|
|
) -> Result<&[u8], PusError> {
|
2022-07-31 13:31:14 +02:00
|
|
|
match current_idx {
|
|
|
|
_ if current_idx > total_len - 2 => Err(PusError::RawDataTooShort(raw_data_len)),
|
2023-07-11 00:03:07 +02:00
|
|
|
_ => Ok(&slice[current_idx..total_len - 2]),
|
2022-07-31 13:31:14 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-05 19:07:31 +02:00
|
|
|
pub(crate) fn verify_crc16_ccitt_false_from_raw_to_pus_error(
|
2023-05-29 13:46:19 +02:00
|
|
|
raw_data: &[u8],
|
|
|
|
crc16: u16,
|
|
|
|
) -> Result<(), PusError> {
|
2023-07-05 19:07:31 +02:00
|
|
|
verify_crc16_ccitt_false_from_raw(raw_data)
|
|
|
|
.then(|| ())
|
|
|
|
.ok_or(PusError::IncorrectCrc(crc16))
|
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn verify_crc16_ccitt_false_from_raw(raw_data: &[u8]) -> bool {
|
2022-07-31 13:31:14 +02:00
|
|
|
let mut digest = CRC_CCITT_FALSE.digest();
|
|
|
|
digest.update(raw_data);
|
|
|
|
if digest.finalize() == 0 {
|
2023-07-05 19:07:31 +02:00
|
|
|
return true;
|
2022-07-31 13:31:14 +02:00
|
|
|
}
|
2023-07-05 19:07:31 +02:00
|
|
|
false
|
2022-07-31 13:31:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
macro_rules! ccsds_impl {
|
|
|
|
() => {
|
|
|
|
delegate!(to self.sp_header {
|
|
|
|
fn ccsds_version(&self) -> u8;
|
|
|
|
fn packet_id(&self) -> crate::PacketId;
|
|
|
|
fn psc(&self) -> crate::PacketSequenceCtrl;
|
|
|
|
fn data_len(&self) -> u16;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-07-31 14:24:16 +02:00
|
|
|
macro_rules! sp_header_impls {
|
|
|
|
() => {
|
|
|
|
delegate!(to self.sp_header {
|
|
|
|
pub fn set_apid(&mut self, apid: u16) -> bool;
|
|
|
|
pub fn set_seq_count(&mut self, seq_count: u16) -> bool;
|
|
|
|
pub fn set_seq_flags(&mut self, seq_flag: SequenceFlags);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-14 20:10:34 +02:00
|
|
|
use crate::util::{GenericUnsignedByteField, ToBeBytes, UnsignedEnum};
|
2022-07-31 13:31:14 +02:00
|
|
|
pub(crate) use ccsds_impl;
|
2022-07-31 14:24:16 +02:00
|
|
|
pub(crate) use sp_header_impls;
|
2022-09-03 18:47:59 +02:00
|
|
|
|
2022-10-26 00:22:56 +02:00
|
|
|
/// Generic trait for ECSS enumeration which consist of a PFC field denoting their bit length
|
2022-09-13 10:18:21 +02:00
|
|
|
/// and an unsigned value. The trait makes no assumptions about the actual type of the unsigned
|
|
|
|
/// value and only requires implementors to implement a function which writes the enumeration into
|
|
|
|
/// a raw byte format.
|
2023-05-14 20:10:34 +02:00
|
|
|
pub trait EcssEnumeration: UnsignedEnum {
|
2022-09-13 10:18:21 +02:00
|
|
|
/// Packet Format Code, which denotes the number of bits of the enumeration
|
2022-09-03 18:47:59 +02:00
|
|
|
fn pfc(&self) -> u8;
|
|
|
|
}
|
|
|
|
|
2022-10-31 00:22:28 +01:00
|
|
|
pub trait EcssEnumerationExt: EcssEnumeration + Debug + Copy + Clone + PartialEq + Eq {}
|
|
|
|
|
2022-10-31 00:23:13 +01:00
|
|
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
2022-12-04 17:11:44 +01:00
|
|
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
2022-09-03 18:47:59 +02:00
|
|
|
pub struct GenericEcssEnumWrapper<TYPE> {
|
2023-05-14 20:10:34 +02:00
|
|
|
field: GenericUnsignedByteField<TYPE>,
|
2022-09-03 18:47:59 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
impl<TYPE> GenericEcssEnumWrapper<TYPE> {
|
2022-09-13 10:18:21 +02:00
|
|
|
pub const fn ptc() -> PacketTypeCodes {
|
|
|
|
PacketTypeCodes::Enumerated
|
|
|
|
}
|
|
|
|
|
2022-09-03 18:47:59 +02:00
|
|
|
pub fn new(val: TYPE) -> Self {
|
2023-05-14 20:10:34 +02:00
|
|
|
Self {
|
|
|
|
field: GenericUnsignedByteField::new(val),
|
|
|
|
}
|
2022-09-03 18:47:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-14 20:10:34 +02:00
|
|
|
impl<TYPE: ToBeBytes> UnsignedEnum for GenericEcssEnumWrapper<TYPE> {
|
2023-07-02 20:57:50 +02:00
|
|
|
fn size(&self) -> usize {
|
2023-05-14 20:10:34 +02:00
|
|
|
(self.pfc() / 8) as usize
|
2022-09-03 18:47:59 +02:00
|
|
|
}
|
|
|
|
|
2023-06-08 20:50:18 +02:00
|
|
|
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
|
2023-05-14 20:10:34 +02:00
|
|
|
self.field.write_to_be_bytes(buf)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<TYPE: ToBeBytes> EcssEnumeration for GenericEcssEnumWrapper<TYPE> {
|
|
|
|
fn pfc(&self) -> u8 {
|
|
|
|
size_of::<TYPE>() as u8 * 8_u8
|
2022-09-03 18:47:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-10-31 00:23:13 +01:00
|
|
|
impl<TYPE: Debug + Copy + Clone + PartialEq + Eq + ToBeBytes> EcssEnumerationExt
|
|
|
|
for GenericEcssEnumWrapper<TYPE>
|
|
|
|
{
|
|
|
|
}
|
2022-10-31 00:22:28 +01:00
|
|
|
|
2022-09-03 18:47:59 +02:00
|
|
|
pub type EcssEnumU8 = GenericEcssEnumWrapper<u8>;
|
|
|
|
pub type EcssEnumU16 = GenericEcssEnumWrapper<u16>;
|
|
|
|
pub type EcssEnumU32 = GenericEcssEnumWrapper<u32>;
|
|
|
|
pub type EcssEnumU64 = GenericEcssEnumWrapper<u64>;
|
|
|
|
|
2023-07-03 00:57:38 +02:00
|
|
|
/// Generic trait for PUS packet abstractions which can written to a raw slice as their raw
|
|
|
|
/// byte representation. This is especially useful for generic abstractions which depend only
|
|
|
|
/// on the serialization of those packets.
|
2023-07-02 18:38:18 +02:00
|
|
|
pub trait SerializablePusPacket {
|
2023-07-03 00:54:21 +02:00
|
|
|
fn len_packed(&self) -> usize;
|
2023-07-02 18:38:18 +02:00
|
|
|
fn write_to_bytes(&self, slice: &mut [u8]) -> Result<usize, PusError>;
|
2023-09-26 16:55:07 +02:00
|
|
|
#[cfg(feature = "alloc")]
|
|
|
|
fn to_vec(&self) -> Result<Vec<u8>, PusError> {
|
|
|
|
// This is the correct way to do this. See
|
|
|
|
// [this issue](https://github.com/rust-lang/rust-clippy/issues/4483) for caveats of more
|
|
|
|
// "efficient" implementations.
|
|
|
|
let mut vec = alloc::vec![0; self.len_packed()];
|
|
|
|
self.write_to_bytes(&mut vec)?;
|
|
|
|
Ok(vec)
|
|
|
|
}
|
2023-07-02 18:38:18 +02:00
|
|
|
}
|
|
|
|
|
2022-09-03 18:47:59 +02:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2023-05-14 20:10:34 +02:00
|
|
|
use crate::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, UnsignedEnum};
|
2022-09-03 18:47:59 +02:00
|
|
|
use crate::ByteConversionError;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_enum_u8() {
|
|
|
|
let mut buf = [0, 0, 0];
|
|
|
|
let my_enum = EcssEnumU8::new(1);
|
|
|
|
my_enum
|
2022-11-02 00:36:18 +01:00
|
|
|
.write_to_be_bytes(&mut buf[1..2])
|
2022-09-03 18:47:59 +02:00
|
|
|
.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
|
2022-11-02 00:36:18 +01:00
|
|
|
.write_to_be_bytes(&mut buf[1..3])
|
2022-09-03 18:47:59 +02:00
|
|
|
.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);
|
2022-11-02 00:36:18 +01:00
|
|
|
let res = my_enum.write_to_be_bytes(&mut buf[0..1]);
|
2022-09-03 18:47:59 +02:00
|
|
|
assert!(res.is_err());
|
|
|
|
let error = res.unwrap_err();
|
|
|
|
match error {
|
2023-08-18 10:09:32 +02:00
|
|
|
ByteConversionError::ToSliceTooSmall { found, expected } => {
|
|
|
|
assert_eq!(expected, 2);
|
|
|
|
assert_eq!(found, 1);
|
2022-09-03 18:47:59 +02:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
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
|
2022-11-02 00:36:18 +01:00
|
|
|
.write_to_be_bytes(&mut buf[1..5])
|
2022-09-03 18:47:59 +02:00
|
|
|
.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);
|
2022-11-02 00:36:18 +01:00
|
|
|
let res = my_enum.write_to_be_bytes(&mut buf[0..3]);
|
2022-09-03 18:47:59 +02:00
|
|
|
assert!(res.is_err());
|
|
|
|
let error = res.unwrap_err();
|
|
|
|
match error {
|
2023-08-18 10:09:32 +02:00
|
|
|
ByteConversionError::ToSliceTooSmall { found, expected } => {
|
|
|
|
assert_eq!(expected, 4);
|
|
|
|
assert_eq!(found, 3);
|
2022-09-03 18:47:59 +02:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
panic!("Unexpected error {:?}", error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|