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
- `CdsCommon` renamed to `CdsBase`
- cfdp: Removed `FileDirectiveType` variant `*Pdu` suffix
- 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`
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:
cargo clippy -- -D warnings
fmt:
cargo fmt --all
check-fmt:
cargo fmt --all -- --check
check:

View File

@@ -183,7 +183,7 @@ impl<'data> Lv<'data> {
}
#[cfg(test)]
pub mod tests {
mod tests {
use alloc::string::ToString;
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).
#![warn(missing_docs)]
use crate::ByteConversionError;
use num_enum::{IntoPrimitive, TryFromPrimitive};
#[cfg(feature = "serde")]

View File

@@ -1,3 +1,4 @@
//! # Acknowledgement (ACK) PDU packet implementation.
use crate::{
cfdp::{ConditionCode, CrcFlag, Direction, TransactionStatus},
ByteConversionError,
@@ -10,6 +11,7 @@ use super::{
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
/// Invalid [FileDirectiveType] of the acknowledged PDU error.
#[derive(Debug, Clone, Copy, PartialEq, Eq, thiserror::Error)]
#[error("invalid directive code of acknowledged PDU")]
pub struct InvalidAckedDirectiveCodeError(pub FileDirectiveType);
@@ -28,15 +30,16 @@ pub struct AckPdu {
}
impl AckPdu {
/// Constructor.
pub fn new(
mut pdu_header: PduHeader,
directive_code_of_acked_pdu: FileDirectiveType,
condition_code: ConditionCode,
transaction_status: TransactionStatus,
) -> 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;
} 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;
} else {
return Err(InvalidAckedDirectiveCodeError(directive_code_of_acked_pdu));
@@ -52,6 +55,9 @@ impl AckPdu {
Ok(ack_pdu)
}
/// Constructor for an ACK PDU acknowledging an EOF PDU.
///
/// Relevant for the file receiver.
pub fn new_for_eof_pdu(
pdu_header: PduHeader,
condition_code: ConditionCode,
@@ -60,13 +66,16 @@ impl AckPdu {
// Unwrap okay here, [new] can only fail on invalid directive codes.
Self::new(
pdu_header,
FileDirectiveType::EofPdu,
FileDirectiveType::Eof,
condition_code,
transaction_status,
)
.unwrap()
}
/// Constructor for an ACK PDU acknowledging a Finished PDU.
///
/// Relevant for the file sender.
pub fn new_for_finished_pdu(
pdu_header: PduHeader,
condition_code: ConditionCode,
@@ -75,28 +84,32 @@ impl AckPdu {
// Unwrap okay here, [new] can only fail on invalid directive codes.
Self::new(
pdu_header,
FileDirectiveType::FinishedPdu,
FileDirectiveType::Finished,
condition_code,
transaction_status,
)
.unwrap()
}
/// PDU header.
#[inline]
pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header
}
/// Directive code of the acknowledged PDU.
#[inline]
pub fn directive_code_of_acked_pdu(&self) -> FileDirectiveType {
self.directive_code_of_acked_pdu
}
/// Condition code.
#[inline]
pub fn condition_code(&self) -> ConditionCode {
self.condition_code
}
/// Transaction status.
#[inline]
pub fn transaction_status(&self) -> TransactionStatus {
self.transaction_status
@@ -110,6 +123,7 @@ impl AckPdu {
3
}
/// Construct [Self] from the provided byte slice.
pub fn from_bytes(buf: &[u8]) -> Result<AckPdu, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(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(|_| {
PduError::InvalidDirectiveType {
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 {
found: directive_type,
expected: FileDirectiveType::AckPdu,
expected: FileDirectiveType::Ack,
});
}
current_idx += 1;
@@ -134,8 +148,8 @@ impl AckPdu {
expected: None,
}
})?;
if acked_directive_type != FileDirectiveType::EofPdu
&& acked_directive_type != FileDirectiveType::FinishedPdu
if acked_directive_type != FileDirectiveType::Eof
&& acked_directive_type != FileDirectiveType::Finished
{
return Err(PduError::InvalidDirectiveType {
found: acked_directive_type as u8,
@@ -167,11 +181,11 @@ impl AckPdu {
.into());
}
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;
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
// acknowledges a Finished PDU, and to 0b0000 otherwise.
buf[current_idx] |= 0b0001;
@@ -185,6 +199,7 @@ impl AckPdu {
Ok(current_idx)
}
/// Length of the written PDU in bytes.
pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len()
}
@@ -198,7 +213,7 @@ impl CfdpPdu for AckPdu {
#[inline]
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.file_flag(), LargeFileFlag::Normal);
assert_eq!(ack_pdu.pdu_type(), PduType::FileDirective);
assert_eq!(
ack_pdu.file_directive_type(),
Some(FileDirectiveType::AckPdu)
);
assert_eq!(ack_pdu.file_directive_type(), Some(FileDirectiveType::Ack));
assert_eq!(ack_pdu.transmission_mode(), TransmissionMode::Acknowledged);
assert_eq!(ack_pdu.direction(), expected_dir);
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 ack_pdu = AckPdu::new(
pdu_header,
FileDirectiveType::FinishedPdu,
FileDirectiveType::Finished,
ConditionCode::NoError,
TransactionStatus::Active,
)
.expect("creating ACK PDU failed");
assert_eq!(
ack_pdu.directive_code_of_acked_pdu(),
FileDirectiveType::FinishedPdu
FileDirectiveType::Finished
);
verify_state(&ack_pdu, CrcFlag::NoCrc, Direction::TowardsReceiver);
}
@@ -273,8 +285,8 @@ mod tests {
assert_eq!(written, ack_pdu.len_written());
verify_raw_header(ack_pdu.pdu_header(), &buf);
assert_eq!(buf[7], FileDirectiveType::AckPdu as u8);
assert_eq!((buf[8] >> 4) & 0b1111, FileDirectiveType::FinishedPdu as u8);
assert_eq!(buf[7], FileDirectiveType::Ack as u8);
assert_eq!((buf[8] >> 4) & 0b1111, FileDirectiveType::Finished as u8);
assert_eq!(buf[8] & 0b1111, 0b0001);
assert_eq!(buf[9] >> 4 & 0b1111, condition_code 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 ack_pdu = AckPdu::new(
pdu_header,
FileDirectiveType::FinishedPdu,
FileDirectiveType::Finished,
ConditionCode::NoError,
TransactionStatus::Active,
)
@@ -320,12 +332,12 @@ mod tests {
assert_eq!(
AckPdu::new(
pdu_header,
FileDirectiveType::MetadataPdu,
FileDirectiveType::Metadata,
ConditionCode::NoError,
TransactionStatus::Active,
)
.unwrap_err(),
InvalidAckedDirectiveCodeError(FileDirectiveType::MetadataPdu)
InvalidAckedDirectiveCodeError(FileDirectiveType::Metadata)
);
}
@@ -372,7 +384,7 @@ mod tests {
);
assert_eq!(
ack_pdu.directive_code_of_acked_pdu(),
FileDirectiveType::EofPdu
FileDirectiveType::Eof
);
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::{
add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field,
FileDirectiveType, PduError, PduHeader,
@@ -25,6 +26,7 @@ pub struct EofPdu {
}
impl EofPdu {
/// Constructor.
pub fn new(
mut pdu_header: PduHeader,
condition_code: ConditionCode,
@@ -45,6 +47,7 @@ impl EofPdu {
eof_pdu
}
/// Constructor for no error EOF PDUs.
pub fn new_no_error(pdu_header: PduHeader, file_checksum: u32, file_size: u64) -> Self {
Self::new(
pdu_header,
@@ -55,21 +58,25 @@ impl EofPdu {
)
}
/// PDU header.
#[inline]
pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header
}
/// Condition code.
#[inline]
pub fn condition_code(&self) -> ConditionCode {
self.condition_code
}
/// File checksum.
#[inline]
pub fn file_checksum(&self) -> u32 {
self.file_checksum
}
/// File size.
#[inline]
pub fn file_size(&self) -> u64 {
self.file_size
@@ -90,6 +97,7 @@ impl EofPdu {
len
}
/// Construct [Self] from the provided byte slice.
pub fn from_bytes(buf: &[u8]) -> Result<EofPdu, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(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(|_| {
PduError::InvalidDirectiveType {
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 {
found: directive_type,
expected: FileDirectiveType::EofPdu,
expected: FileDirectiveType::Eof,
});
}
current_idx += 1;
@@ -145,7 +153,7 @@ impl EofPdu {
.into());
}
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;
buf[current_idx] = (self.condition_code as u8) << 4;
current_idx += 1;
@@ -165,6 +173,7 @@ impl EofPdu {
Ok(current_idx)
}
/// Length of the written PDU in bytes.
pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len()
}
@@ -178,7 +187,7 @@ impl CfdpPdu for EofPdu {
#[inline]
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.file_flag(), file_flag);
assert_eq!(eof_pdu.pdu_type(), PduType::FileDirective);
assert_eq!(
eof_pdu.file_directive_type(),
Some(FileDirectiveType::EofPdu)
);
assert_eq!(eof_pdu.file_directive_type(), Some(FileDirectiveType::Eof));
assert_eq!(eof_pdu.transmission_mode(), TransmissionMode::Acknowledged);
assert_eq!(eof_pdu.direction(), Direction::TowardsReceiver);
assert_eq!(eof_pdu.source_id(), TEST_SRC_ID.into());
@@ -253,7 +259,7 @@ mod tests {
assert_eq!(written, eof_pdu.len_written());
verify_raw_header(eof_pdu.pdu_header(), &buf);
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;
assert_eq!(
(buf[current_idx] >> 4) & 0b1111,

View File

@@ -1,3 +1,4 @@
//! # File Data PDU packet implementation
use crate::cfdp::pdu::{
add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field,
PduError, PduHeader,
@@ -10,18 +11,24 @@ use serde::{Deserialize, Serialize};
use super::{CfdpPdu, FileDirectiveType, WritablePduPacket};
/// Record continuation state for segment metadata.
#[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[bitbybit::bitenum(u2, exhaustive = true)]
#[repr(u8)]
pub enum RecordContinuationState {
/// No start and no end.
NoStartNoEnd = 0b00,
/// Start without end.
StartWithoutEnd = 0b01,
/// End without start.
EndWithoutStart = 0b10,
/// Start and end.
StartAndEnd = 0b11,
}
/// Segment metadata structure.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct SegmentMetadata<'seg_meta> {
@@ -30,6 +37,7 @@ pub struct SegmentMetadata<'seg_meta> {
}
impl<'seg_meta> SegmentMetadata<'seg_meta> {
/// Constructor.
pub fn new(
record_continuation_state: RecordContinuationState,
metadata: Option<&'seg_meta [u8]>,
@@ -45,16 +53,19 @@ impl<'seg_meta> SegmentMetadata<'seg_meta> {
})
}
/// Record continuation state.
#[inline]
pub fn record_continuation_state(&self) -> RecordContinuationState {
self.record_continuation_state
}
/// Raw metadata slice.
#[inline]
pub fn metadata(&self) -> Option<&'seg_meta [u8]> {
self.metadata
}
/// Length of the written segment metadata structure.
#[inline]
pub fn len_written(&self) -> usize {
// 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> {
/// Constructor for a file data PDU including segment metadata.
pub fn new_with_seg_metadata(
pdu_header: PduHeader,
segment_metadata: SegmentMetadata<'seg_meta>,
offset: u64,
file_data: &'file_data [u8],
) -> 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(
pdu_header: PduHeader,
offset: u64,
file_data: &'file_data [u8],
) -> 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,
segment_metadata: Option<SegmentMetadata<'seg_meta>>,
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)
}
/// Optional segment metadata.
#[inline]
pub fn segment_metadata(&self) -> Option<&SegmentMetadata<'_>> {
self.common.segment_metadata.as_ref()
}
/// PDU header.
#[inline]
pub fn pdu_header(&self) -> &PduHeader {
self.common.pdu_header()
}
/// File data offset.
#[inline]
pub fn offset(&self) -> u64 {
self.common.offset
}
/// File data.
#[inline]
pub fn file_data(&self) -> &'file_data [u8] {
self.file_data
}
/// Read [Self] from the provided buffer.
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 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)
}
/// Length of the written PDU.
pub fn len_written(&self) -> usize {
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> {
/// Constructor for a file data PDU including segment metadata.
pub fn new_with_seg_metadata(
pdu_header: PduHeader,
segment_metadata: SegmentMetadata<'seg_meta>,
offset: u64,
file_data_len: u64,
) -> 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 {
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,
segment_metadata: Option<SegmentMetadata<'seg_meta>>,
offset: u64,
@@ -362,6 +385,7 @@ impl<'seg_meta> FileDataPduCreatorWithReservedDatafield<'seg_meta> {
self.common.calc_pdu_datafield_len(self.file_data_len)
}
/// Length of the written PDU.
pub fn len_written(&self) -> usize {
self.common.pdu_header.header_len() + self.calc_pdu_datafield_len()
}
@@ -423,6 +447,7 @@ pub struct FileDataPduCreatorWithUnwrittenData<'buf> {
}
impl FileDataPduCreatorWithUnwrittenData<'_> {
/// Mutable access to the file data field.
pub fn file_data_field_mut(&mut self) -> &mut [u8] {
&mut self.write_buf[self.file_data_offset 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::{
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::{CfdpPdu, InvalidTlvTypeFieldError, WritablePduPacket};
/// Delivery code enumeration.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum DeliveryCode {
/// Completed delivery.
Complete = 0,
/// Incomplete delivery.
Incomplete = 1,
}
/// File status enumeration.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum FileStatus {
/// File was discarded deliberately.
DiscardDeliberately = 0b00,
/// File was rejected by the filestore.
DiscardedFsRejection = 0b01,
/// File was retained (but not necesarilly complete).
Retained = 0b10,
/// Unreported file status.
Unreported = 0b11,
}
@@ -65,6 +74,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
)
}
/// Constructor where the fault location is provided.
pub fn new_with_error(
pdu_header: PduHeader,
condition_code: ConditionCode,
@@ -82,6 +92,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
)
}
/// Generic constructor.
pub fn new(
mut pdu_header: PduHeader,
condition_code: ConditionCode,
@@ -109,32 +120,37 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
finished_pdu
}
/// PDU header.
#[inline]
pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header
}
/// Condition code.
#[inline]
pub fn condition_code(&self) -> ConditionCode {
self.condition_code
}
/// Delivery code.
#[inline]
pub fn delivery_code(&self) -> DeliveryCode {
self.delivery_code
}
/// File status.
#[inline]
pub fn file_status(&self) -> FileStatus {
self.file_status
}
// If there are no filestore responses, an empty slice will be returned.
/// Filestore responses as a slice.
#[inline]
pub fn filestore_responses(&self) -> &[FilestoreResponseTlv<'_, '_, '_>] {
self.fs_responses
}
/// Optional fault location [EntityIdTlv].
#[inline]
pub fn fault_location(&self) -> Option<EntityIdTlv> {
self.fault_location
@@ -166,7 +182,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
}
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;
buf[current_idx] = ((self.condition_code as u8) << 4)
| ((self.delivery_code as u8) << 2)
@@ -184,6 +200,7 @@ impl<'fs_responses> FinishedPduCreator<'fs_responses> {
Ok(current_idx)
}
/// Length of the written PDU in bytes.
pub fn len_written(&self) -> usize {
self.pdu_header.header_len() + self.calc_pdu_datafield_len()
}
@@ -197,7 +214,7 @@ impl CfdpPdu for FinishedPduCreator<'_> {
#[inline]
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)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[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(|_| {
PduError::InvalidDirectiveType {
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 {
found: directive_type,
expected: FileDirectiveType::FinishedPdu,
expected: FileDirectiveType::Finished,
});
}
current_idx += 1;
@@ -297,11 +315,13 @@ impl<'buf> FinishedPduReader<'buf> {
})
}
/// Raw filestore responses.
#[inline]
pub fn fs_responses_raw(&self) -> &[u8] {
self.fs_responses_raw
}
/// Iterator over the filestore responses.
#[inline]
pub fn fs_responses_iter(&self) -> FilestoreResponseIterator<'_> {
FilestoreResponseIterator {
@@ -310,26 +330,31 @@ impl<'buf> FinishedPduReader<'buf> {
}
}
/// Condition code.
#[inline]
pub fn condition_code(&self) -> ConditionCode {
self.condition_code
}
/// Delivery code.
#[inline]
pub fn delivery_code(&self) -> DeliveryCode {
self.delivery_code
}
/// File status.
#[inline]
pub fn file_status(&self) -> FileStatus {
self.file_status
}
/// Optional fault location [EntityIdTlv].
#[inline]
pub fn fault_location(&self) -> Option<EntityIdTlv> {
self.fault_location
}
/// PDU header.
#[inline]
pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header
@@ -399,7 +424,7 @@ impl CfdpPdu for FinishedPduReader<'_> {
#[inline]
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.file_directive_type(),
Some(FileDirectiveType::FinishedPdu)
Some(FileDirectiveType::Finished)
);
assert_eq!(
finished_pdu.transmission_mode(),
@@ -500,7 +525,7 @@ mod tests {
);
verify_raw_header(finished_pdu.pdu_header(), &buf);
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;
assert_eq!(
(buf[current_idx] >> 4) & 0b1111,

View File

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

View File

@@ -15,24 +15,34 @@ pub mod finished;
pub mod metadata;
pub mod nak;
/// File directive type.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum FileDirectiveType {
EofPdu = 0x04,
FinishedPdu = 0x05,
AckPdu = 0x06,
MetadataPdu = 0x07,
NakPdu = 0x08,
PromptPdu = 0x09,
KeepAlivePdu = 0x0c,
/// EOF.
Eof = 0x04,
/// Finished.
Finished = 0x05,
/// ACK.
Ack = 0x06,
/// Metadata.
Metadata = 0x07,
/// NAK.
Nak = 0x08,
/// Prompt.
Prompt = 0x09,
/// Keep Alive.
KeepAlive = 0x0c,
}
/// PDU error.
#[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum PduError {
/// Byte conversion error.
#[error("byte conversion error: {0}")]
ByteConversion(#[from] ByteConversionError),
/// 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.
#[error("invalid transaction ID length {0}")]
InvalidTransactionSeqNumLen(u8),
/// Source and destination entity ID lengths do not match.
#[error(
"missmatch of PDU source ID length {src_id_len} and destination ID length {dest_id_len}"
)]
SourceDestIdLenMissmatch {
/// Source ID length.
src_id_len: usize,
/// Destination ID length.
dest_id_len: usize,
},
/// Wrong directive type, for example when parsing the directive field for a file directive
/// PDU.
#[error("wrong directive type, found {found:?}, expected {expected:?}")]
WrongDirectiveType {
/// Found directive type.
found: FileDirectiveType,
/// Expected directive type.
expected: FileDirectiveType,
},
/// 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.
#[error("invalid directive type, found {found:?}, expected {expected:?}")]
InvalidDirectiveType {
/// Found raw directive type.
found: u8,
/// Expected raw directive type if applicable.
expected: Option<FileDirectiveType>,
},
/// Invalid start or end of scope for a NAK PDU.
#[error("nak pdu: {0}")]
InvalidStartOrEndOfScope(#[from] InvalidStartOrEndOfScopeError),
/// 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/).
#[error("invalid checksum type {0}")]
InvalidChecksumType(u8),
/// File size is too large.
#[error("file size {0} too large")]
FileSizeTooLarge(u64),
/// 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 {
/// Length when written to bytes.
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>;
/// Convert the PDU to an owned vector of bytes.
#[cfg(feature = "alloc")]
fn to_vec(&self) -> Result<Vec<u8>, PduError> {
// 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.
pub trait CfdpPdu {
/// PDU header.
fn pdu_header(&self) -> &PduHeader;
/// Source ID (file sender).
#[inline]
fn source_id(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().source_entity_id
}
/// Destination ID (file sender).
#[inline]
fn dest_id(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().dest_entity_id
}
/// Transaction sequence number.
#[inline]
fn transaction_seq_num(&self) -> UnsignedByteField {
self.pdu_header().common_pdu_conf().transaction_seq_num
}
/// Transmission mode.
#[inline]
fn transmission_mode(&self) -> TransmissionMode {
self.pdu_header().common_pdu_conf().trans_mode
}
/// Direction.
#[inline]
fn direction(&self) -> Direction {
self.pdu_header().common_pdu_conf().direction
}
/// CRC flag.
#[inline]
fn crc_flag(&self) -> CrcFlag {
self.pdu_header().common_pdu_conf().crc_flag
}
/// File flag.
#[inline]
fn file_flag(&self) -> LargeFileFlag {
self.pdu_header().common_pdu_conf().file_flag
}
/// PDU type.
#[inline]
fn pdu_type(&self) -> PduType {
self.pdu_header().pdu_type()
}
/// File directive type when applicable.
fn file_directive_type(&self) -> Option<FileDirectiveType>;
}
@@ -169,15 +204,21 @@ pub trait CfdpPdu {
pub struct CommonPduConfig {
source_entity_id: UnsignedByteField,
dest_entity_id: UnsignedByteField,
/// Transaction sequence number.
pub transaction_seq_num: UnsignedByteField,
/// Transmission mode.
pub trans_mode: TransmissionMode,
/// File flag.
pub file_flag: LargeFileFlag,
/// CRC flag.
pub crc_flag: CrcFlag,
/// Direction.
pub direction: Direction,
}
// TODO: Builder pattern might be applicable here..
impl CommonPduConfig {
/// Generic constructor.
#[inline]
pub fn new(
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]
pub fn new_with_byte_fields(
source_id: impl Into<UnsignedByteField>,
@@ -227,6 +269,7 @@ impl CommonPduConfig {
)
}
/// Source ID (file sender).
#[inline]
pub fn source_id(&self) -> UnsignedByteField {
self.source_entity_id
@@ -255,6 +298,7 @@ impl CommonPduConfig {
Ok((source_id, dest_id))
}
/// Set the source and destination ID field.
#[inline]
pub fn set_source_and_dest_id(
&mut self,
@@ -267,6 +311,7 @@ impl CommonPduConfig {
Ok(())
}
/// Destination ID (file receiver).
#[inline]
pub fn dest_id(&self) -> UnsignedByteField {
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;
/// Abstraction for the PDU header common to all CFDP PDUs.
@@ -322,8 +368,10 @@ pub struct PduHeader {
}
impl PduHeader {
/// Fixed length of the PDU header when written to a raw buffer.
pub const FIXED_LEN: usize = FIXED_HEADER_LEN;
/// Constructor for a File Data PDU header.
#[inline]
pub fn new_for_file_data(
pdu_conf: CommonPduConfig,
@@ -340,6 +388,7 @@ impl PduHeader {
)
}
/// Constructor for a file data PDU.
#[inline]
pub fn new_for_file_data_default(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
Self::new_generic(
@@ -351,6 +400,7 @@ impl PduHeader {
)
}
/// Constructor for a file directive PDU.
#[inline]
pub fn new_for_file_directive(pdu_conf: CommonPduConfig, pdu_datafield_len: u16) -> Self {
Self::new_generic(
@@ -362,6 +412,7 @@ impl PduHeader {
)
}
/// Constructor from a given [CommonPduConfig] and for a file directive PDU.
#[inline]
pub fn from_pdu_conf_for_file_directive(pdu_conf: CommonPduConfig) -> Self {
Self::new_generic(
@@ -373,6 +424,7 @@ impl PduHeader {
)
}
/// Generic constructor.
#[inline]
pub fn new_generic(
pdu_type: PduType,
@@ -399,6 +451,7 @@ impl PduHeader {
+ self.pdu_conf.dest_entity_id.size()
}
/// PDU data field length.
#[inline]
pub fn pdu_datafield_len(&self) -> usize {
self.pdu_datafield_len.into()
@@ -411,6 +464,7 @@ impl PduHeader {
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> {
// The API does not allow passing entity IDs with different sizes, so this should
// never happen.
@@ -578,21 +632,25 @@ impl PduHeader {
))
}
/// PDU type.
#[inline]
pub fn pdu_type(&self) -> PduType {
self.pdu_type
}
/// Common PDU configuration fields.
#[inline]
pub fn common_pdu_conf(&self) -> &CommonPduConfig {
&self.pdu_conf
}
/// Segment metadata flag.
#[inline]
pub fn seg_metadata_flag(&self) -> SegmentMetadataFlag {
self.seg_metadata_flag
}
/// Segmentation Control.
#[inline]
pub fn seg_ctrl(&self) -> SegmentationControl {
self.seg_ctrl

View File

@@ -1,3 +1,4 @@
//! # NAK PDU packet implementation.
use crate::{
cfdp::{CrcFlag, Direction, LargeFileFlag},
ByteConversionError,
@@ -8,6 +9,7 @@ use super::{
PduHeader, WritablePduPacket,
};
/// Invalid start or end of scope value.
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[error("invalid start or end of scope value for NAK PDU")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -52,6 +54,7 @@ fn write_start_and_end_of_scope(
current_index
}
/// Buffer is too small to even hold a PDU header.
#[derive(Debug, PartialEq, Eq, Copy, Clone, thiserror::Error)]
#[error("packet buffer too small for PDU header")]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -94,11 +97,14 @@ pub fn calculate_max_segment_requests(
#[derive(Debug, PartialEq, Eq, Clone)]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum SegmentRequests<'a> {
/// Segment requests for normal file sizes bounded by [u32::MAX].
U32Pairs(&'a [(u32, u32)]),
/// Segment requests for large file sizes bounded by [u64::MAX].
U64Pairs(&'a [(u64, u64)]),
}
impl SegmentRequests<'_> {
/// Wrapped segment requests are empty.
#[inline]
pub fn is_empty(&self) -> bool {
match self {
@@ -152,6 +158,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
)
}
/// Constructor for large file sizes.
pub fn new_large_file_size(
pdu_header: PduHeader,
start_of_scope: u64,
@@ -206,21 +213,25 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
Ok(nak_pdu)
}
/// Start of scope of the NAK PDU.
#[inline]
pub fn start_of_scope(&self) -> u64 {
self.start_of_scope
}
/// End of scope of the NAK PDU.
#[inline]
pub fn end_of_scope(&self) -> u64 {
self.end_of_scope
}
/// Semgent requests.
#[inline]
pub fn segment_requests(&self) -> Option<&SegmentRequests<'_>> {
self.segment_requests.as_ref()
}
/// Number of segment requests.
#[inline]
pub fn num_segment_reqs(&self) -> usize {
match &self.segment_requests {
@@ -232,6 +243,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
}
}
/// PDU header.
#[inline]
pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header
@@ -252,7 +264,7 @@ impl<'seg_reqs> NakPduCreator<'seg_reqs> {
.into());
}
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 = write_start_and_end_of_scope(
@@ -309,7 +321,7 @@ impl CfdpPdu for NakPduCreator<'_> {
#[inline]
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> {
/// Calculartes the maximum amount of segment requests which fit into a packet with the
/// provided parameters.
pub fn calculate_max_segment_requests(
max_packet_size: usize,
pdu_header: &PduHeader,
@@ -344,6 +358,7 @@ impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> {
calculate_max_segment_requests(max_packet_size, pdu_header)
}
/// Constructor.
pub fn new(
buf: &'buf mut [u8],
mut pdu_header: PduHeader,
@@ -371,11 +386,13 @@ impl<'buf> NakPduCreatorWithReservedSeqReqsBuf<'buf> {
}
impl NakPduCreatorWithReservedSeqReqsBuf<'_> {
/// PDU header.
#[inline]
pub fn pdu_header(&self) -> &PduHeader {
&self.pdu_header
}
/// Number of segment requests.
#[inline]
pub fn num_segment_reqs(&self) -> usize {
self.num_segment_reqs
@@ -407,6 +424,7 @@ impl NakPduCreatorWithReservedSeqReqsBuf<'_> {
Ok(())
}
/// Length when written to a buffer.
pub fn len_written(&self) -> usize {
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]
}
/// Iterator over all segment requests.
#[inline]
pub fn segment_request_iter(&self) -> SegmentRequestIter<'_> {
SegmentRequestIter::new(
@@ -447,7 +466,7 @@ impl NakPduCreatorWithReservedSeqReqsBuf<'_> {
) -> Result<usize, InvalidStartOrEndOfScopeError> {
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();
self.buf[current_idx] = FileDirectiveType::NakPdu as u8;
self.buf[current_idx] = FileDirectiveType::Nak as u8;
current_idx += 1;
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> {
Some(FileDirectiveType::NakPdu)
Some(FileDirectiveType::Nak)
}
}
impl<'seg_reqs> NakPduReader<'seg_reqs> {
/// Constructor from a raw bytestream.
pub fn new(buf: &'seg_reqs [u8]) -> Result<NakPduReader<'seg_reqs>, PduError> {
Self::from_bytes(buf)
}
/// Constructor from a raw bytestream.
pub fn from_bytes(buf: &'seg_reqs [u8]) -> Result<NakPduReader<'seg_reqs>, PduError> {
let (pdu_header, mut current_idx) = PduHeader::from_bytes(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(|_| {
PduError::InvalidDirectiveType {
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 {
found: directive_type,
expected: FileDirectiveType::AckPdu,
expected: FileDirectiveType::Ack,
});
}
current_idx += 1;
@@ -682,14 +703,17 @@ impl<'seg_reqs> NakPduReader<'seg_reqs> {
})
}
/// Start of scope.
pub fn start_of_scope(&self) -> u64 {
self.start_of_scope
}
/// End of scope.
pub fn end_of_scope(&self) -> u64 {
self.end_of_scope
}
/// Number of segment requests.
pub fn num_segment_reqs(&self) -> usize {
if self.seg_reqs_raw.is_empty() {
return 0;
@@ -747,10 +771,7 @@ mod tests {
assert_eq!(nak_pdu.crc_flag(), CrcFlag::NoCrc);
assert_eq!(nak_pdu.file_flag(), LargeFileFlag::Normal);
assert_eq!(nak_pdu.pdu_type(), PduType::FileDirective);
assert_eq!(
nak_pdu.file_directive_type(),
Some(FileDirectiveType::NakPdu),
);
assert_eq!(nak_pdu.file_directive_type(), Some(FileDirectiveType::Nak),);
assert_eq!(nak_pdu.transmission_mode(), TransmissionMode::Acknowledged);
assert_eq!(nak_pdu.direction(), Direction::TowardsSender);
assert_eq!(nak_pdu.source_id(), TEST_SRC_ID.into());
@@ -794,7 +815,7 @@ mod tests {
verify_raw_header(nak_pdu.pdu_header(), &buf);
let mut current_idx = nak_pdu.pdu_header().header_len();
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;
let start_of_scope =
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);
let mut current_idx = nak_pdu.pdu_header().header_len();
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;
let start_of_scope =
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);
let mut current_idx = pdu_header.header_len();
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;
let start_of_scope =
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);
let mut current_idx = pdu_header.header_len();
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;
let start_of_scope =
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);
let mut current_idx = pdu_header.header_len();
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;
let start_of_scope =
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);
let mut current_idx = pdu_header.header_len();
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;
let start_of_scope =
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);
let mut current_idx = pdu_header.header_len();
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;
let start_of_scope =
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);
let mut current_idx = pdu_header.header_len();
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;
let start_of_scope =
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.
pub const MIN_TLV_LEN: usize = 2;
/// Trait for generic TLV structures.
pub trait GenericTlv {
/// TLV type field.
fn tlv_type_field(&self) -> TlvTypeField;
@@ -109,21 +110,30 @@ pub enum TlvType {
EntityId = 0x06,
}
/// TLV type field variants.
///
/// This allows specifying custom variants as well.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
pub enum TlvTypeField {
/// Standard TLV types.
Standard(TlvType),
/// Custom TLV type.
Custom(u8),
}
/// Filestore action codes as specified in the standard.
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[repr(u8)]
pub enum FilestoreActionCode {
/// Create file.
CreateFile = 0b0000,
/// Delete file.
DeleteFile = 0b0001,
/// Rename file.
RenameFile = 0b0010,
/// 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
@@ -132,9 +142,13 @@ pub enum FilestoreActionCode {
/// This operation replaces the content of the first specified file with the content of
/// the secondly specified file.
ReplaceFile = 0b0100,
/// Create directory.
CreateDirectory = 0b0101,
/// Remove directory.
RemoveDirectory = 0b0110,
/// Deny file.
DenyFile = 0b0111,
/// Deny directory.
DenyDirectory = 0b1000,
}
@@ -175,8 +189,10 @@ pub struct 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;
/// Generic constructor for a TLV structure.
pub fn new(tlv_type: TlvType, data: &[u8]) -> Result<Tlv<'_>, TlvLvDataTooLargeError> {
Ok(Tlv {
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(
tlv_type: u8,
data: &[u8],
@@ -225,6 +242,7 @@ impl<'data> Tlv<'data> {
self.lv.raw_data()
}
/// Converts to an owned TLV variant, allocating memory for the value field.
#[cfg(feature = "alloc")]
pub fn to_owned(&self) -> TlvOwned {
TlvOwned {
@@ -268,6 +286,7 @@ impl GenericTlv for Tlv<'_> {
}
}
/// Component of the TLV module which require [alloc] support.
#[cfg(feature = "alloc")]
pub mod alloc_mod {
use super::*;
@@ -283,6 +302,7 @@ pub mod alloc_mod {
}
impl TlvOwned {
/// Generic constructor.
pub fn new(tlv_type: TlvType, data: &[u8]) -> Self {
Self {
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 {
Self {
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> {
generic_len_check_data_serialization(buf, self.data.len(), MIN_TLV_LEN)?;
buf[0] = self.tlv_type_field.into();
@@ -318,6 +340,7 @@ pub mod alloc_mod {
self.data.len() + 2
}
/// Convert to [Tlv]
pub fn as_tlv(&self) -> Tlv<'_> {
Tlv {
tlv_type_field: self.tlv_type_field,
@@ -366,6 +389,7 @@ pub mod alloc_mod {
}
}
/// Entity ID TLV.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -374,6 +398,7 @@ pub struct EntityIdTlv {
}
impl EntityIdTlv {
/// Constructor.
#[inline]
pub fn new(entity_id: UnsignedByteField) -> Self {
Self { entity_id }
@@ -389,21 +414,25 @@ impl EntityIdTlv {
Ok(())
}
/// Entity ID.
#[inline]
pub fn entity_id(&self) -> &UnsignedByteField {
&self.entity_id
}
/// Length of the value field.
#[inline]
pub fn len_value(&self) -> usize {
self.entity_id.size()
}
/// Full length of the TLV, including type and length fields.
#[inline]
pub fn len_full(&self) -> usize {
2 + self.entity_id.size()
}
/// Create from a raw bytestream.
pub fn from_bytes(buf: &[u8]) -> Result<Self, TlvLvError> {
Self::len_check(buf)?;
verify_tlv_type(buf[0], TlvType::EntityId)?;
@@ -495,6 +524,7 @@ impl TryFrom<Tlv<'_>> for EntityIdTlv {
}
}
/// Does the [FilestoreActionCode] have a second filename?
#[inline]
pub fn fs_request_has_second_filename(action_code: FilestoreActionCode) -> bool {
if action_code == FilestoreActionCode::RenameFile
@@ -528,6 +558,7 @@ impl FilestoreTlvBase<'_, '_> {
}
}
/// Filestore request TLV.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
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> {
/// Constructor for file creation.
pub fn new_create_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::CreateFile, file_name, None)
}
/// Constructor for file deletion.
pub fn new_delete_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::DeleteFile, file_name, None)
}
/// Constructor for file renaming.
pub fn new_rename_file(
source_name: Lv<'first_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> {
Self::new(FilestoreActionCode::CreateDirectory, dir_name, None)
}
/// Constructor for directory removal.
pub fn new_remove_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::RemoveDirectory, dir_name, None)
}
/// Constructor for file denial.
pub fn new_deny_file(file_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::DenyFile, file_name, None)
}
/// Constructor for directory denial.
pub fn new_deny_directory(dir_name: Lv<'first_name>) -> Result<Self, TlvLvError> {
Self::new(FilestoreActionCode::DenyDirectory, dir_name, None)
}
@@ -628,31 +666,37 @@ impl<'first_name, 'second_name> FilestoreRequestTlv<'first_name, 'second_name> {
})
}
/// Action code.
#[inline]
pub fn action_code(&self) -> FilestoreActionCode {
self.base.action_code
}
/// First name as [Lv].
#[inline]
pub fn first_name(&self) -> Lv<'first_name> {
self.base.first_name
}
/// First name as optional [Lv].
#[inline]
pub fn second_name(&self) -> Option<Lv<'second_name>> {
self.base.second_name
}
/// Length of the value field.
#[inline]
pub fn len_value(&self) -> usize {
self.base.base_len_value()
}
/// Full TLV length.
#[inline]
pub fn len_full(&self) -> usize {
2 + self.len_value()
}
/// Construct from a raw bytestream.
pub fn from_bytes<'longest: 'first_name + 'second_name>(
buf: &'longest [u8],
) -> Result<Self, TlvLvError> {
@@ -737,6 +781,7 @@ impl GenericTlv for FilestoreRequestTlv<'_, '_> {
}
}
/// Filestore response TLV.
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[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(),
)
}
/// Generic constructor.
pub fn new(
action_code: FilestoreActionCode,
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 {
if action_code == FilestoreActionCode::RenameFile
|| action_code == FilestoreActionCode::AppendFile
@@ -806,36 +854,43 @@ impl<'first_name, 'second_name, 'fs_msg> FilestoreResponseTlv<'first_name, 'seco
false
}
/// Action code.
#[inline]
pub fn action_code(&self) -> FilestoreActionCode {
self.base.action_code
}
/// Status code.
#[inline]
pub fn status_code(&self) -> u8 {
self.status_code
}
/// First name as [Lv].
#[inline]
pub fn first_name(&self) -> Lv<'first_name> {
self.base.first_name
}
/// Optional second name as [Lv].
#[inline]
pub fn second_name(&self) -> Option<Lv<'second_name>> {
self.base.second_name
}
/// Length of the value field.
#[inline]
pub fn len_value(&self) -> usize {
self.base.base_len_value() + self.filestore_message.len_full()
}
/// Full length of the TLV.
#[inline]
pub fn len_full(&self) -> usize {
2 + self.len_value()
}
/// Construct from a raw bytestream.
pub fn from_bytes<'buf: 'first_name + 'second_name + 'fs_msg>(
buf: &'buf [u8],
) -> Result<Self, TlvLvError> {

View File

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