1
//! Low-level CCSDS File Delivery Protocol (CFDP) support according to [CCSDS 727.0-B-5](https://public.ccsds.org/Pubs/727x0b5.pdf).
2
use crate::ByteConversionError;
3
use core::fmt::{Display, Formatter};
4
use num_enum::{IntoPrimitive, TryFromPrimitive};
5
#[cfg(feature = "serde")]
6
use serde::{Deserialize, Serialize};
7
#[cfg(feature = "std")]
8
use std::error::Error;
9

            
10
pub mod lv;
11
pub mod pdu;
12
pub mod tlv;
13

            
14
/// This is the name of the standard this module is based on.
15
pub const CFDP_VERSION_2_NAME: &str = "CCSDS 727.0-B-5";
16
/// Currently, only this version is supported.
17
pub const CFDP_VERSION_2: u8 = 0b001;
18

            
19
74
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
20
20
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22
#[repr(u8)]
23
pub enum PduType {
24
    FileDirective = 0,
25
    FileData = 1,
26
}
27

            
28
74
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
29
20
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
30
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
31
#[repr(u8)]
32
pub enum Direction {
33
    TowardsReceiver = 0,
34
    TowardsSender = 1,
35
}
36

            
37
74
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
38
20
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
39
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
40
#[repr(u8)]
41
pub enum TransmissionMode {
42
    Acknowledged = 0,
43
    Unacknowledged = 1,
44
}
45

            
46
74
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
47
16
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
49
#[repr(u8)]
50
pub enum CrcFlag {
51
    NoCrc = 0,
52
    WithCrc = 1,
53
}
54

            
55
impl From<bool> for CrcFlag {
56
8
    fn from(value: bool) -> Self {
57
8
        if value {
58
6
            return CrcFlag::WithCrc;
59
2
        }
60
2
        CrcFlag::NoCrc
61
8
    }
62
}
63

            
64
impl From<CrcFlag> for bool {
65
4
    fn from(value: CrcFlag) -> Self {
66
4
        if value == CrcFlag::WithCrc {
67
2
            return true;
68
2
        }
69
2
        false
70
4
    }
71
}
72

            
73
/// Always 0 and ignored for File Directive PDUs (CCSDS 727.0-B-5 P.75)
74
72
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
75
16
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
76
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
77
#[repr(u8)]
78
pub enum SegmentMetadataFlag {
79
    NotPresent = 0,
80
    Present = 1,
81
}
82

            
83
/// Always 0 and ignored for File Directive PDUs (CCSDS 727.0-B-5 P.75)
84
74
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
85
16
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
86
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
87
#[repr(u8)]
88
pub enum SegmentationControl {
89
    NoRecordBoundaryPreservation = 0,
90
    WithRecordBoundaryPreservation = 1,
91
}
92

            
93
2
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
94
4
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
95
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
96
#[repr(u8)]
97
pub enum FaultHandlerCode {
98
    NoticeOfCancellation = 0b0001,
99
    NoticeOfSuspension = 0b0010,
100
    IgnoreError = 0b0011,
101
    AbandonTransaction = 0b0100,
102
}
103

            
104
22
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
105
8
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
106
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
107
#[repr(u8)]
108
pub enum ConditionCode {
109
    /// This is not an error condition for which a faulty handler override can be specified
110
    NoError = 0b0000,
111
    PositiveAckLimitReached = 0b0001,
112
    KeepAliveLimitReached = 0b0010,
113
    InvalidTransmissionMode = 0b0011,
114
    FilestoreRejection = 0b0100,
115
    FileChecksumFailure = 0b0101,
116
    FileSizeError = 0b0110,
117
    NakLimitReached = 0b0111,
118
    InactivityDetected = 0b1000,
119
    CheckLimitReached = 0b1010,
120
    UnsupportedChecksumType = 0b1011,
121
    /// Not an actual fault condition for which fault handler overrides can be specified
122
    SuspendRequestReceived = 0b1110,
123
    /// Not an actual fault condition for which fault handler overrides can be specified
124
    CancelRequestReceived = 0b1111,
125
}
126

            
127
74
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
128
16
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
129
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
130
#[repr(u8)]
131
pub enum LargeFileFlag {
132
    /// 32 bit maximum file size and FSS size
133
    Normal = 0,
134
    /// 64 bit maximum file size and FSS size
135
    Large = 1,
136
}
137

            
138
/// Transaction status for the ACK PDU field according to chapter 5.2.4 of the CFDP standard.
139
4
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
140
4
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
141
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
142
#[repr(u8)]
143
pub enum TransactionStatus {
144
    /// Transaction is not currently active and the CFDP implementation does not retain a
145
    /// transaction history.
146
    Undefined = 0b00,
147
    Active = 0b01,
148
    /// Transaction was active in the past and was terminated.
149
    Terminated = 0b10,
150
    /// The CFDP implementation does retain a tranaction history, and the transaction is not and
151
    /// never was active at this entity.
152
    Unrecognized = 0b11,
153
}
154

            
155
/// Checksum types according to the
156
/// [SANA Checksum Types registry](https://sanaregistry.org/r/checksum_identifiers/)
157
10
#[derive(Debug, Copy, Clone, PartialEq, Eq, TryFromPrimitive, IntoPrimitive)]
158
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
159
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
160
#[repr(u8)]
161
pub enum ChecksumType {
162
    /// Modular legacy checksum
163
    Modular = 0,
164
    Crc32Proximity1 = 1,
165
    Crc32C = 2,
166
    /// Polynomial: 0x4C11DB7. Preferred checksum for now.
167
    Crc32 = 3,
168
    NullChecksum = 15,
169
}
170

            
171
impl Default for ChecksumType {
172
2
    fn default() -> Self {
173
2
        Self::NullChecksum
174
2
    }
175
}
176

            
177
pub const NULL_CHECKSUM_U32: [u8; 4] = [0; 4];
178

            
179
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
180
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
181
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
182
pub struct TlvLvDataTooLarge(pub usize);
183

            
184
impl Display for TlvLvDataTooLarge {
185
4
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
186
4
        write!(
187
4
            f,
188
4
            "data with size {} larger than allowed {} bytes",
189
4
            self.0,
190
4
            u8::MAX
191
4
        )
192
4
    }
193
}
194

            
195
#[cfg(feature = "std")]
196
impl Error for TlvLvDataTooLarge {}
197

            
198
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
199
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
200
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
201
pub enum TlvLvError {
202
    DataTooLarge(TlvLvDataTooLarge),
203
    ByteConversion(ByteConversionError),
204
    /// First value: Found value. Second value: Expected value if there is one.
205
    InvalidTlvTypeField {
206
        found: u8,
207
        expected: Option<u8>,
208
    },
209
    /// Logically invalid value length detected. The value length may not exceed 255 bytes.
210
    /// Depending on the concrete TLV type, the value length may also be logically invalid.
211
    InvalidValueLength(usize),
212
    /// Only applies to filestore requests and responses. Second name was missing where one is
213
    /// expected.
214
    SecondNameMissing,
215
    /// Invalid action code for filestore requests or responses.
216
    InvalidFilestoreActionCode(u8),
217
}
218

            
219
impl From<TlvLvDataTooLarge> for TlvLvError {
220
    fn from(value: TlvLvDataTooLarge) -> Self {
221
        Self::DataTooLarge(value)
222
    }
223
}
224

            
225
impl From<ByteConversionError> for TlvLvError {
226
2
    fn from(value: ByteConversionError) -> Self {
227
2
        Self::ByteConversion(value)
228
2
    }
229
}
230

            
231
impl Display for TlvLvError {
232
4
    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
233
4
        match self {
234
            TlvLvError::DataTooLarge(e) => {
235
                write!(f, "{}", e)
236
            }
237
            TlvLvError::ByteConversion(e) => {
238
                write!(f, "tlv or lv byte conversion: {}", e)
239
            }
240
2
            TlvLvError::InvalidTlvTypeField { found, expected } => {
241
2
                write!(
242
2
                    f,
243
2
                    "invalid TLV type field, found {found}, expected {expected:?}"
244
2
                )
245
            }
246
2
            TlvLvError::InvalidValueLength(len) => {
247
2
                write!(f, "invalid value length {len}")
248
            }
249
            TlvLvError::SecondNameMissing => {
250
                write!(f, "second name missing for filestore request or response")
251
            }
252
            TlvLvError::InvalidFilestoreActionCode(raw) => {
253
                write!(f, "invalid filestore action code with raw value {raw}")
254
            }
255
        }
256
4
    }
257
}
258

            
259
#[cfg(feature = "std")]
260
impl Error for TlvLvError {
261
    fn source(&self) -> Option<&(dyn Error + 'static)> {
262
        match self {
263
            TlvLvError::DataTooLarge(e) => Some(e),
264
            TlvLvError::ByteConversion(e) => Some(e),
265
            _ => None,
266
        }
267
    }
268
}
269

            
270
#[cfg(test)]
271
mod tests {
272
    use super::*;
273
    #[cfg(feature = "serde")]
274
    use crate::tests::generic_serde_test;
275

            
276
    #[test]
277
2
    fn test_crc_from_bool() {
278
2
        assert_eq!(CrcFlag::from(false), CrcFlag::NoCrc);
279
2
    }
280

            
281
    #[test]
282
2
    fn test_crc_flag_to_bool() {
283
2
        let is_true: bool = CrcFlag::WithCrc.into();
284
2
        assert!(is_true);
285
2
        let is_false: bool = CrcFlag::NoCrc.into();
286
2
        assert!(!is_false);
287
2
    }
288

            
289
    #[test]
290
2
    fn test_default_checksum_type() {
291
2
        let checksum = ChecksumType::default();
292
2
        assert_eq!(checksum, ChecksumType::NullChecksum);
293
2
    }
294

            
295
    #[test]
296
2
    fn test_fault_handler_code_from_u8() {
297
2
        let fault_handler_code_raw = FaultHandlerCode::NoticeOfSuspension as u8;
298
2
        let fault_handler_code = FaultHandlerCode::try_from(fault_handler_code_raw).unwrap();
299
2
        assert_eq!(fault_handler_code, FaultHandlerCode::NoticeOfSuspension);
300
2
    }
301

            
302
    #[test]
303
    #[cfg(feature = "serde")]
304
2
    fn test_serde_impl_pdu_type() {
305
2
        generic_serde_test(PduType::FileData);
306
2
    }
307

            
308
    #[test]
309
    #[cfg(feature = "serde")]
310
2
    fn test_serde_impl_direction() {
311
2
        generic_serde_test(Direction::TowardsReceiver);
312
2
    }
313

            
314
    #[test]
315
    #[cfg(feature = "serde")]
316
2
    fn test_serde_impl_transmission_mode() {
317
2
        generic_serde_test(TransmissionMode::Unacknowledged);
318
2
    }
319

            
320
    #[test]
321
    #[cfg(feature = "serde")]
322
2
    fn test_serde_fault_handler_code() {
323
2
        generic_serde_test(FaultHandlerCode::NoticeOfCancellation);
324
2
    }
325
}