1
use crate::cfdp::pdu::{
2
    add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field,
3
    FileDirectiveType, PduError, PduHeader,
4
};
5
use crate::cfdp::tlv::{EntityIdTlv, WritableTlv};
6
use crate::cfdp::{ConditionCode, CrcFlag, Direction, LargeFileFlag};
7
use crate::ByteConversionError;
8
#[cfg(feature = "serde")]
9
use serde::{Deserialize, Serialize};
10

            
11
use super::{CfdpPdu, WritablePduPacket};
12

            
13
/// Finished PDU abstraction.
14
///
15
/// For more information, refer to CFDP chapter 5.2.2.
16
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
17
2
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
19
pub struct EofPdu {
20
    pdu_header: PduHeader,
21
    condition_code: ConditionCode,
22
    file_checksum: u32,
23
    file_size: u64,
24
    fault_location: Option<EntityIdTlv>,
25
}
26

            
27
impl EofPdu {
28
18
    pub fn new(
29
18
        mut pdu_header: PduHeader,
30
18
        condition_code: ConditionCode,
31
18
        file_checksum: u32,
32
18
        file_size: u64,
33
18
        fault_location: Option<EntityIdTlv>,
34
18
    ) -> Self {
35
18
        // Force correct direction flag.
36
18
        pdu_header.pdu_conf.direction = Direction::TowardsReceiver;
37
18
        let mut eof_pdu = Self {
38
18
            pdu_header,
39
18
            condition_code,
40
18
            file_checksum,
41
18
            file_size,
42
18
            fault_location,
43
18
        };
44
18
        eof_pdu.pdu_header.pdu_datafield_len = eof_pdu.calc_pdu_datafield_len() as u16;
45
18
        eof_pdu
46
18
    }
47

            
48
14
    pub fn new_no_error(pdu_header: PduHeader, file_checksum: u32, file_size: u64) -> Self {
49
14
        Self::new(
50
14
            pdu_header,
51
14
            ConditionCode::NoError,
52
14
            file_checksum,
53
14
            file_size,
54
14
            None,
55
14
        )
56
14
    }
57

            
58
4
    pub fn pdu_header(&self) -> &PduHeader {
59
4
        &self.pdu_header
60
4
    }
61

            
62
8
    pub fn condition_code(&self) -> ConditionCode {
63
8
        self.condition_code
64
8
    }
65

            
66
8
    pub fn file_checksum(&self) -> u32 {
67
8
        self.file_checksum
68
8
    }
69

            
70
8
    pub fn file_size(&self) -> u64 {
71
8
        self.file_size
72
8
    }
73

            
74
50
    fn calc_pdu_datafield_len(&self) -> usize {
75
50
        // One directive type octet, 4 bits condition code, 4 spare bits.
76
50
        let mut len = 2 + core::mem::size_of::<u32>() + 4;
77
50
        if self.pdu_header.pdu_conf.file_flag == LargeFileFlag::Large {
78
4
            len += 4;
79
46
        }
80
50
        if let Some(fault_location) = self.fault_location {
81
16
            len += fault_location.len_full();
82
34
        }
83
50
        if self.crc_flag() == CrcFlag::WithCrc {
84
14
            len += 2;
85
36
        }
86
50
        len
87
50
    }
88

            
89
10
    pub fn from_bytes(buf: &[u8]) -> Result<EofPdu, PduError> {
90
10
        let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
91
10
        let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
92
8
        let is_large_file = pdu_header.pdu_conf.file_flag == LargeFileFlag::Large;
93
8
        let mut min_expected_len = 2 + 4 + 4;
94
8
        if is_large_file {
95
            min_expected_len += 4;
96
8
        }
97
8
        generic_length_checks_pdu_deserialization(buf, min_expected_len, full_len_without_crc)?;
98
8
        let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
99
            PduError::InvalidDirectiveType {
100
                found: buf[current_idx],
101
                expected: Some(FileDirectiveType::EofPdu),
102
            }
103
8
        })?;
104
8
        if directive_type != FileDirectiveType::EofPdu {
105
            return Err(PduError::WrongDirectiveType {
106
                found: directive_type,
107
                expected: FileDirectiveType::EofPdu,
108
            });
109
8
        }
110
8
        current_idx += 1;
111
8
        let condition_code = ConditionCode::try_from((buf[current_idx] >> 4) & 0b1111)
112
8
            .map_err(|_| PduError::InvalidConditionCode((buf[current_idx] >> 4) & 0b1111))?;
113
8
        current_idx += 1;
114
8
        let file_checksum =
115
8
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
116
8
        current_idx += 4;
117
8
        let (fss_field_len, file_size) =
118
8
            read_fss_field(pdu_header.pdu_conf.file_flag, &buf[current_idx..]);
119
8
        current_idx += fss_field_len;
120
8
        let mut fault_location = None;
121
8
        if condition_code != ConditionCode::NoError && current_idx < full_len_without_crc {
122
4
            fault_location = Some(EntityIdTlv::from_bytes(&buf[current_idx..])?);
123
4
        }
124
8
        Ok(Self {
125
8
            pdu_header,
126
8
            condition_code,
127
8
            file_checksum,
128
8
            file_size,
129
8
            fault_location,
130
8
        })
131
10
    }
132
}
133

            
134
impl CfdpPdu for EofPdu {
135
128
    fn pdu_header(&self) -> &PduHeader {
136
128
        &self.pdu_header
137
128
    }
138

            
139
8
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
140
8
        Some(FileDirectiveType::EofPdu)
141
8
    }
142
}
143

            
144
impl WritablePduPacket for EofPdu {
145
14
    fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
146
14
        let expected_len = self.len_written();
147
14
        if buf.len() < expected_len {
148
            return Err(ByteConversionError::ToSliceTooSmall {
149
                found: buf.len(),
150
                expected: expected_len,
151
            }
152
            .into());
153
14
        }
154
14
        let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
155
14
        buf[current_idx] = FileDirectiveType::EofPdu as u8;
156
14
        current_idx += 1;
157
14
        buf[current_idx] = (self.condition_code as u8) << 4;
158
14
        current_idx += 1;
159
14
        buf[current_idx..current_idx + 4].copy_from_slice(&self.file_checksum.to_be_bytes());
160
14
        current_idx += 4;
161
14
        current_idx += write_fss_field(
162
14
            self.pdu_header.pdu_conf.file_flag,
163
14
            self.file_size,
164
14
            &mut buf[current_idx..],
165
14
        )?;
166
14
        if let Some(fault_location) = self.fault_location {
167
4
            current_idx += fault_location.write_to_bytes(&mut buf[current_idx..])?;
168
10
        }
169
14
        if self.crc_flag() == CrcFlag::WithCrc {
170
4
            current_idx = add_pdu_crc(buf, current_idx);
171
10
        }
172
14
        Ok(current_idx)
173
14
    }
174

            
175
32
    fn len_written(&self) -> usize {
176
32
        self.pdu_header.header_len() + self.calc_pdu_datafield_len()
177
32
    }
178
}
179

            
180
#[cfg(test)]
181
mod tests {
182
    use super::*;
183
    use crate::cfdp::pdu::tests::{
184
        common_pdu_conf, verify_raw_header, TEST_DEST_ID, TEST_SEQ_NUM, TEST_SRC_ID,
185
    };
186
    use crate::cfdp::pdu::{FileDirectiveType, PduHeader};
187
    use crate::cfdp::{ConditionCode, CrcFlag, LargeFileFlag, PduType, TransmissionMode};
188
    #[cfg(feature = "serde")]
189
    use crate::tests::generic_serde_test;
190
    use crate::util::{UnsignedByteFieldU16, UnsignedEnum};
191

            
192
4
    fn verify_state_no_error_no_crc(eof_pdu: &EofPdu, file_flag: LargeFileFlag) {
193
4
        verify_state(eof_pdu, CrcFlag::NoCrc, file_flag, ConditionCode::NoError);
194
4
    }
195

            
196
8
    fn verify_state(
197
8
        eof_pdu: &EofPdu,
198
8
        crc_flag: CrcFlag,
199
8
        file_flag: LargeFileFlag,
200
8
        cond_code: ConditionCode,
201
8
    ) {
202
8
        assert_eq!(eof_pdu.file_checksum(), 0x01020304);
203
8
        assert_eq!(eof_pdu.file_size(), 12);
204
8
        assert_eq!(eof_pdu.condition_code(), cond_code);
205

            
206
8
        assert_eq!(eof_pdu.crc_flag(), crc_flag);
207
8
        assert_eq!(eof_pdu.file_flag(), file_flag);
208
8
        assert_eq!(eof_pdu.pdu_type(), PduType::FileDirective);
209
8
        assert_eq!(
210
8
            eof_pdu.file_directive_type(),
211
8
            Some(FileDirectiveType::EofPdu)
212
8
        );
213
8
        assert_eq!(eof_pdu.transmission_mode(), TransmissionMode::Acknowledged);
214
8
        assert_eq!(eof_pdu.direction(), Direction::TowardsReceiver);
215
8
        assert_eq!(eof_pdu.source_id(), TEST_SRC_ID.into());
216
8
        assert_eq!(eof_pdu.dest_id(), TEST_DEST_ID.into());
217
8
        assert_eq!(eof_pdu.transaction_seq_num(), TEST_SEQ_NUM.into());
218
8
    }
219

            
220
    #[test]
221
2
    fn test_basic() {
222
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
223
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
224
2
        let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
225
2
        assert_eq!(eof_pdu.len_written(), pdu_header.header_len() + 2 + 4 + 4);
226
2
        verify_state_no_error_no_crc(&eof_pdu, LargeFileFlag::Normal);
227
2
    }
228

            
229
    #[test]
230
2
    fn test_serialization() {
231
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
232
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
233
2
        let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
234
2
        let mut buf: [u8; 64] = [0; 64];
235
2
        let res = eof_pdu.write_to_bytes(&mut buf);
236
2
        assert!(res.is_ok());
237
2
        let written = res.unwrap();
238
2
        assert_eq!(written, eof_pdu.len_written());
239
2
        verify_raw_header(eof_pdu.pdu_header(), &buf);
240
2
        let mut current_idx = eof_pdu.pdu_header().header_len();
241
2
        buf[current_idx] = FileDirectiveType::EofPdu as u8;
242
2
        current_idx += 1;
243
2
        assert_eq!(
244
2
            (buf[current_idx] >> 4) & 0b1111,
245
2
            ConditionCode::NoError as u8
246
2
        );
247
2
        current_idx += 1;
248
2
        assert_eq!(
249
2
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()),
250
2
            0x01020304
251
2
        );
252
2
        current_idx += 4;
253
2
        assert_eq!(
254
2
            u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()),
255
2
            12
256
2
        );
257
2
        current_idx += 4;
258
2
        assert_eq!(current_idx, written);
259
2
    }
260

            
261
    #[test]
262
2
    fn test_deserialization() {
263
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
264
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
265
2
        let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
266
2
        let mut buf: [u8; 64] = [0; 64];
267
2
        eof_pdu.write_to_bytes(&mut buf).unwrap();
268
2
        let eof_read_back = EofPdu::from_bytes(&buf);
269
2
        if let Err(e) = eof_read_back {
270
            panic!("deserialization failed with: {e}")
271
2
        }
272
2
        let eof_read_back = eof_read_back.unwrap();
273
2
        assert_eq!(eof_read_back, eof_pdu);
274
2
    }
275

            
276
    #[test]
277
2
    fn test_write_to_vec() {
278
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
279
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
280
2
        let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
281
2
        let mut buf: [u8; 64] = [0; 64];
282
2
        let written = eof_pdu.write_to_bytes(&mut buf).unwrap();
283
2
        let pdu_vec = eof_pdu.to_vec().unwrap();
284
2
        assert_eq!(buf[0..written], pdu_vec);
285
2
    }
286

            
287
    #[test]
288
2
    fn test_with_crc() {
289
2
        let pdu_conf = common_pdu_conf(CrcFlag::WithCrc, LargeFileFlag::Normal);
290
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
291
2
        let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
292
2
        let mut buf: [u8; 64] = [0; 64];
293
2
        let written = eof_pdu.write_to_bytes(&mut buf).unwrap();
294
2
        assert_eq!(written, eof_pdu.len_written());
295
2
        let eof_from_raw = EofPdu::from_bytes(&buf).expect("creating EOF PDU failed");
296
2
        assert_eq!(eof_from_raw, eof_pdu);
297
2
        buf[written - 1] -= 1;
298
2
        let crc: u16 = ((buf[written - 2] as u16) << 8) as u16 | buf[written - 1] as u16;
299
2
        let error = EofPdu::from_bytes(&buf).unwrap_err();
300
2
        if let PduError::ChecksumError(e) = error {
301
2
            assert_eq!(e, crc);
302
        } else {
303
            panic!("expected crc error");
304
        }
305
2
    }
306

            
307
    #[test]
308
2
    fn test_with_large_file_flag() {
309
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Large);
310
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
311
2
        let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
312
2
        verify_state_no_error_no_crc(&eof_pdu, LargeFileFlag::Large);
313
2
        assert_eq!(eof_pdu.len_written(), pdu_header.header_len() + 2 + 8 + 4);
314
2
    }
315

            
316
    #[test]
317
    #[cfg(feature = "serde")]
318
2
    fn test_eof_serde() {
319
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
320
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
321
2
        let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
322
2
        generic_serde_test(eof_pdu);
323
2
    }
324

            
325
4
    fn generic_test_with_fault_location_and_error(crc: CrcFlag) {
326
4
        let pdu_conf = common_pdu_conf(crc, LargeFileFlag::Normal);
327
4
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
328
4
        let eof_pdu = EofPdu::new(
329
4
            pdu_header,
330
4
            ConditionCode::FileChecksumFailure,
331
4
            0x01020304,
332
4
            12,
333
4
            Some(EntityIdTlv::new(UnsignedByteFieldU16::new(5).into())),
334
4
        );
335
4
        let mut expected_len = pdu_header.header_len() + 2 + 4 + 4 + 4;
336
4
        if crc == CrcFlag::WithCrc {
337
2
            expected_len += 2;
338
2
        }
339
        // Entity ID TLV increaes length by 4.
340
4
        assert_eq!(eof_pdu.len_written(), expected_len);
341
4
        verify_state(
342
4
            &eof_pdu,
343
4
            crc,
344
4
            LargeFileFlag::Normal,
345
4
            ConditionCode::FileChecksumFailure,
346
4
        );
347
4
        let eof_vec = eof_pdu.to_vec().unwrap();
348
4
        let eof_read_back = EofPdu::from_bytes(&eof_vec);
349
4
        if let Err(e) = eof_read_back {
350
            panic!("deserialization failed with: {e}")
351
4
        }
352
4
        let eof_read_back = eof_read_back.unwrap();
353
4
        assert_eq!(eof_read_back, eof_pdu);
354
4
        assert!(eof_read_back.fault_location.is_some());
355
4
        assert_eq!(eof_read_back.fault_location.unwrap().entity_id().value(), 5);
356
4
        assert_eq!(eof_read_back.fault_location.unwrap().entity_id().size(), 2);
357
4
    }
358

            
359
    #[test]
360
2
    fn test_with_fault_location_and_error() {
361
2
        generic_test_with_fault_location_and_error(CrcFlag::NoCrc);
362
2
    }
363

            
364
    #[test]
365
2
    fn test_with_fault_location_and_error_and_crc() {
366
2
        generic_test_with_fault_location_and_error(CrcFlag::WithCrc);
367
2
    }
368
}