//! Low-level CCSDS File Delivery Protocol (CFDP) support according to [CCSDS 727.0-B-5](https://public.ccsds.org/Pubs/727x0b5.pdf). use crate::ByteConversionError; use num_enum::{IntoPrimitive, TryFromPrimitive}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; pub mod lv; pub mod pdu; pub mod tlv; /// This is the name of the standard this module is based on. pub const CFDP_VERSION_2_NAME: &str = "CCSDS 727.0-B-5"; /// Currently, only this version is supported. pub const CFDP_VERSION_2: u8 = 0b001; /// PDU type. #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[bitbybit::bitenum(u1, exhaustive = true)] #[repr(u8)] pub enum PduType { /// File directive PDU. FileDirective = 0, /// File data PDU. FileData = 1, } /// PDU direction. #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[bitbybit::bitenum(u1, exhaustive = true)] #[repr(u8)] pub enum Direction { /// Going towards the file receiver. TowardsReceiver = 0, /// Going towards the file sender. TowardsSender = 1, } /// PDU transmission mode. #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[bitbybit::bitenum(u1, exhaustive = true)] #[repr(u8)] pub enum TransmissionMode { /// Acknowledged (class 1) transfer. Acknowledged = 0, /// Unacknowledged (class 2) transfer. Unacknowledged = 1, } /// CRC flag. #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[bitbybit::bitenum(u1, exhaustive = true)] #[repr(u8)] pub enum CrcFlag { /// No CRC for the packet. NoCrc = 0, /// Packet has CRC. WithCrc = 1, } impl From for CrcFlag { fn from(value: bool) -> Self { if value { return CrcFlag::WithCrc; } CrcFlag::NoCrc } } impl From for bool { fn from(value: CrcFlag) -> Self { if value == CrcFlag::WithCrc { return true; } false } } /// Always 0 and ignored for File Directive PDUs (CCSDS 727.0-B-5 P.75) #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[bitbybit::bitenum(u1, exhaustive = true)] #[repr(u8)] pub enum SegmentMetadataFlag { /// Segment metadata not present. NotPresent = 0, /// Segment metadata present. Present = 1, } /// Always 0 and ignored for File Directive PDUs (CCSDS 727.0-B-5 P.75) #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[bitbybit::bitenum(u1, exhaustive = true)] #[repr(u8)] pub enum SegmentationControl { /// No record boundary preservation. NoRecordBoundaryPreservation = 0, /// With record boundary preservation. WithRecordBoundaryPreservation = 1, } /// Fault handler codes according to the CFDP standard. #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[bitbybit::bitenum(u3, exhaustive = false)] #[repr(u8)] pub enum FaultHandlerCode { /// Notice of cancellation fault handler code. NoticeOfCancellation = 0b0001, /// Notice of suspension fault handler code. NoticeOfSuspension = 0b0010, /// Ignore error fault handler code. IgnoreError = 0b0011, /// Abandon transaction fault handler code. AbandonTransaction = 0b0100, } /// CFDP condition codes. #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[bitbybit::bitenum(u4, exhaustive = false)] #[repr(u8)] pub enum ConditionCode { /// This is not an error condition for which a faulty handler override can be specified NoError = 0b0000, /// Positive acknowledgement limit reached. PositiveAckLimitReached = 0b0001, /// Keep-alive limit reached. KeepAliveLimitReached = 0b0010, /// Invalid transmission mode. InvalidTransmissionMode = 0b0011, /// Filestore rejection. FilestoreRejection = 0b0100, /// File checksum error. FileChecksumFailure = 0b0101, /// File size error. FileSizeError = 0b0110, /// NAK limit reached. NakLimitReached = 0b0111, /// Inactivity detected. InactivityDetected = 0b1000, /// Check limit reached. CheckLimitReached = 0b1010, /// Unsupported checksum type. UnsupportedChecksumType = 0b1011, /// Not an actual fault condition for which fault handler overrides can be specified SuspendRequestReceived = 0b1110, /// Not an actual fault condition for which fault handler overrides can be specified CancelRequestReceived = 0b1111, } /// Large file flag. #[derive(Debug, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[bitbybit::bitenum(u1, exhaustive = true)] #[repr(u8)] pub enum LargeFileFlag { /// 32 bit maximum file size and FSS size Normal = 0, /// 64 bit maximum file size and FSS size Large = 1, } /// Transaction status for the ACK PDU field according to chapter 5.2.4 of the CFDP standard. #[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 TransactionStatus { /// Transaction is not currently active and the CFDP implementation does not retain a /// transaction history. Undefined = 0b00, /// Transaction is currently active. Active = 0b01, /// Transaction was active in the past and was terminated. Terminated = 0b10, /// The CFDP implementation does retain a tranaction history, and the transaction is not and /// never was active at this entity. Unrecognized = 0b11, } /// Checksum types according to the /// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/) #[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 ChecksumType { /// Modular legacy checksum Modular = 0, /// CRC32 Proximity-1. Crc32Proximity1 = 1, /// CRC32C. Crc32C = 2, /// CRC32. Polynomial: 0x4C11DB7. Preferred checksum for now. Crc32 = 3, /// Null checksum (no checksum). NullChecksum = 15, } impl Default for ChecksumType { fn default() -> Self { Self::NullChecksum } } /// Raw null checksum. pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4]; /// TLV or LV data larger than allowed [u8::MAX]. #[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[error("data with size {0} larger than allowed {max} bytes", max = u8::MAX)] pub struct TlvLvDataTooLargeError(pub usize); /// First value: Found value. Second value: Expected value if there is one. #[derive(Debug, Copy, Clone, PartialEq, Eq, thiserror::Error)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[cfg_attr(feature = "defmt", derive(defmt::Format))] #[error("invalid TLV type field, found {found}, expected {expected:?}")] pub struct InvalidTlvTypeFieldError { found: u8, expected: Option, } /// Generic TLV/LV 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 TlvLvError { /// Data too large error. #[error("{0}")] DataTooLarge(#[from] TlvLvDataTooLargeError), /// Byte conversion error. #[error("byte conversion error: {0}")] ByteConversion(#[from] ByteConversionError), /// Invalid TLV type field error. #[error("{0}")] InvalidTlvTypeField(#[from] InvalidTlvTypeFieldError), /// Invalid value length. #[error("invalid value length {0}")] InvalidValueLength(usize), /// Only applies to filestore requests and responses. Second name was missing where one is /// expected. #[error("second name missing for filestore request or response")] SecondNameMissing, /// Invalid action code for filestore requests or responses. #[error("invalid action code {0}")] InvalidFilestoreActionCode(u8), } #[cfg(test)] mod tests { use super::*; #[cfg(feature = "serde")] use crate::tests::generic_serde_test; #[test] fn test_crc_from_bool() { assert_eq!(CrcFlag::from(false), CrcFlag::NoCrc); } #[test] fn test_crc_flag_to_bool() { let is_true: bool = CrcFlag::WithCrc.into(); assert!(is_true); let is_false: bool = CrcFlag::NoCrc.into(); assert!(!is_false); } #[test] fn test_default_checksum_type() { let checksum = ChecksumType::default(); assert_eq!(checksum, ChecksumType::NullChecksum); } #[test] fn test_fault_handler_code_from_u8() { let fault_handler_code_raw = FaultHandlerCode::NoticeOfSuspension as u8; let fault_handler_code = FaultHandlerCode::try_from(fault_handler_code_raw).unwrap(); assert_eq!(fault_handler_code, FaultHandlerCode::NoticeOfSuspension); } #[test] #[cfg(feature = "serde")] fn test_serde_impl_pdu_type() { generic_serde_test(PduType::FileData); } #[test] #[cfg(feature = "serde")] fn test_serde_impl_direction() { generic_serde_test(Direction::TowardsReceiver); } #[test] #[cfg(feature = "serde")] fn test_serde_impl_transmission_mode() { generic_serde_test(TransmissionMode::Unacknowledged); } #[test] #[cfg(feature = "serde")] fn test_serde_fault_handler_code() { generic_serde_test(FaultHandlerCode::NoticeOfCancellation); } }