thats a lot
This commit is contained in:
parent
4bbf38916a
commit
d2f944580c
15
CHANGELOG.md
15
CHANGELOG.md
@ -8,6 +8,21 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
## Added
|
||||||
|
|
||||||
|
- Added new `util` module which contains the following (new) helper modules:
|
||||||
|
- `UnsignedEnum` trait as an abstraction for unsigned byte fields with variable lengths. It is
|
||||||
|
not tied to the ECSS PFC value like the `EcssEnumeration` trait.
|
||||||
|
- `UnsignedByteField` as a type-erased helper.
|
||||||
|
- `UnsignedU8`, `UnsignedU16`, `UnsignedU32` and `UnsignedU64` as helper types implementing
|
||||||
|
`UnsignedEnum`
|
||||||
|
- Initial CFDP support: Added PDU packet implementation.
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- The `EcssEnumeration` now requires the `UnsignedEnum` trait and only adds the `pfc` method to it.
|
||||||
|
- Renamed `byte_width` usages to `len` (part of new `UnsignedEnum` trait)
|
||||||
|
|
||||||
# [v0.5.4] 2023-02-12
|
# [v0.5.4] 2023-02-12
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
@ -2,6 +2,8 @@ use num_enum::{IntoPrimitive, TryFromPrimitive};
|
|||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub mod pdu;
|
||||||
|
|
||||||
pub const CFDP_VERSION_2_NAME: &str = "CCSDS 727.0-B-5";
|
pub const CFDP_VERSION_2_NAME: &str = "CCSDS 727.0-B-5";
|
||||||
pub const CFDP_VERSION_2: u8 = 0b001;
|
pub const CFDP_VERSION_2: u8 = 0b001;
|
||||||
|
|
||||||
|
271
src/cfdp/pdu/mod.rs
Normal file
271
src/cfdp/pdu/mod.rs
Normal file
@ -0,0 +1,271 @@
|
|||||||
|
use crate::cfdp::*;
|
||||||
|
use crate::util::{UnsignedByteField, UnsignedEnum};
|
||||||
|
use crate::{ByteConversionError, SizeMissmatch};
|
||||||
|
use core::fmt::{Display, Formatter};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::error::Error;
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub enum PduError {
|
||||||
|
ByteConversionError(ByteConversionError),
|
||||||
|
/// Found version ID invalid, not equal to [CFDP_VERSION_2].
|
||||||
|
CfdpVersionMissmatch(u8),
|
||||||
|
/// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported.
|
||||||
|
InvalidEntityLen(u8),
|
||||||
|
/// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported.
|
||||||
|
InvalidTransactionSeqNumLen(u8),
|
||||||
|
/// The first entry will be the source entity ID length, the second one the destination entity
|
||||||
|
/// ID length.
|
||||||
|
SourceDestIdLenMissmatch((usize, usize)),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Display for PduError {
|
||||||
|
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
|
||||||
|
match self {
|
||||||
|
PduError::InvalidEntityLen(raw_id) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Invalid entity ID length {raw_id}, only [1, 2, 4, 8] are allowed"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PduError::InvalidTransactionSeqNumLen(raw_id) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"Invalid transaction seq num length {raw_id}, only [1, 2, 4, 8] are allowed"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PduError::CfdpVersionMissmatch(raw) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"cfdp version missmatch, found {raw}, expected {CFDP_VERSION_2}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PduError::SourceDestIdLenMissmatch((src_len, dest_len)) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"missmatch of source length {src_len} and destination length {dest_len}"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
PduError::ByteConversionError(e) => {
|
||||||
|
write!(f, "low level byte conversion error: {e}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
impl Error for PduError {}
|
||||||
|
|
||||||
|
impl From<ByteConversionError> for PduError {
|
||||||
|
fn from(value: ByteConversionError) -> Self {
|
||||||
|
Self::ByteConversionError(value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Common configuration fields for a PDU.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub struct CommonPduConfig {
|
||||||
|
source_entity_id: UnsignedByteField,
|
||||||
|
dest_entity_id: UnsignedByteField,
|
||||||
|
transaction_seq_num: UnsignedByteField,
|
||||||
|
trans_mode: TransmissionMode,
|
||||||
|
file_flag: LargeFileFlag,
|
||||||
|
crc_flag: CrcFlag,
|
||||||
|
direction: Direction,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommonPduConfig {
|
||||||
|
pub fn new(
|
||||||
|
source_id: UnsignedByteField,
|
||||||
|
dest_id: UnsignedByteField,
|
||||||
|
transaction_seq_num: UnsignedByteField,
|
||||||
|
trans_mode: TransmissionMode,
|
||||||
|
file_flag: LargeFileFlag,
|
||||||
|
crc_flag: CrcFlag,
|
||||||
|
direction: Direction,
|
||||||
|
) -> Result<Self, PduError> {
|
||||||
|
if source_id.len() != dest_id.len() {
|
||||||
|
return Err(PduError::SourceDestIdLenMissmatch((
|
||||||
|
source_id.len(),
|
||||||
|
dest_id.len(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
Ok(Self {
|
||||||
|
source_entity_id: source_id,
|
||||||
|
dest_entity_id: dest_id,
|
||||||
|
transaction_seq_num,
|
||||||
|
trans_mode,
|
||||||
|
file_flag,
|
||||||
|
crc_flag,
|
||||||
|
direction,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Abstraction for the PDU header common to all CFDP PDUs
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub struct PduHeader {
|
||||||
|
pdu_type: PduType,
|
||||||
|
pdu_conf: CommonPduConfig,
|
||||||
|
seg_metadata_flag: SegmentMetadataFlag,
|
||||||
|
seg_ctrl: SegmentationControl,
|
||||||
|
pdu_datafield_len: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PduHeader {
|
||||||
|
pub fn new_for_file_data(
|
||||||
|
pdu_conf: CommonPduConfig,
|
||||||
|
seg_metadata_flag: SegmentMetadataFlag,
|
||||||
|
seg_ctrl: SegmentationControl,
|
||||||
|
pdu_datafield_len: u16,
|
||||||
|
) -> Self {
|
||||||
|
PduHeader {
|
||||||
|
pdu_type: PduType::FileData,
|
||||||
|
pdu_conf,
|
||||||
|
seg_metadata_flag,
|
||||||
|
seg_ctrl,
|
||||||
|
pdu_datafield_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_no_file_data(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
|
||||||
|
PduHeader {
|
||||||
|
pdu_type: PduType::FileData,
|
||||||
|
pdu_conf,
|
||||||
|
seg_metadata_flag: SegmentMetadataFlag::NotPresent,
|
||||||
|
seg_ctrl: SegmentationControl::NoRecordBoundaryPreservation,
|
||||||
|
pdu_datafield_len,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), PduError> {
|
||||||
|
if self.pdu_conf.source_entity_id.len() != self.pdu_conf.dest_entity_id.len() {
|
||||||
|
return Err(PduError::SourceDestIdLenMissmatch((
|
||||||
|
self.pdu_conf.source_entity_id.len(),
|
||||||
|
self.pdu_conf.dest_entity_id.len(),
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
if buf.len()
|
||||||
|
< 4 + self.pdu_conf.source_entity_id.len() + self.pdu_conf.transaction_seq_num.len()
|
||||||
|
{
|
||||||
|
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
||||||
|
found: buf.len(),
|
||||||
|
expected: 4,
|
||||||
|
})
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
let mut current_idx = 0;
|
||||||
|
buf[current_idx] = (CFDP_VERSION_2 << 5)
|
||||||
|
| ((self.pdu_type as u8) << 4)
|
||||||
|
| ((self.pdu_conf.direction as u8) << 3)
|
||||||
|
| ((self.pdu_conf.trans_mode as u8) << 2)
|
||||||
|
| ((self.pdu_conf.crc_flag as u8) << 1)
|
||||||
|
| (self.pdu_conf.file_flag as u8);
|
||||||
|
current_idx += 1;
|
||||||
|
buf[current_idx..current_idx + 2].copy_from_slice(&self.pdu_datafield_len.to_be_bytes());
|
||||||
|
current_idx += 2;
|
||||||
|
buf[current_idx] = ((self.seg_ctrl as u8) << 7)
|
||||||
|
| ((self.pdu_conf.source_entity_id.len() as u8) << 4)
|
||||||
|
| ((self.seg_metadata_flag as u8) << 3)
|
||||||
|
| (self.pdu_conf.transaction_seq_num.len() as u8);
|
||||||
|
self.pdu_conf.source_entity_id.write_to_be_bytes(
|
||||||
|
&mut buf[current_idx..current_idx + self.pdu_conf.source_entity_id.len()],
|
||||||
|
)?;
|
||||||
|
current_idx += self.pdu_conf.source_entity_id.len();
|
||||||
|
self.pdu_conf.transaction_seq_num.write_to_be_bytes(
|
||||||
|
&mut buf[current_idx..current_idx + self.pdu_conf.transaction_seq_num.len()],
|
||||||
|
)?;
|
||||||
|
current_idx += self.pdu_conf.transaction_seq_num.len();
|
||||||
|
self.pdu_conf.dest_entity_id.write_to_be_bytes(
|
||||||
|
&mut buf[current_idx..current_idx + self.pdu_conf.dest_entity_id.len()],
|
||||||
|
)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_be_bytes(buf: &[u8]) -> Result<Self, PduError> {
|
||||||
|
if buf.len() < 4 {
|
||||||
|
return Err(PduError::ByteConversionError(
|
||||||
|
ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
||||||
|
found: buf.len(),
|
||||||
|
expected: 4,
|
||||||
|
}),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let cfdp_version_raw = buf[0] >> 5 & 0b111;
|
||||||
|
if cfdp_version_raw != CFDP_VERSION_2 {
|
||||||
|
return Err(PduError::CfdpVersionMissmatch(cfdp_version_raw));
|
||||||
|
}
|
||||||
|
// Conversion for 1 bit value always works
|
||||||
|
let pdu_type = PduType::try_from((buf[0] >> 4) & 0b1).unwrap();
|
||||||
|
let direction = Direction::try_from((buf[0] >> 3) & 0b1).unwrap();
|
||||||
|
let trans_mode = TransmissionMode::try_from((buf[0] >> 2) & 0b1).unwrap();
|
||||||
|
let crc_flag = CrcFlag::try_from((buf[0] >> 1) & 0b1).unwrap();
|
||||||
|
let file_flag = LargeFileFlag::try_from(buf[0] & 0b1).unwrap();
|
||||||
|
let pdu_datafield_len = u16::from_be_bytes(buf[1..3].try_into().unwrap());
|
||||||
|
let seg_ctrl = SegmentationControl::try_from((buf[3] >> 7) & 0b1).unwrap();
|
||||||
|
let expected_len_entity_ids = ((buf[3] >> 4) & 0b111) as usize;
|
||||||
|
if (expected_len_entity_ids != 1)
|
||||||
|
&& (expected_len_entity_ids != 2)
|
||||||
|
&& (expected_len_entity_ids != 4)
|
||||||
|
&& (expected_len_entity_ids != 8)
|
||||||
|
{
|
||||||
|
return Err(PduError::InvalidEntityLen(expected_len_entity_ids as u8));
|
||||||
|
}
|
||||||
|
let seg_metadata_flag = SegmentMetadataFlag::try_from((buf[3] >> 3) & 0b1).unwrap();
|
||||||
|
let expected_len_seq_num = (buf[3] & 0b111) as usize;
|
||||||
|
if (expected_len_seq_num != 1)
|
||||||
|
&& (expected_len_seq_num != 2)
|
||||||
|
&& (expected_len_seq_num != 4)
|
||||||
|
&& (expected_len_seq_num != 8)
|
||||||
|
{
|
||||||
|
return Err(PduError::InvalidTransactionSeqNumLen(
|
||||||
|
expected_len_seq_num as u8,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
if buf.len() < (4 + 2 * expected_len_entity_ids + expected_len_seq_num) {
|
||||||
|
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
||||||
|
found: buf.len(),
|
||||||
|
expected: 4 + 2 * expected_len_entity_ids + expected_len_seq_num,
|
||||||
|
})
|
||||||
|
.into());
|
||||||
|
}
|
||||||
|
let mut current_idx = 4;
|
||||||
|
let source_id =
|
||||||
|
UnsignedByteField::new_from_be_bytes(expected_len_entity_ids, &buf[current_idx..])
|
||||||
|
.unwrap();
|
||||||
|
current_idx += expected_len_entity_ids;
|
||||||
|
let transaction_seq_num =
|
||||||
|
UnsignedByteField::new_from_be_bytes(expected_len_seq_num, &buf[current_idx..])
|
||||||
|
.unwrap();
|
||||||
|
current_idx += expected_len_seq_num;
|
||||||
|
let dest_id =
|
||||||
|
UnsignedByteField::new_from_be_bytes(expected_len_entity_ids, &buf[current_idx..])
|
||||||
|
.unwrap();
|
||||||
|
let common_pdu_conf = CommonPduConfig::new(
|
||||||
|
source_id,
|
||||||
|
dest_id,
|
||||||
|
transaction_seq_num,
|
||||||
|
trans_mode,
|
||||||
|
file_flag,
|
||||||
|
crc_flag,
|
||||||
|
direction,
|
||||||
|
)
|
||||||
|
.unwrap();
|
||||||
|
Ok(PduHeader {
|
||||||
|
pdu_type,
|
||||||
|
pdu_conf: common_pdu_conf,
|
||||||
|
seg_metadata_flag,
|
||||||
|
seg_ctrl,
|
||||||
|
pdu_datafield_len,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
pub fn pdu_type(&self) -> PduType {
|
||||||
|
self.pdu_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn common_pdu_conf(&self) -> &CommonPduConfig {
|
||||||
|
&self.pdu_conf
|
||||||
|
}
|
||||||
|
}
|
@ -3,7 +3,7 @@
|
|||||||
//!
|
//!
|
||||||
//! You can find the PUS telecommand definitions in the [crate::tc] module and ithe PUS telemetry definitions
|
//! You can find the PUS telecommand definitions in the [crate::tc] module and ithe PUS telemetry definitions
|
||||||
//! inside the [crate::tm] module.
|
//! inside the [crate::tm] module.
|
||||||
use crate::{ByteConversionError, CcsdsPacket, SizeMissmatch};
|
use crate::{ByteConversionError, CcsdsPacket};
|
||||||
use core::fmt::{Debug, Display, Formatter};
|
use core::fmt::{Debug, Display, Formatter};
|
||||||
use core::mem::size_of;
|
use core::mem::size_of;
|
||||||
use crc::{Crc, CRC_16_IBM_3740};
|
use crc::{Crc, CRC_16_IBM_3740};
|
||||||
@ -291,6 +291,7 @@ macro_rules! sp_header_impls {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
use crate::util::{GenericUnsignedByteField, ToBeBytes, UnsignedEnum};
|
||||||
pub(crate) use ccsds_impl;
|
pub(crate) use ccsds_impl;
|
||||||
pub(crate) use sp_header_impls;
|
pub(crate) use sp_header_impls;
|
||||||
|
|
||||||
@ -298,66 +299,17 @@ pub(crate) use sp_header_impls;
|
|||||||
/// and an unsigned value. The trait makes no assumptions about the actual type of the unsigned
|
/// 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
|
/// value and only requires implementors to implement a function which writes the enumeration into
|
||||||
/// a raw byte format.
|
/// a raw byte format.
|
||||||
pub trait EcssEnumeration {
|
pub trait EcssEnumeration: UnsignedEnum {
|
||||||
/// Packet Format Code, which denotes the number of bits of the enumeration
|
/// Packet Format Code, which denotes the number of bits of the enumeration
|
||||||
fn pfc(&self) -> u8;
|
fn pfc(&self) -> u8;
|
||||||
fn byte_width(&self) -> usize {
|
|
||||||
(self.pfc() / 8) as usize
|
|
||||||
}
|
|
||||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait EcssEnumerationExt: EcssEnumeration + Debug + Copy + Clone + PartialEq + Eq {}
|
pub trait EcssEnumerationExt: EcssEnumeration + Debug + Copy + Clone + PartialEq + Eq {}
|
||||||
|
|
||||||
pub trait ToBeBytes {
|
|
||||||
type ByteArray: AsRef<[u8]>;
|
|
||||||
fn to_be_bytes(&self) -> Self::ByteArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ToBeBytes for () {
|
|
||||||
type ByteArray = [u8; 0];
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct GenericEcssEnumWrapper<TYPE> {
|
pub struct GenericEcssEnumWrapper<TYPE> {
|
||||||
val: TYPE,
|
field: GenericUnsignedByteField<TYPE>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TYPE> GenericEcssEnumWrapper<TYPE> {
|
impl<TYPE> GenericEcssEnumWrapper<TYPE> {
|
||||||
@ -366,7 +318,19 @@ impl<TYPE> GenericEcssEnumWrapper<TYPE> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn new(val: TYPE) -> Self {
|
pub fn new(val: TYPE) -> Self {
|
||||||
Self { val }
|
Self {
|
||||||
|
field: GenericUnsignedByteField::new(val),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TYPE: ToBeBytes> UnsignedEnum for GenericEcssEnumWrapper<TYPE> {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
(self.pfc() / 8) as usize
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> {
|
||||||
|
self.field.write_to_be_bytes(buf)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -374,17 +338,6 @@ impl<TYPE: ToBeBytes> EcssEnumeration for GenericEcssEnumWrapper<TYPE> {
|
|||||||
fn pfc(&self) -> u8 {
|
fn pfc(&self) -> u8 {
|
||||||
size_of::<TYPE>() as u8 * 8_u8
|
size_of::<TYPE>() as u8 * 8_u8
|
||||||
}
|
}
|
||||||
|
|
||||||
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> {
|
|
||||||
if buf.len() < self.byte_width() {
|
|
||||||
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
|
||||||
found: buf.len(),
|
|
||||||
expected: self.byte_width(),
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
buf[0..self.byte_width()].copy_from_slice(self.val.to_be_bytes().as_ref());
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<TYPE: Debug + Copy + Clone + PartialEq + Eq + ToBeBytes> EcssEnumerationExt
|
impl<TYPE: Debug + Copy + Clone + PartialEq + Eq + ToBeBytes> EcssEnumerationExt
|
||||||
@ -399,7 +352,7 @@ pub type EcssEnumU64 = GenericEcssEnumWrapper<u64>;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use crate::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, EcssEnumeration};
|
use crate::ecss::{EcssEnumU16, EcssEnumU32, EcssEnumU8, UnsignedEnum};
|
||||||
use crate::ByteConversionError;
|
use crate::ByteConversionError;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -60,8 +60,10 @@ extern crate alloc;
|
|||||||
extern crate std;
|
extern crate std;
|
||||||
|
|
||||||
use crate::ecss::CCSDS_HEADER_LEN;
|
use crate::ecss::CCSDS_HEADER_LEN;
|
||||||
use core::fmt::{Display, Formatter};
|
use core::fmt::{Debug, Display, Formatter};
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
|
#[cfg(not(feature = "std"))]
|
||||||
|
use num_traits::Unsigned;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
|
|
||||||
@ -73,6 +75,7 @@ pub mod ecss;
|
|||||||
pub mod tc;
|
pub mod tc;
|
||||||
pub mod time;
|
pub mod time;
|
||||||
pub mod tm;
|
pub mod tm;
|
||||||
|
pub mod util;
|
||||||
|
|
||||||
mod private {
|
mod private {
|
||||||
pub trait Sealed {}
|
pub trait Sealed {}
|
||||||
|
257
src/util.rs
Normal file
257
src/util.rs
Normal file
@ -0,0 +1,257 @@
|
|||||||
|
use crate::{ByteConversionError, SizeMissmatch};
|
||||||
|
use core::fmt::Debug;
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
pub trait ToBeBytes {
|
||||||
|
type ByteArray: AsRef<[u8]>;
|
||||||
|
/// Length when written to big endian bytes.
|
||||||
|
fn written_len(&self) -> usize;
|
||||||
|
fn to_be_bytes(&self) -> Self::ByteArray;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToBeBytes for () {
|
||||||
|
type ByteArray = [u8; 0];
|
||||||
|
|
||||||
|
fn written_len(&self) -> usize {
|
||||||
|
0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||||
|
[]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToBeBytes for u8 {
|
||||||
|
type ByteArray = [u8; 1];
|
||||||
|
|
||||||
|
fn written_len(&self) -> usize {
|
||||||
|
1
|
||||||
|
}
|
||||||
|
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||||
|
u8::to_be_bytes(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToBeBytes for u16 {
|
||||||
|
type ByteArray = [u8; 2];
|
||||||
|
|
||||||
|
fn written_len(&self) -> usize {
|
||||||
|
2
|
||||||
|
}
|
||||||
|
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||||
|
u16::to_be_bytes(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToBeBytes for u32 {
|
||||||
|
type ByteArray = [u8; 4];
|
||||||
|
|
||||||
|
fn written_len(&self) -> usize {
|
||||||
|
4
|
||||||
|
}
|
||||||
|
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||||
|
u32::to_be_bytes(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToBeBytes for u64 {
|
||||||
|
type ByteArray = [u8; 8];
|
||||||
|
|
||||||
|
fn written_len(&self) -> usize {
|
||||||
|
8
|
||||||
|
}
|
||||||
|
fn to_be_bytes(&self) -> Self::ByteArray {
|
||||||
|
u64::to_be_bytes(*self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(clippy::len_without_is_empty)]
|
||||||
|
pub trait UnsignedEnum {
|
||||||
|
fn len(&self) -> usize;
|
||||||
|
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError>;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait UnsignedEnumExt: UnsignedEnum + Debug + Copy + Clone + PartialEq + Eq {}
|
||||||
|
|
||||||
|
/// Type erased variant.
|
||||||
|
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub struct UnsignedByteField {
|
||||||
|
width: usize,
|
||||||
|
value: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnsignedByteField {
|
||||||
|
pub fn new(width: usize, value: u64) -> Self {
|
||||||
|
Self { width, value }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new_from_be_bytes(width: usize, buf: &[u8]) -> Result<Self, ByteConversionError> {
|
||||||
|
if width > buf.len() {
|
||||||
|
return Err(ByteConversionError::FromSliceTooSmall(SizeMissmatch {
|
||||||
|
found: buf.len(),
|
||||||
|
expected: width,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
match width {
|
||||||
|
0 => Ok(Self::new(0, 0)),
|
||||||
|
1 => Ok(Self::new(1, buf[0] as u64)),
|
||||||
|
2 => Ok(Self::new(
|
||||||
|
2,
|
||||||
|
u16::from_be_bytes(buf[0..2].try_into().unwrap()) as u64,
|
||||||
|
)),
|
||||||
|
4 => Ok(Self::new(
|
||||||
|
2,
|
||||||
|
u32::from_be_bytes(buf[0..4].try_into().unwrap()) as u64,
|
||||||
|
)),
|
||||||
|
8 => Ok(Self::new(
|
||||||
|
2,
|
||||||
|
u64::from_be_bytes(buf[0..8].try_into().unwrap()),
|
||||||
|
)),
|
||||||
|
// TODO: I don't know whether it is a good idea to panic here.
|
||||||
|
_ => panic!("invalid width"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl UnsignedEnum for UnsignedByteField {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.width
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> {
|
||||||
|
if buf.len() < self.len() {
|
||||||
|
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
||||||
|
expected: self.len(),
|
||||||
|
found: buf.len(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
match self.len() {
|
||||||
|
0 => Ok(()),
|
||||||
|
1 => {
|
||||||
|
let u8 = UnsignedU8::try_from(*self).unwrap();
|
||||||
|
u8.write_to_be_bytes(buf)
|
||||||
|
}
|
||||||
|
2 => {
|
||||||
|
let u16 = UnsignedU16::try_from(*self).unwrap();
|
||||||
|
u16.write_to_be_bytes(buf)
|
||||||
|
}
|
||||||
|
4 => {
|
||||||
|
let u32 = UnsignedU32::try_from(*self).unwrap();
|
||||||
|
u32.write_to_be_bytes(buf)
|
||||||
|
}
|
||||||
|
8 => {
|
||||||
|
let u64 = UnsignedU64::try_from(*self).unwrap();
|
||||||
|
u64.write_to_be_bytes(buf)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
// The API does not allow this.
|
||||||
|
panic!("unexpected written length");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
|
||||||
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
|
pub struct GenericUnsignedByteField<TYPE> {
|
||||||
|
val: TYPE,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TYPE> GenericUnsignedByteField<TYPE> {
|
||||||
|
pub fn new(val: TYPE) -> Self {
|
||||||
|
Self { val }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<TYPE: ToBeBytes> UnsignedEnum for GenericUnsignedByteField<TYPE> {
|
||||||
|
fn len(&self) -> usize {
|
||||||
|
self.val.written_len()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_to_be_bytes(&self, buf: &mut [u8]) -> Result<(), ByteConversionError> {
|
||||||
|
if buf.len() < self.len() {
|
||||||
|
return Err(ByteConversionError::ToSliceTooSmall(SizeMissmatch {
|
||||||
|
found: buf.len(),
|
||||||
|
expected: self.len(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
buf[0..self.len()].copy_from_slice(self.val.to_be_bytes().as_ref());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type UnsignedByteFieldEmpty = GenericUnsignedByteField<()>;
|
||||||
|
pub type UnsignedU8 = GenericUnsignedByteField<u8>;
|
||||||
|
pub type UnsignedU16 = GenericUnsignedByteField<u16>;
|
||||||
|
pub type UnsignedU32 = GenericUnsignedByteField<u32>;
|
||||||
|
pub type UnsignedU64 = GenericUnsignedByteField<u64>;
|
||||||
|
|
||||||
|
impl From<UnsignedU8> for UnsignedByteField {
|
||||||
|
fn from(value: UnsignedU8) -> Self {
|
||||||
|
Self::new(1, value.val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<UnsignedByteField> for UnsignedU8 {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: UnsignedByteField) -> Result<Self, ()> {
|
||||||
|
if value.value > 2_u64.pow(8) - 1 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(Self::new(value.value as u8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UnsignedU16> for UnsignedByteField {
|
||||||
|
fn from(value: UnsignedU16) -> Self {
|
||||||
|
Self::new(2, value.val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<UnsignedByteField> for UnsignedU16 {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: UnsignedByteField) -> Result<Self, ()> {
|
||||||
|
if value.value > 2_u64.pow(16) - 1 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(Self::new(value.value as u16))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UnsignedU32> for UnsignedByteField {
|
||||||
|
fn from(value: UnsignedU32) -> Self {
|
||||||
|
Self::new(4, value.val as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<UnsignedByteField> for UnsignedU32 {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: UnsignedByteField) -> Result<Self, ()> {
|
||||||
|
if value.value > 2_u64.pow(32) - 1 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(Self::new(value.value as u32))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UnsignedU64> for UnsignedByteField {
|
||||||
|
fn from(value: UnsignedU64) -> Self {
|
||||||
|
Self::new(8, value.val)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<UnsignedByteField> for UnsignedU64 {
|
||||||
|
type Error = ();
|
||||||
|
|
||||||
|
fn try_from(value: UnsignedByteField) -> Result<Self, ()> {
|
||||||
|
if value.value > 2_u64.pow(64) - 1 {
|
||||||
|
return Err(());
|
||||||
|
}
|
||||||
|
Ok(Self::new(value.value))
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user