docs and minor cfdp change #187

Merged
muellerr merged 1 commits from cfdp-update-docs into main 2025-11-04 18:54:40 +01:00
15 changed files with 338 additions and 96 deletions

View File

@@ -13,6 +13,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
## Changed ## Changed
- `CdsCommon` renamed to `CdsBase` - `CdsCommon` renamed to `CdsBase`
- cfdp: Removed `FileDirectiveType` variant `*Pdu` suffix
- Simplified CDS short timestamp, contains one less field which reduced serialization length. - Simplified CDS short timestamp, contains one less field which reduced serialization length.
- Renamed `UnsignedEnum::value` to `UnsignedEnum::value_raw`, `value` is reserved for the `const` - Renamed `UnsignedEnum::value` to `UnsignedEnum::value_raw`, `value` is reserved for the `const`
value getter. value getter.

View File

@@ -1,9 +1,12 @@
all: check build embedded test clippy fmt docs coverage all: check build embedded test clippy check-fmt docs coverage
clippy: clippy:
cargo clippy -- -D warnings cargo clippy -- -D warnings
fmt: fmt:
cargo fmt --all
check-fmt:
cargo fmt --all -- --check cargo fmt --all -- --check
check: check:

View File

@@ -183,7 +183,7 @@ impl<'data> Lv<'data> {
} }
#[cfg(test)] #[cfg(test)]
pub mod tests { mod tests {
use alloc::string::ToString; use alloc::string::ToString;
use super::*; use super::*;

View File

@@ -1,4 +1,5 @@
//! Low-level CCSDS File Delivery Protocol (CFDP) support according to [CCSDS 727.0-B-5](https://public.ccsds.org/Pubs/727x0b5.pdf). //! Low-level CCSDS File Delivery Protocol (CFDP) support according to [CCSDS 727.0-B-5](https://public.ccsds.org/Pubs/727x0b5.pdf).
#![warn(missing_docs)]
use crate::ByteConversionError; use crate::ByteConversionError;
use num_enum::{IntoPrimitive, TryFromPrimitive}; use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "serde")] #[cfg(feature = "serde")]

View File

@@ -1,3 +1,4 @@
//! # Acknowledgement (ACK) PDU packet implementation.
use crate::{ use crate::{
cfdp::{ConditionCode, CrcFlag, Direction, TransactionStatus}, cfdp::{ConditionCode, CrcFlag, Direction, TransactionStatus},
ByteConversionError, ByteConversionError,
@@ -10,6 +11,7 @@ use super::{
#[cfg(feature = "serde")] #[cfg(feature = "serde")]
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
/// Invalid [FileDirectiveType] of the acknowledged PDU error.
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("invalid directive code of acknowledged PDU")] #[error("invalid directive code of acknowledged PDU")]
pub struct InvalidAckedDirectiveCodeError(pub FileDirectiveType); pub struct InvalidAckedDirectiveCodeError(pub FileDirectiveType);
@@ -28,15 +30,16 @@ pub struct AckPdu {
} }
impl AckPdu { impl AckPdu {
/// Constructor.
pub fn new( pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
directive_code_of_acked_pdu: FileDirectiveType, directive_code_of_acked_pdu: FileDirectiveType,
condition_code: ConditionCode, condition_code: ConditionCode,
transaction_status: TransactionStatus, transaction_status: TransactionStatus,
) -> Result<Self, InvalidAckedDirectiveCodeError> { ) -> Result<Self, InvalidAckedDirectiveCodeError> {
if directive_code_of_acked_pdu == FileDirectiveType::EofPdu { if directive_code_of_acked_pdu == FileDirectiveType::Eof {
pdu_header.pdu_conf.direction = Direction::TowardsSender; pdu_header.pdu_conf.direction = Direction::TowardsSender;
} else if directive_code_of_acked_pdu == FileDirectiveType::FinishedPdu { } else if directive_code_of_acked_pdu == FileDirectiveType::Finished {
pdu_header.pdu_conf.direction = Direction::TowardsReceiver; pdu_header.pdu_conf.direction = Direction::TowardsReceiver;
} else { } else {
return Err(InvalidAckedDirectiveCodeError(directive_code_of_acked_pdu)); return Err(InvalidAckedDirectiveCodeError(directive_code_of_acked_pdu));
@@ -52,6 +55,9 @@ impl AckPdu {
Ok(ack_pdu) Ok(ack_pdu)
} }
/// Constructor for an ACK PDU acknowledging an EOF PDU.
///
/// Relevant for the file receiver.
pub fn new_for_eof_pdu( pub fn new_for_eof_pdu(
pdu_header: PduHeader, pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,
@@ -60,13 +66,16 @@ impl AckPdu {
// Unwrap okay here, [new] can only fail on invalid directive codes. // Unwrap okay here, [new] can only fail on invalid directive codes.
Self::new( Self::new(
pdu_header, pdu_header,
FileDirectiveType::EofPdu, FileDirectiveType::Eof,
condition_code, condition_code,
transaction_status, transaction_status,
) )
.unwrap() .unwrap()
} }
/// Constructor for an ACK PDU acknowledging a Finished PDU.
///
/// Relevant for the file sender.
pub fn new_for_finished_pdu( pub fn new_for_finished_pdu(
pdu_header: PduHeader, pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,
@@ -75,28 +84,32 @@ impl AckPdu {
// Unwrap okay here, [new] can only fail on invalid directive codes. // Unwrap okay here, [new] can only fail on invalid directive codes.
Self::new( Self::new(
pdu_header, pdu_header,
FileDirectiveType::FinishedPdu, FileDirectiveType::Finished,
condition_code, condition_code,
transaction_status, transaction_status,
) )
.unwrap() .unwrap()
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Directive code of the acknowledged PDU.
#[inline] #[inline]
pub fn directive_code_of_acked_pdu(&self) -> FileDirectiveType { pub fn directive_code_of_acked_pdu(&self) -> FileDirectiveType {
self.directive_code_of_acked_pdu self.directive_code_of_acked_pdu
} }
/// Condition code.
#[inline] #[inline]
pub fn condition_code(&self) -> ConditionCode { pub fn condition_code(&self) -> ConditionCode {
self.condition_code self.condition_code
} }
/// Transaction status.
#[inline] #[inline]
pub fn transaction_status(&self) -> TransactionStatus { pub fn transaction_status(&self) -> TransactionStatus {
self.transaction_status self.transaction_status
@@ -110,6 +123,7 @@ impl AckPdu {
3 3
} }
/// Construct [Self] from the provided byte slice.
pub fn from_bytes(buf: &[u8]) -> Result<AckPdu, PduError> { pub fn from_bytes(buf: &[u8]) -> Result<AckPdu, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
@@ -117,13 +131,13 @@ impl AckPdu {
let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
PduError::InvalidDirectiveType { PduError::InvalidDirectiveType {
found: buf[current_idx], found: buf[current_idx],
expected: Some(FileDirectiveType::AckPdu), expected: Some(FileDirectiveType::Ack),
} }
})?; })?;
if directive_type != FileDirectiveType::AckPdu { if directive_type != FileDirectiveType::Ack {
return Err(PduError::WrongDirectiveType { return Err(PduError::WrongDirectiveType {
found: directive_type, found: directive_type,
expected: FileDirectiveType::AckPdu, expected: FileDirectiveType::Ack,
}); });
} }
current_idx += 1; current_idx += 1;
@@ -134,8 +148,8 @@ impl AckPdu {
expected: None, expected: None,
} }
})?; })?;
if acked_directive_type != FileDirectiveType::EofPdu if acked_directive_type != FileDirectiveType::Eof
&& acked_directive_type != FileDirectiveType::FinishedPdu && acked_directive_type != FileDirectiveType::Finished
{ {
return Err(PduError::InvalidDirectiveType { return Err(PduError::InvalidDirectiveType {
found: acked_directive_type as u8, found: acked_directive_type as u8,
@@ -167,11 +181,11 @@ impl AckPdu {
.into()); .into());
} }
let mut current_idx = self.pdu_header.write_to_bytes(buf)?; let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
buf[current_idx] = FileDirectiveType::AckPdu as u8; buf[current_idx] = FileDirectiveType::Ack as u8;
current_idx += 1; current_idx += 1;
buf[current_idx] = (self.directive_code_of_acked_pdu as u8) << 4; buf[current_idx] = (self.directive_code_of_acked_pdu as u8) << 4;
if self.directive_code_of_acked_pdu == FileDirectiveType::FinishedPdu { if self.directive_code_of_acked_pdu == FileDirectiveType::Finished {
// This is the directive subtype code. It needs to be set to 0b0001 if the ACK PDU // This is the directive subtype code. It needs to be set to 0b0001 if the ACK PDU
// acknowledges a Finished PDU, and to 0b0000 otherwise. // acknowledges a Finished PDU, and to 0b0000 otherwise.
buf[current_idx] |= 0b0001; buf[current_idx] |= 0b0001;
@@ -185,6 +199,7 @@ impl AckPdu {
Ok(current_idx) Ok(current_idx)
} }
/// Length of the written PDU in bytes.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len() self.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -198,7 +213,7 @@ impl CfdpPdu for AckPdu {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::AckPdu) Some(FileDirectiveType::Ack)
} }
} }
@@ -230,10 +245,7 @@ mod tests {
assert_eq!(ack_pdu.crc_flag(), expected_crc_flag); assert_eq!(ack_pdu.crc_flag(), expected_crc_flag);
assert_eq!(ack_pdu.file_flag(), LargeFileFlag::Normal); assert_eq!(ack_pdu.file_flag(), LargeFileFlag::Normal);
assert_eq!(ack_pdu.pdu_type(), PduType::FileDirective); assert_eq!(ack_pdu.pdu_type(), PduType::FileDirective);
assert_eq!( assert_eq!(ack_pdu.file_directive_type(), Some(FileDirectiveType::Ack));
ack_pdu.file_directive_type(),
Some(FileDirectiveType::AckPdu)
);
assert_eq!(ack_pdu.transmission_mode(), TransmissionMode::Acknowledged); assert_eq!(ack_pdu.transmission_mode(), TransmissionMode::Acknowledged);
assert_eq!(ack_pdu.direction(), expected_dir); assert_eq!(ack_pdu.direction(), expected_dir);
assert_eq!(ack_pdu.source_id(), TEST_SRC_ID.into()); assert_eq!(ack_pdu.source_id(), TEST_SRC_ID.into());
@@ -247,14 +259,14 @@ mod tests {
let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0); let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
let ack_pdu = AckPdu::new( let ack_pdu = AckPdu::new(
pdu_header, pdu_header,
FileDirectiveType::FinishedPdu, FileDirectiveType::Finished,
ConditionCode::NoError, ConditionCode::NoError,
TransactionStatus::Active, TransactionStatus::Active,
) )
.expect("creating ACK PDU failed"); .expect("creating ACK PDU failed");
assert_eq!( assert_eq!(
ack_pdu.directive_code_of_acked_pdu(), ack_pdu.directive_code_of_acked_pdu(),
FileDirectiveType::FinishedPdu FileDirectiveType::Finished
); );
verify_state(&ack_pdu, CrcFlag::NoCrc, Direction::TowardsReceiver); verify_state(&ack_pdu, CrcFlag::NoCrc, Direction::TowardsReceiver);
} }
@@ -273,8 +285,8 @@ mod tests {
assert_eq!(written, ack_pdu.len_written()); assert_eq!(written, ack_pdu.len_written());
verify_raw_header(ack_pdu.pdu_header(), &buf); verify_raw_header(ack_pdu.pdu_header(), &buf);
assert_eq!(buf[7], FileDirectiveType::AckPdu as u8); assert_eq!(buf[7], FileDirectiveType::Ack as u8);
assert_eq!((buf[8] >> 4) & 0b1111, FileDirectiveType::FinishedPdu as u8); assert_eq!((buf[8] >> 4) & 0b1111, FileDirectiveType::Finished as u8);
assert_eq!(buf[8] & 0b1111, 0b0001); assert_eq!(buf[8] & 0b1111, 0b0001);
assert_eq!(buf[9] >> 4 & 0b1111, condition_code as u8); assert_eq!(buf[9] >> 4 & 0b1111, condition_code as u8);
assert_eq!(buf[9] & 0b11, transaction_status as u8); assert_eq!(buf[9] & 0b11, transaction_status as u8);
@@ -292,7 +304,7 @@ mod tests {
let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0); let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
let ack_pdu = AckPdu::new( let ack_pdu = AckPdu::new(
pdu_header, pdu_header,
FileDirectiveType::FinishedPdu, FileDirectiveType::Finished,
ConditionCode::NoError, ConditionCode::NoError,
TransactionStatus::Active, TransactionStatus::Active,
) )
@@ -320,12 +332,12 @@ mod tests {
assert_eq!( assert_eq!(
AckPdu::new( AckPdu::new(
pdu_header, pdu_header,
FileDirectiveType::MetadataPdu, FileDirectiveType::Metadata,
ConditionCode::NoError, ConditionCode::NoError,
TransactionStatus::Active, TransactionStatus::Active,
) )
.unwrap_err(), .unwrap_err(),
InvalidAckedDirectiveCodeError(FileDirectiveType::MetadataPdu) InvalidAckedDirectiveCodeError(FileDirectiveType::Metadata)
); );
} }
@@ -372,7 +384,7 @@ mod tests {
); );
assert_eq!( assert_eq!(
ack_pdu.directive_code_of_acked_pdu(), ack_pdu.directive_code_of_acked_pdu(),
FileDirectiveType::EofPdu FileDirectiveType::Eof
); );
verify_state(&ack_pdu, CrcFlag::WithCrc, Direction::TowardsSender); verify_state(&ack_pdu, CrcFlag::WithCrc, Direction::TowardsSender);
} }

View File

@@ -1,3 +1,4 @@
//! # End-of-File (EOF) PDU packet implementation.
use crate::cfdp::pdu::{ use crate::cfdp::pdu::{
add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field, add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field,
FileDirectiveType, PduError, PduHeader, FileDirectiveType, PduError, PduHeader,
@@ -25,6 +26,7 @@ pub struct EofPdu {
} }
impl EofPdu { impl EofPdu {
/// Constructor.
pub fn new( pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,
@@ -45,6 +47,7 @@ impl EofPdu {
eof_pdu eof_pdu
} }
/// Constructor for no error EOF PDUs.
pub fn new_no_error(pdu_header: PduHeader, file_checksum: u32, file_size: u64) -> Self { pub fn new_no_error(pdu_header: PduHeader, file_checksum: u32, file_size: u64) -> Self {
Self::new( Self::new(
pdu_header, pdu_header,
@@ -55,21 +58,25 @@ impl EofPdu {
) )
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Condition code.
#[inline] #[inline]
pub fn condition_code(&self) -> ConditionCode { pub fn condition_code(&self) -> ConditionCode {
self.condition_code self.condition_code
} }
/// File checksum.
#[inline] #[inline]
pub fn file_checksum(&self) -> u32 { pub fn file_checksum(&self) -> u32 {
self.file_checksum self.file_checksum
} }
/// File size.
#[inline] #[inline]
pub fn file_size(&self) -> u64 { pub fn file_size(&self) -> u64 {
self.file_size self.file_size
@@ -90,6 +97,7 @@ impl EofPdu {
len len
} }
/// Construct [Self] from the provided byte slice.
pub fn from_bytes(buf: &[u8]) -> Result<EofPdu, PduError> { pub fn from_bytes(buf: &[u8]) -> Result<EofPdu, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
@@ -102,13 +110,13 @@ impl EofPdu {
let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
PduError::InvalidDirectiveType { PduError::InvalidDirectiveType {
found: buf[current_idx], found: buf[current_idx],
expected: Some(FileDirectiveType::EofPdu), expected: Some(FileDirectiveType::Eof),
} }
})?; })?;
if directive_type != FileDirectiveType::EofPdu { if directive_type != FileDirectiveType::Eof {
return Err(PduError::WrongDirectiveType { return Err(PduError::WrongDirectiveType {
found: directive_type, found: directive_type,
expected: FileDirectiveType::EofPdu, expected: FileDirectiveType::Eof,
}); });
} }
current_idx += 1; current_idx += 1;
@@ -145,7 +153,7 @@ impl EofPdu {
.into()); .into());
} }
let mut current_idx = self.pdu_header.write_to_bytes(buf)?; let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
buf[current_idx] = FileDirectiveType::EofPdu as u8; buf[current_idx] = FileDirectiveType::Eof as u8;
current_idx += 1; current_idx += 1;
buf[current_idx] = (self.condition_code as u8) << 4; buf[current_idx] = (self.condition_code as u8) << 4;
current_idx += 1; current_idx += 1;
@@ -165,6 +173,7 @@ impl EofPdu {
Ok(current_idx) Ok(current_idx)
} }
/// Length of the written PDU in bytes.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len() self.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -178,7 +187,7 @@ impl CfdpPdu for EofPdu {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::EofPdu) Some(FileDirectiveType::Eof)
} }
} }
@@ -221,10 +230,7 @@ mod tests {
assert_eq!(eof_pdu.crc_flag(), crc_flag); assert_eq!(eof_pdu.crc_flag(), crc_flag);
assert_eq!(eof_pdu.file_flag(), file_flag); assert_eq!(eof_pdu.file_flag(), file_flag);
assert_eq!(eof_pdu.pdu_type(), PduType::FileDirective); assert_eq!(eof_pdu.pdu_type(), PduType::FileDirective);
assert_eq!( assert_eq!(eof_pdu.file_directive_type(), Some(FileDirectiveType::Eof));
eof_pdu.file_directive_type(),
Some(FileDirectiveType::EofPdu)
);
assert_eq!(eof_pdu.transmission_mode(), TransmissionMode::Acknowledged); assert_eq!(eof_pdu.transmission_mode(), TransmissionMode::Acknowledged);
assert_eq!(eof_pdu.direction(), Direction::TowardsReceiver); assert_eq!(eof_pdu.direction(), Direction::TowardsReceiver);
assert_eq!(eof_pdu.source_id(), TEST_SRC_ID.into()); assert_eq!(eof_pdu.source_id(), TEST_SRC_ID.into());
@@ -253,7 +259,7 @@ mod tests {
assert_eq!(written, eof_pdu.len_written()); assert_eq!(written, eof_pdu.len_written());
verify_raw_header(eof_pdu.pdu_header(), &buf); verify_raw_header(eof_pdu.pdu_header(), &buf);
let mut current_idx = eof_pdu.pdu_header().header_len(); let mut current_idx = eof_pdu.pdu_header().header_len();
buf[current_idx] = FileDirectiveType::EofPdu as u8; buf[current_idx] = FileDirectiveType::Eof as u8;
current_idx += 1; current_idx += 1;
assert_eq!( assert_eq!(
(buf[current_idx] >> 4) & 0b1111, (buf[current_idx] >> 4) & 0b1111,

View File

@@ -1,3 +1,4 @@
//! # File Data PDU packet implementation
use crate::cfdp::pdu::{ use crate::cfdp::pdu::{
add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field, add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field,
PduError, PduHeader, PduError, PduHeader,
@@ -10,18 +11,24 @@ use serde::{Deserialize, Serialize};
use super::{CfdpPdu, FileDirectiveType, WritablePduPacket}; use super::{CfdpPdu, FileDirectiveType, WritablePduPacket};
/// Record continuation state for segment metadata.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u2, exhaustive = true)] #[bitbybit::bitenum(u2, exhaustive = true)]
#[repr(u8)] #[repr(u8)]
pub enum RecordContinuationState { pub enum RecordContinuationState {
/// No start and no end.
NoStartNoEnd = 0b00, NoStartNoEnd = 0b00,
/// Start without end.
StartWithoutEnd = 0b01, StartWithoutEnd = 0b01,
/// End without start.
EndWithoutStart = 0b10, EndWithoutStart = 0b10,
/// Start and end.
StartAndEnd = 0b11, StartAndEnd = 0b11,
} }
/// Segment metadata structure.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SegmentMetadata<'seg_meta> { pub struct SegmentMetadata<'seg_meta> {
@@ -30,6 +37,7 @@ pub struct SegmentMetadata<'seg_meta> {
} }
impl<'seg_meta> SegmentMetadata<'seg_meta> { impl<'seg_meta> SegmentMetadata<'seg_meta> {
/// Constructor.
pub fn new( pub fn new(
record_continuation_state: RecordContinuationState, record_continuation_state: RecordContinuationState,
metadata: Option<&'seg_meta [u8]>, metadata: Option<&'seg_meta [u8]>,
@@ -45,16 +53,19 @@ impl<'seg_meta> SegmentMetadata<'seg_meta> {
}) })
} }
/// Record continuation state.
#[inline] #[inline]
pub fn record_continuation_state(&self) -> RecordContinuationState { pub fn record_continuation_state(&self) -> RecordContinuationState {
self.record_continuation_state self.record_continuation_state
} }
/// Raw metadata slice.
#[inline] #[inline]
pub fn metadata(&self) -> Option<&'seg_meta [u8]> { pub fn metadata(&self) -> Option<&'seg_meta [u8]> {
self.metadata self.metadata
} }
/// Length of the written segment metadata structure.
#[inline] #[inline]
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
// Map empty metadata to 0 and slice to its length. // Map empty metadata to 0 and slice to its length.
@@ -169,24 +180,27 @@ pub struct FileDataPdu<'seg_meta, 'file_data> {
} }
impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> { impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> {
/// Constructor for a file data PDU including segment metadata.
pub fn new_with_seg_metadata( pub fn new_with_seg_metadata(
pdu_header: PduHeader, pdu_header: PduHeader,
segment_metadata: SegmentMetadata<'seg_meta>, segment_metadata: SegmentMetadata<'seg_meta>,
offset: u64, offset: u64,
file_data: &'file_data [u8], file_data: &'file_data [u8],
) -> Self { ) -> Self {
Self::new_generic(pdu_header, Some(segment_metadata), offset, file_data) Self::new(pdu_header, Some(segment_metadata), offset, file_data)
} }
/// Constructor for a file data PDU without segment metadata.
pub fn new_no_seg_metadata( pub fn new_no_seg_metadata(
pdu_header: PduHeader, pdu_header: PduHeader,
offset: u64, offset: u64,
file_data: &'file_data [u8], file_data: &'file_data [u8],
) -> Self { ) -> Self {
Self::new_generic(pdu_header, None, offset, file_data) Self::new(pdu_header, None, offset, file_data)
} }
pub fn new_generic( /// Generic constructor for a file data PDU.
pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
segment_metadata: Option<SegmentMetadata<'seg_meta>>, segment_metadata: Option<SegmentMetadata<'seg_meta>>,
offset: u64, offset: u64,
@@ -213,26 +227,31 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> {
.calc_pdu_datafield_len(self.file_data.len() as u64) .calc_pdu_datafield_len(self.file_data.len() as u64)
} }
/// Optional segment metadata.
#[inline] #[inline]
pub fn segment_metadata(&self) -> Option<&SegmentMetadata<'_>> { pub fn segment_metadata(&self) -> Option<&SegmentMetadata<'_>> {
self.common.segment_metadata.as_ref() self.common.segment_metadata.as_ref()
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
self.common.pdu_header() self.common.pdu_header()
} }
/// File data offset.
#[inline] #[inline]
pub fn offset(&self) -> u64 { pub fn offset(&self) -> u64 {
self.common.offset self.common.offset
} }
/// File data.
#[inline] #[inline]
pub fn file_data(&self) -> &'file_data [u8] { pub fn file_data(&self) -> &'file_data [u8] {
self.file_data self.file_data
} }
/// Read [Self] from the provided buffer.
pub fn from_bytes<'buf: 'seg_meta + 'file_data>(buf: &'buf [u8]) -> Result<Self, PduError> { pub fn from_bytes<'buf: 'seg_meta + 'file_data>(buf: &'buf [u8]) -> Result<Self, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
@@ -281,6 +300,7 @@ impl<'seg_meta, 'file_data> FileDataPdu<'seg_meta, 'file_data> {
Ok(current_idx) Ok(current_idx)
} }
/// Length of the written PDU.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.common.pdu_header.header_len() + self.calc_pdu_datafield_len() self.common.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -323,20 +343,23 @@ pub struct FileDataPduCreatorWithReservedDatafield<'seg_meta> {
} }
impl<'seg_meta> FileDataPduCreatorWithReservedDatafield<'seg_meta> { impl<'seg_meta> FileDataPduCreatorWithReservedDatafield<'seg_meta> {
/// Constructor for a file data PDU including segment metadata.
pub fn new_with_seg_metadata( pub fn new_with_seg_metadata(
pdu_header: PduHeader, pdu_header: PduHeader,
segment_metadata: SegmentMetadata<'seg_meta>, segment_metadata: SegmentMetadata<'seg_meta>,
offset: u64, offset: u64,
file_data_len: u64, file_data_len: u64,
) -> Self { ) -> Self {
Self::new_generic(pdu_header, Some(segment_metadata), offset, file_data_len) Self::new(pdu_header, Some(segment_metadata), offset, file_data_len)
} }
/// Constructor for a file data PDU without segment metadata.
pub fn new_no_seg_metadata(pdu_header: PduHeader, offset: u64, file_data_len: u64) -> Self { pub fn new_no_seg_metadata(pdu_header: PduHeader, offset: u64, file_data_len: u64) -> Self {
Self::new_generic(pdu_header, None, offset, file_data_len) Self::new(pdu_header, None, offset, file_data_len)
} }
pub fn new_generic( /// Generic constructor.
pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
segment_metadata: Option<SegmentMetadata<'seg_meta>>, segment_metadata: Option<SegmentMetadata<'seg_meta>>,
offset: u64, offset: u64,
@@ -362,6 +385,7 @@ impl<'seg_meta> FileDataPduCreatorWithReservedDatafield<'seg_meta> {
self.common.calc_pdu_datafield_len(self.file_data_len) self.common.calc_pdu_datafield_len(self.file_data_len)
} }
/// Length of the written PDU.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.common.pdu_header.header_len() + self.calc_pdu_datafield_len() self.common.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -423,6 +447,7 @@ pub struct FileDataPduCreatorWithUnwrittenData<'buf> {
} }
impl FileDataPduCreatorWithUnwrittenData<'_> { impl FileDataPduCreatorWithUnwrittenData<'_> {
/// Mutable access to the file data field.
pub fn file_data_field_mut(&mut self) -> &mut [u8] { pub fn file_data_field_mut(&mut self) -> &mut [u8] {
&mut self.write_buf[self.file_data_offset as usize &mut self.write_buf[self.file_data_offset as usize
..self.file_data_offset as usize + self.file_data_len as usize] ..self.file_data_offset as usize + self.file_data_len as usize]

View File

@@ -1,3 +1,4 @@
//! # Finished PDU packet implementation.
use crate::cfdp::pdu::{ use crate::cfdp::pdu::{
add_pdu_crc, generic_length_checks_pdu_deserialization, FileDirectiveType, PduError, PduHeader, add_pdu_crc, generic_length_checks_pdu_deserialization, FileDirectiveType, PduError, PduHeader,
}; };
@@ -13,23 +14,31 @@ use serde::{Deserialize, Serialize};
use super::tlv::ReadableTlv; use super::tlv::ReadableTlv;
use super::{CfdpPdu, InvalidTlvTypeFieldError, WritablePduPacket}; use super::{CfdpPdu, InvalidTlvTypeFieldError, WritablePduPacket};
/// Delivery code enumeration.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum DeliveryCode { pub enum DeliveryCode {
/// Completed delivery.
Complete = 0, Complete = 0,
/// Incomplete delivery.
Incomplete = 1, Incomplete = 1,
} }
/// File status enumeration.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum FileStatus { pub enum FileStatus {
/// File was discarded deliberately.
DiscardDeliberately = 0b00, DiscardDeliberately = 0b00,
/// File was rejected by the filestore.
DiscardedFsRejection = 0b01, DiscardedFsRejection = 0b01,
/// File was retained (but not necesarilly complete).
Retained = 0b10, Retained = 0b10,
/// Unreported file status.
Unreported = 0b11, Unreported = 0b11,
} }
@@ -65,6 +74,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
) )
} }
/// Constructor where the fault location is provided.
pub fn new_with_error( pub fn new_with_error(
pdu_header: PduHeader, pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,
@@ -82,6 +92,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
) )
} }
/// Generic constructor.
pub fn new( pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
condition_code: ConditionCode, condition_code: ConditionCode,
@@ -109,32 +120,37 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
finished_pdu finished_pdu
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Condition code.
#[inline] #[inline]
pub fn condition_code(&self) -> ConditionCode { pub fn condition_code(&self) -> ConditionCode {
self.condition_code self.condition_code
} }
/// Delivery code.
#[inline] #[inline]
pub fn delivery_code(&self) -> DeliveryCode { pub fn delivery_code(&self) -> DeliveryCode {
self.delivery_code self.delivery_code
} }
/// File status.
#[inline] #[inline]
pub fn file_status(&self) -> FileStatus { pub fn file_status(&self) -> FileStatus {
self.file_status self.file_status
} }
// If there are no filestore responses, an empty slice will be returned. /// Filestore responses as a slice.
#[inline] #[inline]
pub fn filestore_responses(&self) -> &[FilestoreResponseTlv<'_, '_, '_>] { pub fn filestore_responses(&self) -> &[FilestoreResponseTlv<'_, '_, '_>] {
self.fs_responses self.fs_responses
} }
/// Optional fault location [EntityIdTlv].
#[inline] #[inline]
pub fn fault_location(&self) -> Option<EntityIdTlv> { pub fn fault_location(&self) -> Option<EntityIdTlv> {
self.fault_location self.fault_location
@@ -166,7 +182,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
} }
let mut current_idx = self.pdu_header.write_to_bytes(buf)?; let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
buf[current_idx] = FileDirectiveType::FinishedPdu as u8; buf[current_idx] = FileDirectiveType::Finished as u8;
current_idx += 1; current_idx += 1;
buf[current_idx] = ((self.condition_code as u8) << 4) buf[current_idx] = ((self.condition_code as u8) << 4)
| ((self.delivery_code as u8) << 2) | ((self.delivery_code as u8) << 2)
@@ -184,6 +200,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
Ok(current_idx) Ok(current_idx)
} }
/// Length of the written PDU in bytes.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len() self.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -197,7 +214,7 @@ impl CfdpPdu for FinishedPduCreator<'_> {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::FinishedPdu) Some(FileDirectiveType::Finished)
} }
} }
@@ -242,6 +259,7 @@ impl<'buf> Iterator for FilestoreResponseIterator<'buf> {
} }
} }
/// Fnished PDU reader structure.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -269,13 +287,13 @@ impl<'buf> FinishedPduReader<'buf> {
let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
PduError::InvalidDirectiveType { PduError::InvalidDirectiveType {
found: buf[current_idx], found: buf[current_idx],
expected: Some(FileDirectiveType::FinishedPdu), expected: Some(FileDirectiveType::Finished),
} }
})?; })?;
if directive_type != FileDirectiveType::FinishedPdu { if directive_type != FileDirectiveType::Finished {
return Err(PduError::WrongDirectiveType { return Err(PduError::WrongDirectiveType {
found: directive_type, found: directive_type,
expected: FileDirectiveType::FinishedPdu, expected: FileDirectiveType::Finished,
}); });
} }
current_idx += 1; current_idx += 1;
@@ -297,11 +315,13 @@ impl<'buf> FinishedPduReader<'buf> {
}) })
} }
/// Raw filestore responses.
#[inline] #[inline]
pub fn fs_responses_raw(&self) -> &[u8] { pub fn fs_responses_raw(&self) -> &[u8] {
self.fs_responses_raw self.fs_responses_raw
} }
/// Iterator over the filestore responses.
#[inline] #[inline]
pub fn fs_responses_iter(&self) -> FilestoreResponseIterator<'_> { pub fn fs_responses_iter(&self) -> FilestoreResponseIterator<'_> {
FilestoreResponseIterator { FilestoreResponseIterator {
@@ -310,26 +330,31 @@ impl<'buf> FinishedPduReader<'buf> {
} }
} }
/// Condition code.
#[inline] #[inline]
pub fn condition_code(&self) -> ConditionCode { pub fn condition_code(&self) -> ConditionCode {
self.condition_code self.condition_code
} }
/// Delivery code.
#[inline] #[inline]
pub fn delivery_code(&self) -> DeliveryCode { pub fn delivery_code(&self) -> DeliveryCode {
self.delivery_code self.delivery_code
} }
/// File status.
#[inline] #[inline]
pub fn file_status(&self) -> FileStatus { pub fn file_status(&self) -> FileStatus {
self.file_status self.file_status
} }
/// Optional fault location [EntityIdTlv].
#[inline] #[inline]
pub fn fault_location(&self) -> Option<EntityIdTlv> { pub fn fault_location(&self) -> Option<EntityIdTlv> {
self.fault_location self.fault_location
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
@@ -399,7 +424,7 @@ impl CfdpPdu for FinishedPduReader<'_> {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::FinishedPdu) Some(FileDirectiveType::Finished)
} }
} }
@@ -468,7 +493,7 @@ mod tests {
assert_eq!(finished_pdu.pdu_type(), PduType::FileDirective); assert_eq!(finished_pdu.pdu_type(), PduType::FileDirective);
assert_eq!( assert_eq!(
finished_pdu.file_directive_type(), finished_pdu.file_directive_type(),
Some(FileDirectiveType::FinishedPdu) Some(FileDirectiveType::Finished)
); );
assert_eq!( assert_eq!(
finished_pdu.transmission_mode(), finished_pdu.transmission_mode(),
@@ -500,7 +525,7 @@ mod tests {
); );
verify_raw_header(finished_pdu.pdu_header(), &buf); verify_raw_header(finished_pdu.pdu_header(), &buf);
let mut current_idx = finished_pdu.pdu_header().header_len(); let mut current_idx = finished_pdu.pdu_header().header_len();
assert_eq!(buf[current_idx], FileDirectiveType::FinishedPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Finished as u8);
current_idx += 1; current_idx += 1;
assert_eq!( assert_eq!(
(buf[current_idx] >> 4) & 0b1111, (buf[current_idx] >> 4) & 0b1111,

View File

@@ -1,3 +1,4 @@
//! # Metadata PDU packet implementation.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
use super::tlv::TlvOwned; use super::tlv::TlvOwned;
use crate::cfdp::lv::Lv; use crate::cfdp::lv::Lv;
@@ -16,16 +17,21 @@ use serde::{Deserialize, Serialize};
use super::tlv::ReadableTlv; use super::tlv::ReadableTlv;
use super::{CfdpPdu, WritablePduPacket}; use super::{CfdpPdu, WritablePduPacket};
/// Generic metadata parameters.
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)] #[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub struct MetadataGenericParams { pub struct MetadataGenericParams {
/// Closure requested flag.
pub closure_requested: bool, pub closure_requested: bool,
/// Checksum type.
pub checksum_type: ChecksumType, pub checksum_type: ChecksumType,
/// File size.
pub file_size: u64, pub file_size: u64,
} }
impl MetadataGenericParams { impl MetadataGenericParams {
/// Constructor.
pub fn new(closure_requested: bool, checksum_type: ChecksumType, file_size: u64) -> Self { pub fn new(closure_requested: bool, checksum_type: ChecksumType, file_size: u64) -> Self {
Self { Self {
closure_requested, closure_requested,
@@ -35,6 +41,7 @@ impl MetadataGenericParams {
} }
} }
/// Build the metadata options from a slice of [Tlv]s
pub fn build_metadata_opts_from_slice( pub fn build_metadata_opts_from_slice(
buf: &mut [u8], buf: &mut [u8],
tlvs: &[Tlv], tlvs: &[Tlv],
@@ -46,6 +53,7 @@ pub fn build_metadata_opts_from_slice(
Ok(written) Ok(written)
} }
/// Build the metadata options from a vector of [Tlv]s
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn build_metadata_opts_from_vec( pub fn build_metadata_opts_from_vec(
buf: &mut [u8], buf: &mut [u8],
@@ -54,6 +62,7 @@ pub fn build_metadata_opts_from_vec(
build_metadata_opts_from_slice(buf, tlvs.as_slice()) build_metadata_opts_from_slice(buf, tlvs.as_slice())
} }
/// Build the metadata options from a slice of [TlvOwned]s
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn build_metadata_opts_from_owned_slice(tlvs: &[TlvOwned]) -> Vec<u8> { pub fn build_metadata_opts_from_owned_slice(tlvs: &[TlvOwned]) -> Vec<u8> {
let mut sum_vec = Vec::new(); let mut sum_vec = Vec::new();
@@ -77,6 +86,7 @@ pub struct MetadataPduCreator<'src_name, 'dest_name, 'opts> {
} }
impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'opts> { impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'opts> {
/// Constructor for a metadata PDU without options.
pub fn new_no_opts( pub fn new_no_opts(
pdu_header: PduHeader, pdu_header: PduHeader,
metadata_params: MetadataGenericParams, metadata_params: MetadataGenericParams,
@@ -92,6 +102,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op
) )
} }
/// Constructor for a metadata PDU with options.
pub fn new_with_opts( pub fn new_with_opts(
pdu_header: PduHeader, pdu_header: PduHeader,
metadata_params: MetadataGenericParams, metadata_params: MetadataGenericParams,
@@ -108,6 +119,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op
) )
} }
/// Generic constructor for a metadata PDU.
pub fn new( pub fn new(
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
metadata_params: MetadataGenericParams, metadata_params: MetadataGenericParams,
@@ -128,26 +140,31 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op
pdu pdu
} }
/// Metadata generic parameters.
#[inline] #[inline]
pub fn metadata_params(&self) -> &MetadataGenericParams { pub fn metadata_params(&self) -> &MetadataGenericParams {
&self.metadata_params &self.metadata_params
} }
/// Source file name as a [Lv].
#[inline] #[inline]
pub fn src_file_name(&self) -> Lv<'src_name> { pub fn src_file_name(&self) -> Lv<'src_name> {
self.src_file_name self.src_file_name
} }
/// Destination file name as a [Lv].
#[inline] #[inline]
pub fn dest_file_name(&self) -> Lv<'dest_name> { pub fn dest_file_name(&self) -> Lv<'dest_name> {
self.dest_file_name self.dest_file_name
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Raw options.
#[inline] #[inline]
pub fn options(&self) -> &'opts [u8] { pub fn options(&self) -> &'opts [u8] {
self.options self.options
@@ -191,7 +208,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op
} }
let mut current_idx = self.pdu_header.write_to_bytes(buf)?; let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
buf[current_idx] = FileDirectiveType::MetadataPdu as u8; buf[current_idx] = FileDirectiveType::Metadata as u8;
current_idx += 1; current_idx += 1;
buf[current_idx] = ((self.metadata_params.closure_requested as u8) << 6) buf[current_idx] = ((self.metadata_params.closure_requested as u8) << 6)
| (self.metadata_params.checksum_type as u8); | (self.metadata_params.checksum_type as u8);
@@ -215,6 +232,7 @@ impl<'src_name, 'dest_name, 'opts> MetadataPduCreator<'src_name, 'dest_name, 'op
Ok(current_idx) Ok(current_idx)
} }
/// Length of the written PDU in bytes.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len() self.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -228,7 +246,7 @@ impl CfdpPdu for MetadataPduCreator<'_, '_, '_> {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::MetadataPdu) Some(FileDirectiveType::Metadata)
} }
} }
@@ -291,10 +309,12 @@ pub struct MetadataPduReader<'buf> {
} }
impl<'raw> MetadataPduReader<'raw> { impl<'raw> MetadataPduReader<'raw> {
/// Constructor from raw bytes.
pub fn new(buf: &'raw [u8]) -> Result<Self, PduError> { pub fn new(buf: &'raw [u8]) -> Result<Self, PduError> {
Self::from_bytes(buf) Self::from_bytes(buf)
} }
/// Constructor from raw bytes.
pub fn from_bytes(buf: &'raw [u8]) -> Result<Self, PduError> { pub fn from_bytes(buf: &'raw [u8]) -> Result<Self, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
@@ -308,13 +328,13 @@ impl<'raw> MetadataPduReader<'raw> {
let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
PduError::InvalidDirectiveType { PduError::InvalidDirectiveType {
found: buf[current_idx], found: buf[current_idx],
expected: Some(FileDirectiveType::MetadataPdu), expected: Some(FileDirectiveType::Metadata),
} }
})?; })?;
if directive_type != FileDirectiveType::MetadataPdu { if directive_type != FileDirectiveType::Metadata {
return Err(PduError::WrongDirectiveType { return Err(PduError::WrongDirectiveType {
found: directive_type, found: directive_type,
expected: FileDirectiveType::MetadataPdu, expected: FileDirectiveType::Metadata,
}); });
} }
current_idx += 1; current_idx += 1;
@@ -350,26 +370,31 @@ impl<'raw> MetadataPduReader<'raw> {
}) })
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Raw options.
#[inline] #[inline]
pub fn options(&self) -> &'raw [u8] { pub fn options(&self) -> &'raw [u8] {
self.options self.options
} }
/// Generic metadata parameters.
#[inline] #[inline]
pub fn metadata_params(&self) -> &MetadataGenericParams { pub fn metadata_params(&self) -> &MetadataGenericParams {
&self.metadata_params &self.metadata_params
} }
/// Source file name as a [Lv].
#[inline] #[inline]
pub fn src_file_name(&self) -> Lv<'_> { pub fn src_file_name(&self) -> Lv<'_> {
self.src_file_name self.src_file_name
} }
/// Destination file name as a [Lv].
#[inline] #[inline]
pub fn dest_file_name(&self) -> Lv<'_> { pub fn dest_file_name(&self) -> Lv<'_> {
self.dest_file_name self.dest_file_name
@@ -383,12 +408,12 @@ impl CfdpPdu for MetadataPduReader<'_> {
} }
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::MetadataPdu) Some(FileDirectiveType::Metadata)
} }
} }
#[cfg(test)] #[cfg(test)]
pub mod tests { mod tests {
use alloc::string::ToString; use alloc::string::ToString;
use crate::cfdp::lv::Lv; use crate::cfdp::lv::Lv;
@@ -471,7 +496,7 @@ pub mod tests {
); );
assert_eq!( assert_eq!(
metadata_pdu.file_directive_type(), metadata_pdu.file_directive_type(),
Some(FileDirectiveType::MetadataPdu) Some(FileDirectiveType::Metadata)
); );
assert_eq!( assert_eq!(
metadata_pdu.transmission_mode(), metadata_pdu.transmission_mode(),
@@ -502,7 +527,7 @@ pub mod tests {
+ expected_src_filename.len_full() + expected_src_filename.len_full()
+ expected_dest_filename.len_full() + expected_dest_filename.len_full()
); );
assert_eq!(buf[7], FileDirectiveType::MetadataPdu as u8); assert_eq!(buf[7], FileDirectiveType::Metadata as u8);
assert_eq!(buf[8] >> 6, closure_requested as u8); assert_eq!(buf[8] >> 6, closure_requested as u8);
assert_eq!(buf[8] & 0b1111, checksum_type as u8); assert_eq!(buf[8] & 0b1111, checksum_type as u8);
assert_eq!(u32::from_be_bytes(buf[9..13].try_into().unwrap()), 0x1010); assert_eq!(u32::from_be_bytes(buf[9..13].try_into().unwrap()), 0x1010);
@@ -807,10 +832,10 @@ pub mod tests {
let error = metadata_error.unwrap_err(); let error = metadata_error.unwrap_err();
if let PduError::InvalidDirectiveType { found, expected } = error { if let PduError::InvalidDirectiveType { found, expected } = error {
assert_eq!(found, 0xff); assert_eq!(found, 0xff);
assert_eq!(expected, Some(FileDirectiveType::MetadataPdu)); assert_eq!(expected, Some(FileDirectiveType::Metadata));
assert_eq!( assert_eq!(
error.to_string(), error.to_string(),
"invalid directive type, found 255, expected Some(MetadataPdu)" "invalid directive type, found 255, expected Some(Metadata)"
); );
} else { } else {
panic!("Expected InvalidDirectiveType error, got {:?}", error); panic!("Expected InvalidDirectiveType error, got {:?}", error);
@@ -827,16 +852,16 @@ pub mod tests {
&[], &[],
); );
let mut metadata_vec = metadata_pdu.to_vec().unwrap(); let mut metadata_vec = metadata_pdu.to_vec().unwrap();
metadata_vec[7] = FileDirectiveType::EofPdu as u8; metadata_vec[7] = FileDirectiveType::Eof as u8;
let metadata_error = MetadataPduReader::from_bytes(&metadata_vec); let metadata_error = MetadataPduReader::from_bytes(&metadata_vec);
assert!(metadata_error.is_err()); assert!(metadata_error.is_err());
let error = metadata_error.unwrap_err(); let error = metadata_error.unwrap_err();
if let PduError::WrongDirectiveType { found, expected } = error { if let PduError::WrongDirectiveType { found, expected } = error {
assert_eq!(found, FileDirectiveType::EofPdu); assert_eq!(found, FileDirectiveType::Eof);
assert_eq!(expected, FileDirectiveType::MetadataPdu); assert_eq!(expected, FileDirectiveType::Metadata);
assert_eq!( assert_eq!(
error.to_string(), error.to_string(),
"wrong directive type, found EofPdu, expected MetadataPdu" "wrong directive type, found Eof, expected Metadata"
); );
} else { } else {
panic!("Expected InvalidDirectiveType error, got {:?}", error); panic!("Expected InvalidDirectiveType error, got {:?}", error);

View File

@@ -15,24 +15,34 @@ pub mod finished;
pub mod metadata; pub mod metadata;
pub mod nak; pub mod nak;
/// File directive type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum FileDirectiveType { pub enum FileDirectiveType {
EofPdu = 0x04, /// EOF.
FinishedPdu = 0x05, Eof = 0x04,
AckPdu = 0x06, /// Finished.
MetadataPdu = 0x07, Finished = 0x05,
NakPdu = 0x08, /// ACK.
PromptPdu = 0x09, Ack = 0x06,
KeepAlivePdu = 0x0c, /// Metadata.
Metadata = 0x07,
/// NAK.
Nak = 0x08,
/// Prompt.
Prompt = 0x09,
/// Keep Alive.
KeepAlive = 0x0c,
} }
/// PDU error.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PduError { pub enum PduError {
/// Byte conversion error.
#[error("byte conversion error: {0}")] #[error("byte conversion error: {0}")]
ByteConversion(#[from] ByteConversionError), ByteConversion(#[from] ByteConversionError),
/// Found version ID invalid, not equal to [super::CFDP_VERSION_2]. /// Found version ID invalid, not equal to [super::CFDP_VERSION_2].
@@ -44,27 +54,35 @@ pub enum PduError {
/// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported. /// Invalid length for the entity ID detected. Only the values 1, 2, 4 and 8 are supported.
#[error("invalid transaction ID length {0}")] #[error("invalid transaction ID length {0}")]
InvalidTransactionSeqNumLen(u8), InvalidTransactionSeqNumLen(u8),
/// Source and destination entity ID lengths do not match.
#[error( #[error(
"missmatch of PDU source ID length {src_id_len} and destination ID length {dest_id_len}" "missmatch of PDU source ID length {src_id_len} and destination ID length {dest_id_len}"
)] )]
SourceDestIdLenMissmatch { SourceDestIdLenMissmatch {
/// Source ID length.
src_id_len: usize, src_id_len: usize,
/// Destination ID length.
dest_id_len: usize, dest_id_len: usize,
}, },
/// Wrong directive type, for example when parsing the directive field for a file directive /// Wrong directive type, for example when parsing the directive field for a file directive
/// PDU. /// PDU.
#[error("wrong directive type, found {found:?}, expected {expected:?}")] #[error("wrong directive type, found {found:?}, expected {expected:?}")]
WrongDirectiveType { WrongDirectiveType {
/// Found directive type.
found: FileDirectiveType, found: FileDirectiveType,
/// Expected directive type.
expected: FileDirectiveType, expected: FileDirectiveType,
}, },
/// The directive type field contained a value not in the range of permitted values. This can /// The directive type field contained a value not in the range of permitted values. This can
/// also happen if an invalid value is passed to the ACK PDU reader. /// also happen if an invalid value is passed to the ACK PDU reader.
#[error("invalid directive type, found {found:?}, expected {expected:?}")] #[error("invalid directive type, found {found:?}, expected {expected:?}")]
InvalidDirectiveType { InvalidDirectiveType {
/// Found raw directive type.
found: u8, found: u8,
/// Expected raw directive type if applicable.
expected: Option<FileDirectiveType>, expected: Option<FileDirectiveType>,
}, },
/// Invalid start or end of scope for a NAK PDU.
#[error("nak pdu: {0}")] #[error("nak pdu: {0}")]
InvalidStartOrEndOfScope(#[from] InvalidStartOrEndOfScopeError), InvalidStartOrEndOfScope(#[from] InvalidStartOrEndOfScopeError),
/// Invalid condition code. Contains the raw detected value. /// Invalid condition code. Contains the raw detected value.
@@ -74,6 +92,7 @@ pub enum PduError {
/// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/). /// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/).
#[error("invalid checksum type {0}")] #[error("invalid checksum type {0}")]
InvalidChecksumType(u8), InvalidChecksumType(u8),
/// File size is too large.
#[error("file size {0} too large")] #[error("file size {0} too large")]
FileSizeTooLarge(u64), FileSizeTooLarge(u64),
/// If the CRC flag for a PDU is enabled and the checksum check fails. Contains raw 16-bit CRC. /// If the CRC flag for a PDU is enabled and the checksum check fails. Contains raw 16-bit CRC.
@@ -96,9 +115,15 @@ impl From<InvalidAckedDirectiveCodeError> for PduError {
} }
} }
/// Generic trait for a PDU which can be written to bytes.
pub trait WritablePduPacket { pub trait WritablePduPacket {
/// Length when written to bytes.
fn len_written(&self) -> usize; fn len_written(&self) -> usize;
/// Write the PDU to a raw buffer, returning the written length.
fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError>; fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError>;
/// Convert the PDU to an owned vector of bytes.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
fn to_vec(&self) -> Result<Vec<u8>, PduError> { fn to_vec(&self) -> Result<Vec<u8>, PduError> {
// This is the correct way to do this. See // This is the correct way to do this. See
@@ -112,48 +137,58 @@ pub trait WritablePduPacket {
/// Abstraction trait for fields and properties common for all PDUs. /// Abstraction trait for fields and properties common for all PDUs.
pub trait CfdpPdu { pub trait CfdpPdu {
/// PDU header.
fn pdu_header(&self) -> &PduHeader; fn pdu_header(&self) -> &PduHeader;
/// Source ID (file sender).
#[inline] #[inline]
fn source_id(&self) -> UnsignedByteField { fn source_id(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().source_entity_id self.pdu_header().common_pdu_conf().source_entity_id
} }
/// Destination ID (file sender).
#[inline] #[inline]
fn dest_id(&self) -> UnsignedByteField { fn dest_id(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().dest_entity_id self.pdu_header().common_pdu_conf().dest_entity_id
} }
/// Transaction sequence number.
#[inline] #[inline]
fn transaction_seq_num(&self) -> UnsignedByteField { fn transaction_seq_num(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().transaction_seq_num self.pdu_header().common_pdu_conf().transaction_seq_num
} }
/// Transmission mode.
#[inline] #[inline]
fn transmission_mode(&self) -> TransmissionMode { fn transmission_mode(&self) -> TransmissionMode {
self.pdu_header().common_pdu_conf().trans_mode self.pdu_header().common_pdu_conf().trans_mode
} }
/// Direction.
#[inline] #[inline]
fn direction(&self) -> Direction { fn direction(&self) -> Direction {
self.pdu_header().common_pdu_conf().direction self.pdu_header().common_pdu_conf().direction
} }
/// CRC flag.
#[inline] #[inline]
fn crc_flag(&self) -> CrcFlag { fn crc_flag(&self) -> CrcFlag {
self.pdu_header().common_pdu_conf().crc_flag self.pdu_header().common_pdu_conf().crc_flag
} }
/// File flag.
#[inline] #[inline]
fn file_flag(&self) -> LargeFileFlag { fn file_flag(&self) -> LargeFileFlag {
self.pdu_header().common_pdu_conf().file_flag self.pdu_header().common_pdu_conf().file_flag
} }
/// PDU type.
#[inline] #[inline]
fn pdu_type(&self) -> PduType { fn pdu_type(&self) -> PduType {
self.pdu_header().pdu_type() self.pdu_header().pdu_type()
} }
/// File directive type when applicable.
fn file_directive_type(&self) -> Option<FileDirectiveType>; fn file_directive_type(&self) -> Option<FileDirectiveType>;
} }
@@ -169,15 +204,21 @@ pub trait CfdpPdu {
pub struct CommonPduConfig { pub struct CommonPduConfig {
source_entity_id: UnsignedByteField, source_entity_id: UnsignedByteField,
dest_entity_id: UnsignedByteField, dest_entity_id: UnsignedByteField,
/// Transaction sequence number.
pub transaction_seq_num: UnsignedByteField, pub transaction_seq_num: UnsignedByteField,
/// Transmission mode.
pub trans_mode: TransmissionMode, pub trans_mode: TransmissionMode,
/// File flag.
pub file_flag: LargeFileFlag, pub file_flag: LargeFileFlag,
/// CRC flag.
pub crc_flag: CrcFlag, pub crc_flag: CrcFlag,
/// Direction.
pub direction: Direction, pub direction: Direction,
} }
// TODO: Builder pattern might be applicable here.. // TODO: Builder pattern might be applicable here..
impl CommonPduConfig { impl CommonPduConfig {
/// Generic constructor.
#[inline] #[inline]
pub fn new( pub fn new(
source_id: impl Into<UnsignedByteField>, source_id: impl Into<UnsignedByteField>,
@@ -210,6 +251,7 @@ impl CommonPduConfig {
}) })
} }
/// Constructor for custom byte field with default field values for the other fields.
#[inline] #[inline]
pub fn new_with_byte_fields( pub fn new_with_byte_fields(
source_id: impl Into<UnsignedByteField>, source_id: impl Into<UnsignedByteField>,
@@ -227,6 +269,7 @@ impl CommonPduConfig {
) )
} }
/// Source ID (file sender).
#[inline] #[inline]
pub fn source_id(&self) -> UnsignedByteField { pub fn source_id(&self) -> UnsignedByteField {
self.source_entity_id self.source_entity_id
@@ -255,6 +298,7 @@ impl CommonPduConfig {
Ok((source_id, dest_id)) Ok((source_id, dest_id))
} }
/// Set the source and destination ID field.
#[inline] #[inline]
pub fn set_source_and_dest_id( pub fn set_source_and_dest_id(
&mut self, &mut self,
@@ -267,6 +311,7 @@ impl CommonPduConfig {
Ok(()) Ok(())
} }
/// Destination ID (file receiver).
#[inline] #[inline]
pub fn dest_id(&self) -> UnsignedByteField { pub fn dest_id(&self) -> UnsignedByteField {
self.dest_entity_id self.dest_entity_id
@@ -305,6 +350,7 @@ impl PartialEq for CommonPduConfig {
} }
} }
/// Fixed header length of the PDU header.
pub const FIXED_HEADER_LEN: usize = 4; pub const FIXED_HEADER_LEN: usize = 4;
/// Abstraction for the PDU header common to all CFDP PDUs. /// Abstraction for the PDU header common to all CFDP PDUs.
@@ -322,8 +368,10 @@ pub struct PduHeader {
} }
impl PduHeader { impl PduHeader {
/// Fixed length of the PDU header when written to a raw buffer.
pub const FIXED_LEN: usize = FIXED_HEADER_LEN; pub const FIXED_LEN: usize = FIXED_HEADER_LEN;
/// Constructor for a File Data PDU header.
#[inline] #[inline]
pub fn new_for_file_data( pub fn new_for_file_data(
pdu_conf: CommonPduConfig, pdu_conf: CommonPduConfig,
@@ -340,6 +388,7 @@ impl PduHeader {
) )
} }
/// Constructor for a file data PDU.
#[inline] #[inline]
pub fn new_for_file_data_default(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self { pub fn new_for_file_data_default(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
Self::new_generic( Self::new_generic(
@@ -351,6 +400,7 @@ impl PduHeader {
) )
} }
/// Constructor for a file directive PDU.
#[inline] #[inline]
pub fn new_for_file_directive(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self { pub fn new_for_file_directive(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
Self::new_generic( Self::new_generic(
@@ -362,6 +412,7 @@ impl PduHeader {
) )
} }
/// Constructor from a given [CommonPduConfig] and for a file directive PDU.
#[inline] #[inline]
pub fn from_pdu_conf_for_file_directive(pdu_conf: CommonPduConfig) -> Self { pub fn from_pdu_conf_for_file_directive(pdu_conf: CommonPduConfig) -> Self {
Self::new_generic( Self::new_generic(
@@ -373,6 +424,7 @@ impl PduHeader {
) )
} }
/// Generic constructor.
#[inline] #[inline]
pub fn new_generic( pub fn new_generic(
pdu_type: PduType, pdu_type: PduType,
@@ -399,6 +451,7 @@ impl PduHeader {
+ self.pdu_conf.dest_entity_id.size() + self.pdu_conf.dest_entity_id.size()
} }
/// PDU data field length.
#[inline] #[inline]
pub fn pdu_datafield_len(&self) -> usize { pub fn pdu_datafield_len(&self) -> usize {
self.pdu_datafield_len.into() self.pdu_datafield_len.into()
@@ -411,6 +464,7 @@ impl PduHeader {
self.header_len() + self.pdu_datafield_len as usize self.header_len() + self.pdu_datafield_len as usize
} }
/// Write the header to a raw buffer, returning the written length on success.
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> { pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
// The API does not allow passing entity IDs with different sizes, so this should // The API does not allow passing entity IDs with different sizes, so this should
// never happen. // never happen.
@@ -578,21 +632,25 @@ impl PduHeader {
)) ))
} }
/// PDU type.
#[inline] #[inline]
pub fn pdu_type(&self) -> PduType { pub fn pdu_type(&self) -> PduType {
self.pdu_type self.pdu_type
} }
/// Common PDU configuration fields.
#[inline] #[inline]
pub fn common_pdu_conf(&self) -> &CommonPduConfig { pub fn common_pdu_conf(&self) -> &CommonPduConfig {
&self.pdu_conf &self.pdu_conf
} }
/// Segment metadata flag.
#[inline] #[inline]
pub fn seg_metadata_flag(&self) -> SegmentMetadataFlag { pub fn seg_metadata_flag(&self) -> SegmentMetadataFlag {
self.seg_metadata_flag self.seg_metadata_flag
} }
/// Segmentation Control.
#[inline] #[inline]
pub fn seg_ctrl(&self) -> SegmentationControl { pub fn seg_ctrl(&self) -> SegmentationControl {
self.seg_ctrl self.seg_ctrl

View File

@@ -1,3 +1,4 @@
//! # NAK PDU packet implementation.
use crate::{ use crate::{
cfdp::{CrcFlag, Direction, LargeFileFlag}, cfdp::{CrcFlag, Direction, LargeFileFlag},
ByteConversionError, ByteConversionError,
@@ -8,6 +9,7 @@ use super::{
PduHeader, WritablePduPacket, PduHeader, WritablePduPacket,
}; };
/// Invalid start or end of scope value.
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)] #[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[error("invalid start or end of scope value for NAK PDU")] #[error("invalid start or end of scope value for NAK PDU")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -52,6 +54,7 @@ fn write_start_and_end_of_scope(
current_index current_index
} }
/// Buffer is too small to even hold a PDU header.
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)] #[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[error("packet buffer too small for PDU header")] #[error("packet buffer too small for PDU header")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -94,11 +97,14 @@ pub fn calculate_max_segment_requests(
#[derive(Debug, PartialEq, Eq, Clone)] #[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SegmentRequests<'a> { pub enum SegmentRequests<'a> {
/// Segment requests for normal file sizes bounded by [u32::MAX].
U32Pairs(&'a [(u32, u32)]), U32Pairs(&'a [(u32, u32)]),
/// Segment requests for large file sizes bounded by [u64::MAX].
U64Pairs(&'a [(u64, u64)]), U64Pairs(&'a [(u64, u64)]),
} }
impl SegmentRequests<'_> { impl SegmentRequests<'_> {
/// Wrapped segment requests are empty.
#[inline] #[inline]
pub fn is_empty(&self) -> bool { pub fn is_empty(&self) -> bool {
match self { match self {
@@ -152,6 +158,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
) )
} }
/// Constructor for large file sizes.
pub fn new_large_file_size( pub fn new_large_file_size(
pdu_header: PduHeader, pdu_header: PduHeader,
start_of_scope: u64, start_of_scope: u64,
@@ -206,21 +213,25 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
Ok(nak_pdu) Ok(nak_pdu)
} }
/// Start of scope of the NAK PDU.
#[inline] #[inline]
pub fn start_of_scope(&self) -> u64 { pub fn start_of_scope(&self) -> u64 {
self.start_of_scope self.start_of_scope
} }
/// End of scope of the NAK PDU.
#[inline] #[inline]
pub fn end_of_scope(&self) -> u64 { pub fn end_of_scope(&self) -> u64 {
self.end_of_scope self.end_of_scope
} }
/// Semgent requests.
#[inline] #[inline]
pub fn segment_requests(&self) -> Option<&SegmentRequests<'_>> { pub fn segment_requests(&self) -> Option<&SegmentRequests<'_>> {
self.segment_requests.as_ref() self.segment_requests.as_ref()
} }
/// Number of segment requests.
#[inline] #[inline]
pub fn num_segment_reqs(&self) -> usize { pub fn num_segment_reqs(&self) -> usize {
match &self.segment_requests { match &self.segment_requests {
@@ -232,6 +243,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
} }
} }
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
@@ -252,7 +264,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
.into()); .into());
} }
let mut current_index = self.pdu_header.write_to_bytes(buf)?; let mut current_index = self.pdu_header.write_to_bytes(buf)?;
buf[current_index] = FileDirectiveType::NakPdu as u8; buf[current_index] = FileDirectiveType::Nak as u8;
current_index += 1; current_index += 1;
current_index = write_start_and_end_of_scope( current_index = write_start_and_end_of_scope(
@@ -309,7 +321,7 @@ impl CfdpPdu for NakPduCreator<'_> {
#[inline] #[inline]
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::NakPdu) Some(FileDirectiveType::Nak)
} }
} }
@@ -337,6 +349,8 @@ pub struct NakPduCreatorWithReservedSeqReqsBuf<'buf> {
} }
impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> { impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> {
/// Calculartes the maximum amount of segment requests which fit into a packet with the
/// provided parameters.
pub fn calculate_max_segment_requests( pub fn calculate_max_segment_requests(
max_packet_size: usize, max_packet_size: usize,
pdu_header: &PduHeader, pdu_header: &PduHeader,
@@ -344,6 +358,7 @@ impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> {
calculate_max_segment_requests(max_packet_size, pdu_header) calculate_max_segment_requests(max_packet_size, pdu_header)
} }
/// Constructor.
pub fn new( pub fn new(
buf: &'buf mut [u8], buf: &'buf mut [u8],
mut pdu_header: PduHeader, mut pdu_header: PduHeader,
@@ -371,11 +386,13 @@ impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> {
} }
impl NakPduCreatorWithReservedSeqReqsBuf<'_> { impl NakPduCreatorWithReservedSeqReqsBuf<'_> {
/// PDU header.
#[inline] #[inline]
pub fn pdu_header(&self) -> &PduHeader { pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header &self.pdu_header
} }
/// Number of segment requests.
#[inline] #[inline]
pub fn num_segment_reqs(&self) -> usize { pub fn num_segment_reqs(&self) -> usize {
self.num_segment_reqs self.num_segment_reqs
@@ -407,6 +424,7 @@ impl NakPduCreatorWithReservedSeqReqsBuf<'_> {
Ok(()) Ok(())
} }
/// Length when written to a buffer.
pub fn len_written(&self) -> usize { pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len() self.pdu_header.header_len() + self.calc_pdu_datafield_len()
} }
@@ -427,6 +445,7 @@ impl NakPduCreatorWithReservedSeqReqsBuf<'_> {
&self.buf[segment_req_buf_offset..segment_req_buf_offset + len] &self.buf[segment_req_buf_offset..segment_req_buf_offset + len]
} }
/// Iterator over all segment requests.
#[inline] #[inline]
pub fn segment_request_iter(&self) -> SegmentRequestIter<'_> { pub fn segment_request_iter(&self) -> SegmentRequestIter<'_> {
SegmentRequestIter::new( SegmentRequestIter::new(
@@ -447,7 +466,7 @@ impl NakPduCreatorWithReservedSeqReqsBuf<'_> {
) -> Result<usize, InvalidStartOrEndOfScopeError> { ) -> Result<usize, InvalidStartOrEndOfScopeError> {
self.set_start_and_end_of_scope(start_of_scope, end_of_scope)?; self.set_start_and_end_of_scope(start_of_scope, end_of_scope)?;
let mut current_idx = self.pdu_header.write_to_bytes(self.buf).unwrap(); let mut current_idx = self.pdu_header.write_to_bytes(self.buf).unwrap();
self.buf[current_idx] = FileDirectiveType::NakPdu as u8; self.buf[current_idx] = FileDirectiveType::Nak as u8;
current_idx += 1; current_idx += 1;
if self.pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large { if self.pdu_header.common_pdu_conf().file_flag == LargeFileFlag::Large {
@@ -613,15 +632,17 @@ impl CfdpPdu for NakPduReader<'_> {
} }
fn file_directive_type(&self) -> Option<FileDirectiveType> { fn file_directive_type(&self) -> Option<FileDirectiveType> {
Some(FileDirectiveType::NakPdu) Some(FileDirectiveType::Nak)
} }
} }
impl<'seg_reqs> NakPduReader<'seg_reqs> { impl<'seg_reqs> NakPduReader<'seg_reqs> {
/// Constructor from a raw bytestream.
pub fn new(buf: &'seg_reqs [u8]) -> Result<NakPduReader<'seg_reqs>, PduError> { pub fn new(buf: &'seg_reqs [u8]) -> Result<NakPduReader<'seg_reqs>, PduError> {
Self::from_bytes(buf) Self::from_bytes(buf)
} }
/// Constructor from a raw bytestream.
pub fn from_bytes(buf: &'seg_reqs [u8]) -> Result<NakPduReader<'seg_reqs>, PduError> { pub fn from_bytes(buf: &'seg_reqs [u8]) -> Result<NakPduReader<'seg_reqs>, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?; let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?; let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
@@ -631,13 +652,13 @@ impl<'seg_reqs> NakPduReader<'seg_reqs> {
let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| { let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
PduError::InvalidDirectiveType { PduError::InvalidDirectiveType {
found: buf[current_idx], found: buf[current_idx],
expected: Some(FileDirectiveType::NakPdu), expected: Some(FileDirectiveType::Nak),
} }
})?; })?;
if directive_type != FileDirectiveType::NakPdu { if directive_type != FileDirectiveType::Nak {
return Err(PduError::WrongDirectiveType { return Err(PduError::WrongDirectiveType {
found: directive_type, found: directive_type,
expected: FileDirectiveType::AckPdu, expected: FileDirectiveType::Ack,
}); });
} }
current_idx += 1; current_idx += 1;
@@ -682,14 +703,17 @@ impl<'seg_reqs> NakPduReader<'seg_reqs> {
}) })
} }
/// Start of scope.
pub fn start_of_scope(&self) -> u64 { pub fn start_of_scope(&self) -> u64 {
self.start_of_scope self.start_of_scope
} }
/// End of scope.
pub fn end_of_scope(&self) -> u64 { pub fn end_of_scope(&self) -> u64 {
self.end_of_scope self.end_of_scope
} }
/// Number of segment requests.
pub fn num_segment_reqs(&self) -> usize { pub fn num_segment_reqs(&self) -> usize {
if self.seg_reqs_raw.is_empty() { if self.seg_reqs_raw.is_empty() {
return 0; return 0;
@@ -747,10 +771,7 @@ mod tests {
assert_eq!(nak_pdu.crc_flag(), CrcFlag::NoCrc); assert_eq!(nak_pdu.crc_flag(), CrcFlag::NoCrc);
assert_eq!(nak_pdu.file_flag(), LargeFileFlag::Normal); assert_eq!(nak_pdu.file_flag(), LargeFileFlag::Normal);
assert_eq!(nak_pdu.pdu_type(), PduType::FileDirective); assert_eq!(nak_pdu.pdu_type(), PduType::FileDirective);
assert_eq!( assert_eq!(nak_pdu.file_directive_type(), Some(FileDirectiveType::Nak),);
nak_pdu.file_directive_type(),
Some(FileDirectiveType::NakPdu),
);
assert_eq!(nak_pdu.transmission_mode(), TransmissionMode::Acknowledged); assert_eq!(nak_pdu.transmission_mode(), TransmissionMode::Acknowledged);
assert_eq!(nak_pdu.direction(), Direction::TowardsSender); assert_eq!(nak_pdu.direction(), Direction::TowardsSender);
assert_eq!(nak_pdu.source_id(), TEST_SRC_ID.into()); assert_eq!(nak_pdu.source_id(), TEST_SRC_ID.into());
@@ -794,7 +815,7 @@ mod tests {
verify_raw_header(nak_pdu.pdu_header(), &buf); verify_raw_header(nak_pdu.pdu_header(), &buf);
let mut current_idx = nak_pdu.pdu_header().header_len(); let mut current_idx = nak_pdu.pdu_header().header_len();
assert_eq!(current_idx + 9, nak_pdu.len_written()); assert_eq!(current_idx + 9, nak_pdu.len_written());
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
@@ -821,7 +842,7 @@ mod tests {
verify_raw_header(nak_pdu.pdu_header(), &buf); verify_raw_header(nak_pdu.pdu_header(), &buf);
let mut current_idx = nak_pdu.pdu_header().header_len(); let mut current_idx = nak_pdu.pdu_header().header_len();
assert_eq!(current_idx + 9 + 16, nak_pdu.len_written()); assert_eq!(current_idx + 9 + 16, nak_pdu.len_written());
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
@@ -1034,7 +1055,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 9, len_written); assert_eq!(current_idx + 9, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
@@ -1060,7 +1081,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 1 + 8 + 2, len_written); assert_eq!(current_idx + 1 + 8 + 2, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
@@ -1087,7 +1108,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 1 + 16, len_written); assert_eq!(current_idx + 1 + 16, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap()); u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap());
@@ -1131,7 +1152,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 1 + 16, len_written); assert_eq!(current_idx + 1 + 16, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap()); u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap());
@@ -1171,7 +1192,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 1 + 8 + num_segments * 8, len_written); assert_eq!(current_idx + 1 + 8 + num_segments * 8, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()); u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
@@ -1227,7 +1248,7 @@ mod tests {
verify_raw_header(&pdu_header, &buf); verify_raw_header(&pdu_header, &buf);
let mut current_idx = pdu_header.header_len(); let mut current_idx = pdu_header.header_len();
assert_eq!(current_idx + 1 + 16 + num_segments * 16, len_written); assert_eq!(current_idx + 1 + 16 + num_segments * 16, len_written);
assert_eq!(buf[current_idx], FileDirectiveType::NakPdu as u8); assert_eq!(buf[current_idx], FileDirectiveType::Nak as u8);
current_idx += 1; current_idx += 1;
let start_of_scope = let start_of_scope =
u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap()); u64::from_be_bytes(buf[current_idx..current_idx + 8].try_into().unwrap());

View File

@@ -22,6 +22,7 @@ pub mod msg_to_user;
/// Minimum length of a type-length-value structure, including type and length fields. /// Minimum length of a type-length-value structure, including type and length fields.
pub const MIN_TLV_LEN: usize = 2; pub const MIN_TLV_LEN: usize = 2;
/// Trait for generic TLV structures.
pub trait GenericTlv { pub trait GenericTlv {
/// TLV type field. /// TLV type field.
fn tlv_type_field(&self) -> TlvTypeField; fn tlv_type_field(&self) -> TlvTypeField;
@@ -109,21 +110,30 @@ pub enum TlvType {
EntityId = 0x06, EntityId = 0x06,
} }
/// TLV type field variants.
///
/// This allows specifying custom variants as well.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TlvTypeField { pub enum TlvTypeField {
/// Standard TLV types.
Standard(TlvType), Standard(TlvType),
/// Custom TLV type.
Custom(u8), Custom(u8),
} }
/// Filestore action codes as specified in the standard.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)] #[repr(u8)]
pub enum FilestoreActionCode { pub enum FilestoreActionCode {
/// Create file.
CreateFile = 0b0000, CreateFile = 0b0000,
/// Delete file.
DeleteFile = 0b0001, DeleteFile = 0b0001,
/// Rename file.
RenameFile = 0b0010, RenameFile = 0b0010,
/// This operation appends one file to another. The first specified name will form the first /// This operation appends one file to another. The first specified name will form the first
/// part of the new file and the name of the new file. This function can be used to get /// part of the new file and the name of the new file. This function can be used to get
@@ -132,9 +142,13 @@ pub enum FilestoreActionCode {
/// This operation replaces the content of the first specified file with the content of /// This operation replaces the content of the first specified file with the content of
/// the secondly specified file. /// the secondly specified file.
ReplaceFile = 0b0100, ReplaceFile = 0b0100,
/// Create directory.
CreateDirectory = 0b0101, CreateDirectory = 0b0101,
/// Remove directory.
RemoveDirectory = 0b0110, RemoveDirectory = 0b0110,
/// Deny file.
DenyFile = 0b0111, DenyFile = 0b0111,
/// Deny directory.
DenyDirectory = 0b1000, DenyDirectory = 0b1000,
} }
@@ -175,8 +189,10 @@ pub struct Tlv<'data> {
} }
impl<'data> Tlv<'data> { impl<'data> Tlv<'data> {
/// Minimum length of a TLV structure, including type and length fields.
pub const MIN_LEN: usize = MIN_TLV_LEN; pub const MIN_LEN: usize = MIN_TLV_LEN;
/// Generic constructor for a TLV structure.
pub fn new(tlv_type: TlvType, data: &[u8]) -> Result<Tlv<'_>, TlvLvDataTooLargeError> { pub fn new(tlv_type: TlvType, data: &[u8]) -> Result<Tlv<'_>, TlvLvDataTooLargeError> {
Ok(Tlv { Ok(Tlv {
tlv_type_field: TlvTypeField::Standard(tlv_type), tlv_type_field: TlvTypeField::Standard(tlv_type),
@@ -184,6 +200,7 @@ impl<'data> Tlv<'data> {
}) })
} }
/// Constructor for a TLV with a custom type field.
pub fn new_with_custom_type( pub fn new_with_custom_type(
tlv_type: u8, tlv_type: u8,
data: &[u8], data: &[u8],
@@ -225,6 +242,7 @@ impl<'data> Tlv<'data> {
self.lv.raw_data() self.lv.raw_data()
} }
/// Converts to an owned TLV variant, allocating memory for the value field.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn to_owned(&self) -> TlvOwned { pub fn to_owned(&self) -> TlvOwned {
TlvOwned { TlvOwned {
@@ -268,6 +286,7 @@ impl GenericTlv for Tlv<'_> {
} }
} }
/// Component of the TLV module which require [alloc] support.
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub mod alloc_mod { pub mod alloc_mod {
use super::*; use super::*;
@@ -283,6 +302,7 @@ pub mod alloc_mod {
} }
impl TlvOwned { impl TlvOwned {
/// Generic constructor.
pub fn new(tlv_type: TlvType, data: &[u8]) -> Self { pub fn new(tlv_type: TlvType, data: &[u8]) -> Self {
Self { Self {
tlv_type_field: TlvTypeField::Standard(tlv_type), tlv_type_field: TlvTypeField::Standard(tlv_type),
@@ -290,6 +310,7 @@ pub mod alloc_mod {
} }
} }
/// Generic constructor with a custom TLV type.
pub fn new_with_custom_type(tlv_type: u8, data: &[u8]) -> Self { pub fn new_with_custom_type(tlv_type: u8, data: &[u8]) -> Self {
Self { Self {
tlv_type_field: TlvTypeField::Custom(tlv_type), tlv_type_field: TlvTypeField::Custom(tlv_type),
@@ -305,6 +326,7 @@ pub mod alloc_mod {
} }
} }
/// Write to a byte slice.
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> { pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError> {
generic_len_check_data_serialization(buf, self.data.len(), MIN_TLV_LEN)?; generic_len_check_data_serialization(buf, self.data.len(), MIN_TLV_LEN)?;
buf[0] = self.tlv_type_field.into(); buf[0] = self.tlv_type_field.into();
@@ -318,6 +340,7 @@ pub mod alloc_mod {
self.data.len() + 2 self.data.len() + 2
} }
/// Convert to [Tlv]
pub fn as_tlv(&self) -> Tlv<'_> { pub fn as_tlv(&self) -> Tlv<'_> {
Tlv { Tlv {
tlv_type_field: self.tlv_type_field, tlv_type_field: self.tlv_type_field,
@@ -366,6 +389,7 @@ pub mod alloc_mod {
} }
} }
/// Entity ID TLV.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -374,6 +398,7 @@ pub struct EntityIdTlv {
} }
impl EntityIdTlv { impl EntityIdTlv {
/// Constructor.
#[inline] #[inline]
pub fn new(entity_id: UnsignedByteField) -> Self { pub fn new(entity_id: UnsignedByteField) -> Self {
Self { entity_id } Self { entity_id }
@@ -389,21 +414,25 @@ impl EntityIdTlv {
Ok(()) Ok(())
} }
/// Entity ID.
#[inline] #[inline]
pub fn entity_id(&self) -> &UnsignedByteField { pub fn entity_id(&self) -> &UnsignedByteField {
&self.entity_id &self.entity_id
} }
/// Length of the value field.
#[inline] #[inline]
pub fn len_value(&self) -> usize { pub fn len_value(&self) -> usize {
self.entity_id.size() self.entity_id.size()
} }
/// Full length of the TLV, including type and length fields.
#[inline] #[inline]
pub fn len_full(&self) -> usize { pub fn len_full(&self) -> usize {
2 + self.entity_id.size() 2 + self.entity_id.size()
} }
/// Create from a raw bytestream.
pub fn from_bytes(buf: &[u8]) -> Result<Self, TlvLvError> { pub fn from_bytes(buf: &[u8]) -> Result<Self, TlvLvError> {
Self::len_check(buf)?; Self::len_check(buf)?;
verify_tlv_type(buf[0], TlvType::EntityId)?; verify_tlv_type(buf[0], TlvType::EntityId)?;
@@ -495,6 +524,7 @@ impl TryFrom<Tlv<'_>> for EntityIdTlv {
} }
} }
/// Does the [FilestoreActionCode] have a second filename?
#[inline] #[inline]
pub fn fs_request_has_second_filename(action_code: FilestoreActionCode) -> bool { pub fn fs_request_has_second_filename(action_code: FilestoreActionCode) -> bool {
if action_code == FilestoreActionCode::RenameFile if action_code == FilestoreActionCode::RenameFile
@@ -528,6 +558,7 @@ impl FilestoreTlvBase<'_, '_> {
} }
} }
/// Filestore request TLV.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct FilestoreRequestTlv<'first_name, 'second_name> { pub struct FilestoreRequestTlv<'first_name, 'second_name> {
@@ -536,14 +567,17 @@ pub struct FilestoreRequestTlv<'first_name, 'second_name> {
} }
impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> { impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
/// Constructor for file creation.
pub fn new_create_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_create_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::CreateFile, file_name, None) Self::new(FilestoreActionCode::CreateFile, file_name, None)
} }
/// Constructor for file deletion.
pub fn new_delete_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_delete_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::DeleteFile, file_name, None) Self::new(FilestoreActionCode::DeleteFile, file_name, None)
} }
/// Constructor for file renaming.
pub fn new_rename_file( pub fn new_rename_file(
source_name: Lv<'first_name>, source_name: Lv<'first_name>,
target_name: Lv<'second_name>, target_name: Lv<'second_name>,
@@ -583,18 +617,22 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
) )
} }
/// Constructor for directory creation.
pub fn new_create_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_create_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::CreateDirectory, dir_name, None) Self::new(FilestoreActionCode::CreateDirectory, dir_name, None)
} }
/// Constructor for directory removal.
pub fn new_remove_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_remove_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::RemoveDirectory, dir_name, None) Self::new(FilestoreActionCode::RemoveDirectory, dir_name, None)
} }
/// Constructor for file denial.
pub fn new_deny_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_deny_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::DenyFile, file_name, None) Self::new(FilestoreActionCode::DenyFile, file_name, None)
} }
/// Constructor for directory denial.
pub fn new_deny_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> { pub fn new_deny_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::DenyDirectory, dir_name, None) Self::new(FilestoreActionCode::DenyDirectory, dir_name, None)
} }
@@ -628,31 +666,37 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
}) })
} }
/// Action code.
#[inline] #[inline]
pub fn action_code(&self) -> FilestoreActionCode { pub fn action_code(&self) -> FilestoreActionCode {
self.base.action_code self.base.action_code
} }
/// First name as [Lv].
#[inline] #[inline]
pub fn first_name(&self) -> Lv<'first_name> { pub fn first_name(&self) -> Lv<'first_name> {
self.base.first_name self.base.first_name
} }
/// First name as optional [Lv].
#[inline] #[inline]
pub fn second_name(&self) -> Option<Lv<'second_name>> { pub fn second_name(&self) -> Option<Lv<'second_name>> {
self.base.second_name self.base.second_name
} }
/// Length of the value field.
#[inline] #[inline]
pub fn len_value(&self) -> usize { pub fn len_value(&self) -> usize {
self.base.base_len_value() self.base.base_len_value()
} }
/// Full TLV length.
#[inline] #[inline]
pub fn len_full(&self) -> usize { pub fn len_full(&self) -> usize {
2 + self.len_value() 2 + self.len_value()
} }
/// Construct from a raw bytestream.
pub fn from_bytes<'longest: 'first_name + 'second_name>( pub fn from_bytes<'longest: 'first_name + 'second_name>(
buf: &'longest [u8], buf: &'longest [u8],
) -> Result<Self, TlvLvError> { ) -> Result<Self, TlvLvError> {
@@ -737,6 +781,7 @@ impl GenericTlv for FilestoreRequestTlv<'_, '_> {
} }
} }
/// Filestore response TLV.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))] #[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -768,6 +813,8 @@ impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'seco
Lv::new_empty(), Lv::new_empty(),
) )
} }
/// Generic constructor.
pub fn new( pub fn new(
action_code: FilestoreActionCode, action_code: FilestoreActionCode,
status_code: u8, status_code: u8,
@@ -796,6 +843,7 @@ impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'seco
}) })
} }
/// Check whether this response has a second filename.
pub fn has_second_filename(action_code: FilestoreActionCode) -> bool { pub fn has_second_filename(action_code: FilestoreActionCode) -> bool {
if action_code == FilestoreActionCode::RenameFile if action_code == FilestoreActionCode::RenameFile
|| action_code == FilestoreActionCode::AppendFile || action_code == FilestoreActionCode::AppendFile
@@ -806,36 +854,43 @@ impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'seco
false false
} }
/// Action code.
#[inline] #[inline]
pub fn action_code(&self) -> FilestoreActionCode { pub fn action_code(&self) -> FilestoreActionCode {
self.base.action_code self.base.action_code
} }
/// Status code.
#[inline] #[inline]
pub fn status_code(&self) -> u8 { pub fn status_code(&self) -> u8 {
self.status_code self.status_code
} }
/// First name as [Lv].
#[inline] #[inline]
pub fn first_name(&self) -> Lv<'first_name> { pub fn first_name(&self) -> Lv<'first_name> {
self.base.first_name self.base.first_name
} }
/// Optional second name as [Lv].
#[inline] #[inline]
pub fn second_name(&self) -> Option<Lv<'second_name>> { pub fn second_name(&self) -> Option<Lv<'second_name>> {
self.base.second_name self.base.second_name
} }
/// Length of the value field.
#[inline] #[inline]
pub fn len_value(&self) -> usize { pub fn len_value(&self) -> usize {
self.base.base_len_value() + self.filestore_message.len_full() self.base.base_len_value() + self.filestore_message.len_full()
} }
/// Full length of the TLV.
#[inline] #[inline]
pub fn len_full(&self) -> usize { pub fn len_full(&self) -> usize {
2 + self.len_value() 2 + self.len_value()
} }
/// Construct from a raw bytestream.
pub fn from_bytes<'buf: 'first_name + 'second_name + 'fs_msg>( pub fn from_bytes<'buf: 'first_name + 'second_name + 'fs_msg>(
buf: &'buf [u8], buf: &'buf [u8],
) -> Result<Self, TlvLvError> { ) -> Result<Self, TlvLvError> {

View File

@@ -8,8 +8,10 @@ use crate::{
}; };
use delegate::delegate; use delegate::delegate;
/// Message To User TLV structure.
#[derive(Debug, Copy, Clone, PartialEq, Eq)] #[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct MsgToUserTlv<'data> { pub struct MsgToUserTlv<'data> {
/// Wrapped generic TLV structure.
pub tlv: Tlv<'data>, pub tlv: Tlv<'data>,
} }
@@ -23,7 +25,9 @@ impl<'data> MsgToUserTlv<'data> {
delegate! { delegate! {
to self.tlv { to self.tlv {
/// Value field of the TLV.
pub fn value(&self) -> &[u8]; pub fn value(&self) -> &[u8];
/// Helper method to retrieve the length of the value. Simply calls the [slice::len] method of /// Helper method to retrieve the length of the value. Simply calls the [slice::len] method of
/// [Self::value] /// [Self::value]
pub fn len_value(&self) -> usize; pub fn len_value(&self) -> usize;
@@ -37,14 +41,16 @@ impl<'data> MsgToUserTlv<'data> {
} }
} }
/// Is this a standard TLV?
#[inline] #[inline]
pub fn is_standard_tlv(&self) -> bool { pub fn is_standard_tlv(&self) -> bool {
true true
} }
/// TLV type field.
#[inline] #[inline]
pub fn tlv_type(&self) -> Option<TlvType> { pub fn tlv_type(&self) -> TlvType {
Some(TlvType::MsgToUser) TlvType::MsgToUser
} }
/// Check whether this message is a reserved CFDP message like a Proxy Operation Message. /// Check whether this message is a reserved CFDP message like a Proxy Operation Message.
@@ -85,11 +91,13 @@ impl<'data> MsgToUserTlv<'data> {
Ok(msg_to_user) Ok(msg_to_user)
} }
/// Convert to a generic [Tlv].
#[inline] #[inline]
pub fn to_tlv(&self) -> Tlv<'data> { pub fn to_tlv(&self) -> Tlv<'data> {
self.tlv self.tlv
} }
/// Convert to an [TlvOwned].
#[cfg(feature = "alloc")] #[cfg(feature = "alloc")]
pub fn to_owned(&self) -> TlvOwned { pub fn to_owned(&self) -> TlvOwned {
self.tlv.to_owned() self.tlv.to_owned()
@@ -102,6 +110,7 @@ impl<'data> MsgToUserTlv<'data> {
delegate!( delegate!(
to self.tlv { to self.tlv {
/// Write the TLV to a byte buffer.
pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>; pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, ByteConversionError>;
} }
); );
@@ -142,7 +151,7 @@ mod tests {
assert!(msg_to_user.is_ok()); assert!(msg_to_user.is_ok());
let msg_to_user = msg_to_user.unwrap(); let msg_to_user = msg_to_user.unwrap();
assert!(msg_to_user.is_standard_tlv()); assert!(msg_to_user.is_standard_tlv());
assert_eq!(msg_to_user.tlv_type().unwrap(), TlvType::MsgToUser); assert_eq!(msg_to_user.tlv_type(), TlvType::MsgToUser);
assert_eq!( assert_eq!(
msg_to_user.tlv_type_field(), msg_to_user.tlv_type_field(),
TlvTypeField::Standard(TlvType::MsgToUser) TlvTypeField::Standard(TlvType::MsgToUser)

View File

@@ -1,4 +1,5 @@
//! CCSDS Time Code Formats according to [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf) //! CCSDS Time Code Formats according to [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
#![warn(missing_docs)]
use crate::ByteConversionError; use crate::ByteConversionError;
#[cfg(feature = "chrono")] #[cfg(feature = "chrono")]
use chrono::{TimeZone, Utc}; use chrono::{TimeZone, Utc};