1use crate::cfdp::pdu::{
3 add_pdu_crc, generic_length_checks_pdu_deserialization, read_fss_field, write_fss_field,
4 FileDirectiveType, PduError, PduHeader,
5};
6use crate::cfdp::tlv::{EntityIdTlv, WritableTlv};
7use crate::cfdp::{ConditionCode, CrcFlag, Direction, LargeFileFlag};
8use crate::ByteConversionError;
9#[cfg(feature = "serde")]
10use serde::{Deserialize, Serialize};
11
12use super::{CfdpPdu, WritablePduPacket};
13
14#[derive(Debug, Copy, Clone, PartialEq, Eq)]
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
20pub struct EofPdu {
21 pdu_header: PduHeader,
22 condition_code: ConditionCode,
23 file_checksum: u32,
24 file_size: u64,
25 fault_location: Option<EntityIdTlv>,
26}
27
28impl EofPdu {
29 pub fn new(
31 mut pdu_header: PduHeader,
32 condition_code: ConditionCode,
33 file_checksum: u32,
34 file_size: u64,
35 fault_location: Option<EntityIdTlv>,
36 ) -> Self {
37 pdu_header.pdu_conf.direction = Direction::TowardsReceiver;
39 let mut eof_pdu = Self {
40 pdu_header,
41 condition_code,
42 file_checksum,
43 file_size,
44 fault_location,
45 };
46 eof_pdu.pdu_header.pdu_datafield_len = eof_pdu.calc_pdu_datafield_len() as u16;
47 eof_pdu
48 }
49
50 pub fn new_no_error(pdu_header: PduHeader, file_checksum: u32, file_size: u64) -> Self {
52 Self::new(
53 pdu_header,
54 ConditionCode::NoError,
55 file_checksum,
56 file_size,
57 None,
58 )
59 }
60
61 #[inline]
63 pub fn pdu_header(&self) -> &PduHeader {
64 &self.pdu_header
65 }
66
67 #[inline]
69 pub fn condition_code(&self) -> ConditionCode {
70 self.condition_code
71 }
72
73 #[inline]
75 pub fn file_checksum(&self) -> u32 {
76 self.file_checksum
77 }
78
79 #[inline]
81 pub fn file_size(&self) -> u64 {
82 self.file_size
83 }
84
85 fn calc_pdu_datafield_len(&self) -> usize {
86 let mut len = 2 + core::mem::size_of::<u32>() + 4;
88 if self.pdu_header.pdu_conf.file_flag == LargeFileFlag::Large {
89 len += 4;
90 }
91 if let Some(fault_location) = self.fault_location {
92 len += fault_location.len_full();
93 }
94 if self.crc_flag() == CrcFlag::WithCrc {
95 len += 2;
96 }
97 len
98 }
99
100 pub fn from_bytes(buf: &[u8]) -> Result<EofPdu, PduError> {
102 let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
103 let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
104 let is_large_file = pdu_header.pdu_conf.file_flag == LargeFileFlag::Large;
105 let mut min_expected_len = 2 + 4 + 4;
106 if is_large_file {
107 min_expected_len += 4;
108 }
109 generic_length_checks_pdu_deserialization(buf, min_expected_len, full_len_without_crc)?;
110 let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
111 PduError::InvalidDirectiveType {
112 found: buf[current_idx],
113 expected: Some(FileDirectiveType::Eof),
114 }
115 })?;
116 if directive_type != FileDirectiveType::Eof {
117 return Err(PduError::WrongDirectiveType {
118 found: directive_type,
119 expected: FileDirectiveType::Eof,
120 });
121 }
122 current_idx += 1;
123 let condition_code = ConditionCode::try_from((buf[current_idx] >> 4) & 0b1111)
124 .map_err(|_| PduError::InvalidConditionCode((buf[current_idx] >> 4) & 0b1111))?;
125 current_idx += 1;
126 let file_checksum =
127 u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap());
128 current_idx += 4;
129 let (fss_field_len, file_size) =
130 read_fss_field(pdu_header.pdu_conf.file_flag, &buf[current_idx..]);
131 current_idx += fss_field_len;
132 let mut fault_location = None;
133 if condition_code != ConditionCode::NoError && current_idx < full_len_without_crc {
134 fault_location = Some(EntityIdTlv::from_bytes(&buf[current_idx..])?);
135 }
136 Ok(Self {
137 pdu_header,
138 condition_code,
139 file_checksum,
140 file_size,
141 fault_location,
142 })
143 }
144
145 pub fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
147 let expected_len = self.len_written();
148 if buf.len() < expected_len {
149 return Err(ByteConversionError::ToSliceTooSmall {
150 found: buf.len(),
151 expected: expected_len,
152 }
153 .into());
154 }
155 let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
156 buf[current_idx] = FileDirectiveType::Eof as u8;
157 current_idx += 1;
158 buf[current_idx] = (self.condition_code as u8) << 4;
159 current_idx += 1;
160 buf[current_idx..current_idx + 4].copy_from_slice(&self.file_checksum.to_be_bytes());
161 current_idx += 4;
162 current_idx += write_fss_field(
163 self.pdu_header.pdu_conf.file_flag,
164 self.file_size,
165 &mut buf[current_idx..],
166 )?;
167 if let Some(fault_location) = self.fault_location {
168 current_idx += fault_location.write_to_bytes(&mut buf[current_idx..])?;
169 }
170 if self.crc_flag() == CrcFlag::WithCrc {
171 current_idx = add_pdu_crc(buf, current_idx);
172 }
173 Ok(current_idx)
174 }
175
176 pub fn len_written(&self) -> usize {
178 self.pdu_header.header_len() + self.calc_pdu_datafield_len()
179 }
180}
181
182impl CfdpPdu for EofPdu {
183 #[inline]
184 fn pdu_header(&self) -> &PduHeader {
185 self.pdu_header()
186 }
187
188 #[inline]
189 fn file_directive_type(&self) -> Option<FileDirectiveType> {
190 Some(FileDirectiveType::Eof)
191 }
192}
193
194impl WritablePduPacket for EofPdu {
195 fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
196 self.write_to_bytes(buf)
197 }
198
199 fn len_written(&self) -> usize {
200 self.len_written()
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use crate::cfdp::pdu::tests::{
208 common_pdu_conf, verify_raw_header, TEST_DEST_ID, TEST_SEQ_NUM, TEST_SRC_ID,
209 };
210 use crate::cfdp::pdu::{FileDirectiveType, PduHeader};
211 use crate::cfdp::{ConditionCode, CrcFlag, LargeFileFlag, PduType, TransmissionMode};
212 #[cfg(feature = "serde")]
213 use crate::tests::generic_serde_test;
214 use crate::util::{UnsignedByteFieldU16, UnsignedEnum};
215
216 fn verify_state_no_error_no_crc(eof_pdu: &EofPdu, file_flag: LargeFileFlag) {
217 verify_state(eof_pdu, CrcFlag::NoCrc, file_flag, ConditionCode::NoError);
218 }
219
220 fn verify_state(
221 eof_pdu: &EofPdu,
222 crc_flag: CrcFlag,
223 file_flag: LargeFileFlag,
224 cond_code: ConditionCode,
225 ) {
226 assert_eq!(eof_pdu.file_checksum(), 0x01020304);
227 assert_eq!(eof_pdu.file_size(), 12);
228 assert_eq!(eof_pdu.condition_code(), cond_code);
229
230 assert_eq!(eof_pdu.crc_flag(), crc_flag);
231 assert_eq!(eof_pdu.file_flag(), file_flag);
232 assert_eq!(eof_pdu.pdu_type(), PduType::FileDirective);
233 assert_eq!(eof_pdu.file_directive_type(), Some(FileDirectiveType::Eof));
234 assert_eq!(eof_pdu.transmission_mode(), TransmissionMode::Acknowledged);
235 assert_eq!(eof_pdu.direction(), Direction::TowardsReceiver);
236 assert_eq!(eof_pdu.source_id(), TEST_SRC_ID.into());
237 assert_eq!(eof_pdu.dest_id(), TEST_DEST_ID.into());
238 assert_eq!(eof_pdu.transaction_seq_num(), TEST_SEQ_NUM.into());
239 }
240
241 #[test]
242 fn test_basic() {
243 let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
244 let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
245 let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
246 assert_eq!(eof_pdu.len_written(), pdu_header.header_len() + 2 + 4 + 4);
247 verify_state_no_error_no_crc(&eof_pdu, LargeFileFlag::Normal);
248 }
249
250 #[test]
251 fn test_serialization() {
252 let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
253 let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
254 let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
255 let mut buf: [u8; 64] = [0; 64];
256 let res = eof_pdu.write_to_bytes(&mut buf);
257 assert!(res.is_ok());
258 let written = res.unwrap();
259 assert_eq!(written, eof_pdu.len_written());
260 verify_raw_header(eof_pdu.pdu_header(), &buf);
261 let mut current_idx = eof_pdu.pdu_header().header_len();
262 buf[current_idx] = FileDirectiveType::Eof as u8;
263 current_idx += 1;
264 assert_eq!(
265 (buf[current_idx] >> 4) & 0b1111,
266 ConditionCode::NoError as u8
267 );
268 current_idx += 1;
269 assert_eq!(
270 u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()),
271 0x01020304
272 );
273 current_idx += 4;
274 assert_eq!(
275 u32::from_be_bytes(buf[current_idx..current_idx + 4].try_into().unwrap()),
276 12
277 );
278 current_idx += 4;
279 assert_eq!(current_idx, written);
280 }
281
282 #[test]
283 fn test_deserialization() {
284 let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
285 let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
286 let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
287 let mut buf: [u8; 64] = [0; 64];
288 eof_pdu.write_to_bytes(&mut buf).unwrap();
289 let eof_read_back = EofPdu::from_bytes(&buf);
290 if let Err(e) = eof_read_back {
291 panic!("deserialization failed with: {e}")
292 }
293 let eof_read_back = eof_read_back.unwrap();
294 assert_eq!(eof_read_back, eof_pdu);
295 }
296
297 #[test]
298 fn test_write_to_vec() {
299 let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
300 let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
301 let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
302 let mut buf: [u8; 64] = [0; 64];
303 let written = eof_pdu.write_to_bytes(&mut buf).unwrap();
304 let pdu_vec = eof_pdu.to_vec().unwrap();
305 assert_eq!(buf[0..written], pdu_vec);
306 }
307
308 #[test]
309 fn test_with_crc() {
310 let pdu_conf = common_pdu_conf(CrcFlag::WithCrc, LargeFileFlag::Normal);
311 let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
312 let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
313 let mut buf: [u8; 64] = [0; 64];
314 let written = eof_pdu.write_to_bytes(&mut buf).unwrap();
315 assert_eq!(written, eof_pdu.len_written());
316 let eof_from_raw = EofPdu::from_bytes(&buf).expect("creating EOF PDU failed");
317 assert_eq!(eof_from_raw, eof_pdu);
318 buf[written - 1] -= 1;
319 let crc: u16 = ((buf[written - 2] as u16) << 8) as u16 | buf[written - 1] as u16;
320 let error = EofPdu::from_bytes(&buf).unwrap_err();
321 if let PduError::Checksum(e) = error {
322 assert_eq!(e, crc);
323 } else {
324 panic!("expected crc error");
325 }
326 }
327
328 #[test]
329 fn test_with_large_file_flag() {
330 let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Large);
331 let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
332 let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
333 verify_state_no_error_no_crc(&eof_pdu, LargeFileFlag::Large);
334 assert_eq!(eof_pdu.len_written(), pdu_header.header_len() + 2 + 8 + 4);
335 }
336
337 #[test]
338 #[cfg(feature = "serde")]
339 fn test_eof_serde() {
340 let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
341 let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
342 let eof_pdu = EofPdu::new_no_error(pdu_header, 0x01020304, 12);
343 generic_serde_test(eof_pdu);
344 }
345
346 fn generic_test_with_fault_location_and_error(crc: CrcFlag) {
347 let pdu_conf = common_pdu_conf(crc, LargeFileFlag::Normal);
348 let pdu_header = PduHeader::new_for_file_directive(pdu_conf, 0);
349 let eof_pdu = EofPdu::new(
350 pdu_header,
351 ConditionCode::FileChecksumFailure,
352 0x01020304,
353 12,
354 Some(EntityIdTlv::new(UnsignedByteFieldU16::new(5).into())),
355 );
356 let mut expected_len = pdu_header.header_len() + 2 + 4 + 4 + 4;
357 if crc == CrcFlag::WithCrc {
358 expected_len += 2;
359 }
360 assert_eq!(eof_pdu.len_written(), expected_len);
362 verify_state(
363 &eof_pdu,
364 crc,
365 LargeFileFlag::Normal,
366 ConditionCode::FileChecksumFailure,
367 );
368 let eof_vec = eof_pdu.to_vec().unwrap();
369 let eof_read_back = EofPdu::from_bytes(&eof_vec);
370 if let Err(e) = eof_read_back {
371 panic!("deserialization failed with: {e}")
372 }
373 let eof_read_back = eof_read_back.unwrap();
374 assert_eq!(eof_read_back, eof_pdu);
375 assert!(eof_read_back.fault_location.is_some());
376 assert_eq!(eof_read_back.fault_location.unwrap().entity_id().value(), 5);
377 assert_eq!(eof_read_back.fault_location.unwrap().entity_id().size(), 2);
378 }
379
380 #[test]
381 fn test_with_fault_location_and_error() {
382 generic_test_with_fault_location_and_error(CrcFlag::NoCrc);
383 }
384
385 #[test]
386 fn test_with_fault_location_and_error_and_crc() {
387 generic_test_with_fault_location_and_error(CrcFlag::WithCrc);
388 }
389}