1
use crate::{
2
    cfdp::{ConditionCode, CrcFlag, Direction, TransactionStatus},
3
    ByteConversionError,
4
};
5

            
6
use super::{
7
    add_pdu_crc, generic_length_checks_pdu_deserialization, CfdpPdu, FileDirectiveType, PduError,
8
    PduHeader, WritablePduPacket,
9
};
10
#[cfg(feature = "serde")]
11
use serde::{Deserialize, Serialize};
12

            
13
/// ACK PDU abstraction.
14
///
15
/// For more information, refer to CFDP chapter 5.2.4.
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 AckPdu {
20
    pdu_header: PduHeader,
21
    directive_code_of_acked_pdu: FileDirectiveType,
22
    condition_code: ConditionCode,
23
    transaction_status: TransactionStatus,
24
}
25

            
26
impl AckPdu {
27
18
    pub fn new(
28
18
        mut pdu_header: PduHeader,
29
18
        directive_code_of_acked_pdu: FileDirectiveType,
30
18
        condition_code: ConditionCode,
31
18
        transaction_status: TransactionStatus,
32
18
    ) -> Result<Self, PduError> {
33
18
        if directive_code_of_acked_pdu == FileDirectiveType::EofPdu {
34
4
            pdu_header.pdu_conf.direction = Direction::TowardsSender;
35
14
        } else if directive_code_of_acked_pdu == FileDirectiveType::FinishedPdu {
36
14
            pdu_header.pdu_conf.direction = Direction::TowardsReceiver;
37
14
        } else {
38
            return Err(PduError::InvalidDirectiveType {
39
                found: directive_code_of_acked_pdu as u8,
40
                expected: None,
41
            });
42
        }
43
        // Force correct direction flag.
44
18
        let mut ack_pdu = Self {
45
18
            pdu_header,
46
18
            directive_code_of_acked_pdu,
47
18
            condition_code,
48
18
            transaction_status,
49
18
        };
50
18
        ack_pdu.pdu_header.pdu_datafield_len = ack_pdu.calc_pdu_datafield_len() as u16;
51
18
        Ok(ack_pdu)
52
18
    }
53

            
54
4
    pub fn new_for_eof_pdu(
55
4
        pdu_header: PduHeader,
56
4
        condition_code: ConditionCode,
57
4
        transaction_status: TransactionStatus,
58
4
    ) -> Self {
59
4
        // Unwrap okay here, [new] can only fail on invalid directive codes.
60
4
        Self::new(
61
4
            pdu_header,
62
4
            FileDirectiveType::EofPdu,
63
4
            condition_code,
64
4
            transaction_status,
65
4
        )
66
4
        .unwrap()
67
4
    }
68

            
69
8
    pub fn new_for_finished_pdu(
70
8
        pdu_header: PduHeader,
71
8
        condition_code: ConditionCode,
72
8
        transaction_status: TransactionStatus,
73
8
    ) -> Self {
74
8
        // Unwrap okay here, [new] can only fail on invalid directive codes.
75
8
        Self::new(
76
8
            pdu_header,
77
8
            FileDirectiveType::FinishedPdu,
78
8
            condition_code,
79
8
            transaction_status,
80
8
        )
81
8
        .unwrap()
82
8
    }
83

            
84
4
    pub fn pdu_header(&self) -> &PduHeader {
85
4
        &self.pdu_header
86
4
    }
87

            
88
4
    pub fn directive_code_of_acked_pdu(&self) -> FileDirectiveType {
89
4
        self.directive_code_of_acked_pdu
90
4
    }
91

            
92
4
    pub fn condition_code(&self) -> ConditionCode {
93
4
        self.condition_code
94
4
    }
95

            
96
4
    pub fn transaction_status(&self) -> TransactionStatus {
97
4
        self.transaction_status
98
4
    }
99

            
100
36
    fn calc_pdu_datafield_len(&self) -> usize {
101
36
        if self.crc_flag() == CrcFlag::WithCrc {
102
14
            return 5;
103
22
        }
104
22
        3
105
36
    }
106

            
107
4
    pub fn from_bytes(buf: &[u8]) -> Result<AckPdu, PduError> {
108
4
        let (pdu_header, mut current_idx) = PduHeader::from_bytes(buf)?;
109
4
        let full_len_without_crc = pdu_header.verify_length_and_checksum(buf)?;
110
4
        generic_length_checks_pdu_deserialization(buf, current_idx + 3, full_len_without_crc)?;
111
4
        let directive_type = FileDirectiveType::try_from(buf[current_idx]).map_err(|_| {
112
            PduError::InvalidDirectiveType {
113
                found: buf[current_idx],
114
                expected: Some(FileDirectiveType::AckPdu),
115
            }
116
4
        })?;
117
4
        if directive_type != FileDirectiveType::AckPdu {
118
            return Err(PduError::WrongDirectiveType {
119
                found: directive_type,
120
                expected: FileDirectiveType::AckPdu,
121
            });
122
4
        }
123
4
        current_idx += 1;
124
4
        let acked_directive_type =
125
4
            FileDirectiveType::try_from(buf[current_idx] >> 4).map_err(|_| {
126
                PduError::InvalidDirectiveType {
127
                    found: buf[current_idx],
128
                    expected: None,
129
                }
130
4
            })?;
131
4
        if acked_directive_type != FileDirectiveType::EofPdu
132
4
            && acked_directive_type != FileDirectiveType::FinishedPdu
133
        {
134
            return Err(PduError::InvalidDirectiveType {
135
                found: acked_directive_type as u8,
136
                expected: None,
137
            });
138
4
        }
139
4
        current_idx += 1;
140
4
        let condition_code = ConditionCode::try_from((buf[current_idx] >> 4) & 0b1111)
141
4
            .map_err(|_| PduError::InvalidConditionCode((buf[current_idx] >> 4) & 0b1111))?;
142
4
        let transaction_status = TransactionStatus::try_from(buf[current_idx] & 0b11).unwrap();
143
4
        Self::new(
144
4
            pdu_header,
145
4
            acked_directive_type,
146
4
            condition_code,
147
4
            transaction_status,
148
4
        )
149
4
    }
150
}
151

            
152
impl CfdpPdu for AckPdu {
153
76
    fn pdu_header(&self) -> &PduHeader {
154
76
        &self.pdu_header
155
76
    }
156

            
157
4
    fn file_directive_type(&self) -> Option<FileDirectiveType> {
158
4
        Some(FileDirectiveType::AckPdu)
159
4
    }
160
}
161

            
162
impl WritablePduPacket for AckPdu {
163
8
    fn write_to_bytes(&self, buf: &mut [u8]) -> Result<usize, PduError> {
164
8
        let expected_len = self.len_written();
165
8
        if buf.len() < expected_len {
166
            return Err(ByteConversionError::ToSliceTooSmall {
167
                found: buf.len(),
168
                expected: expected_len,
169
            }
170
            .into());
171
8
        }
172
8
        let mut current_idx = self.pdu_header.write_to_bytes(buf)?;
173
8
        buf[current_idx] = FileDirectiveType::AckPdu as u8;
174
8
        current_idx += 1;
175
8

            
176
8
        buf[current_idx] = (self.directive_code_of_acked_pdu as u8) << 4;
177
8
        if self.directive_code_of_acked_pdu == FileDirectiveType::FinishedPdu {
178
8
            // This is the directive subtype code. It needs to be set to 0b0001 if the ACK PDU
179
8
            // acknowledges a Finished PDU, and to 0b0000 otherwise.
180
8
            buf[current_idx] |= 0b0001;
181
8
        }
182
8
        current_idx += 1;
183
8
        buf[current_idx] = ((self.condition_code as u8) << 4) | (self.transaction_status as u8);
184
8
        current_idx += 1;
185
8
        if self.crc_flag() == CrcFlag::WithCrc {
186
2
            current_idx = add_pdu_crc(buf, current_idx);
187
6
        }
188
8
        Ok(current_idx)
189
8
    }
190

            
191
18
    fn len_written(&self) -> usize {
192
18
        self.pdu_header.header_len() + self.calc_pdu_datafield_len()
193
18
    }
194
}
195

            
196
#[cfg(test)]
197
mod tests {
198
    use crate::cfdp::{
199
        pdu::tests::{common_pdu_conf, verify_raw_header, TEST_DEST_ID, TEST_SEQ_NUM, TEST_SRC_ID},
200
        LargeFileFlag, PduType, TransmissionMode,
201
    };
202

            
203
    use super::*;
204
    #[cfg(feature = "serde")]
205
    use crate::tests::generic_serde_test;
206

            
207
4
    fn verify_state(ack_pdu: &AckPdu, expected_crc_flag: CrcFlag, expected_dir: Direction) {
208
4
        assert_eq!(ack_pdu.condition_code(), ConditionCode::NoError);
209
4
        assert_eq!(ack_pdu.transaction_status(), TransactionStatus::Active);
210

            
211
4
        assert_eq!(ack_pdu.crc_flag(), expected_crc_flag);
212
4
        assert_eq!(ack_pdu.file_flag(), LargeFileFlag::Normal);
213
4
        assert_eq!(ack_pdu.pdu_type(), PduType::FileDirective);
214
4
        assert_eq!(
215
4
            ack_pdu.file_directive_type(),
216
4
            Some(FileDirectiveType::AckPdu)
217
4
        );
218
4
        assert_eq!(ack_pdu.transmission_mode(), TransmissionMode::Acknowledged);
219
4
        assert_eq!(ack_pdu.direction(), expected_dir);
220
4
        assert_eq!(ack_pdu.source_id(), TEST_SRC_ID.into());
221
4
        assert_eq!(ack_pdu.dest_id(), TEST_DEST_ID.into());
222
4
        assert_eq!(ack_pdu.transaction_seq_num(), TEST_SEQ_NUM.into());
223
4
    }
224

            
225
    #[test]
226
2
    fn test_basic() {
227
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
228
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
229
2
        let ack_pdu = AckPdu::new(
230
2
            pdu_header,
231
2
            FileDirectiveType::FinishedPdu,
232
2
            ConditionCode::NoError,
233
2
            TransactionStatus::Active,
234
2
        )
235
2
        .expect("creating ACK PDU failed");
236
2
        assert_eq!(
237
2
            ack_pdu.directive_code_of_acked_pdu(),
238
2
            FileDirectiveType::FinishedPdu
239
2
        );
240
2
        verify_state(&ack_pdu, CrcFlag::NoCrc, Direction::TowardsReceiver);
241
2
    }
242

            
243
4
    fn generic_serialization_test(
244
4
        condition_code: ConditionCode,
245
4
        transaction_status: TransactionStatus,
246
4
    ) {
247
4
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
248
4
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
249
4
        let ack_pdu = AckPdu::new_for_finished_pdu(pdu_header, condition_code, transaction_status);
250
4
        let mut buf: [u8; 64] = [0; 64];
251
4
        let res = ack_pdu.write_to_bytes(&mut buf);
252
4
        assert!(res.is_ok());
253
4
        let written = res.unwrap();
254
4
        assert_eq!(written, ack_pdu.len_written());
255
4
        verify_raw_header(ack_pdu.pdu_header(), &buf);
256
4

            
257
4
        assert_eq!(buf[7], FileDirectiveType::AckPdu as u8);
258
4
        assert_eq!((buf[8] >> 4) & 0b1111, FileDirectiveType::FinishedPdu as u8);
259
4
        assert_eq!(buf[8] & 0b1111, 0b0001);
260
4
        assert_eq!(buf[9] >> 4 & 0b1111, condition_code as u8);
261
4
        assert_eq!(buf[9] & 0b11, transaction_status as u8);
262
4
        assert_eq!(written, 10);
263
4
    }
264

            
265
    #[test]
266
2
    fn test_serialization_no_error() {
267
2
        generic_serialization_test(ConditionCode::NoError, TransactionStatus::Active);
268
2
    }
269

            
270
    #[test]
271
2
    fn test_serialization_fs_error() {
272
2
        generic_serialization_test(ConditionCode::FileSizeError, TransactionStatus::Terminated);
273
2
    }
274

            
275
    #[test]
276
2
    fn test_deserialization() {
277
2
        let pdu_conf = common_pdu_conf(CrcFlag::NoCrc, LargeFileFlag::Normal);
278
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
279
2
        let ack_pdu = AckPdu::new_for_finished_pdu(
280
2
            pdu_header,
281
2
            ConditionCode::NoError,
282
2
            TransactionStatus::Active,
283
2
        );
284
2
        let ack_vec = ack_pdu.to_vec().unwrap();
285
2
        let ack_deserialized =
286
2
            AckPdu::from_bytes(&ack_vec).expect("ACK PDU deserialization failed");
287
2
        assert_eq!(ack_deserialized, ack_pdu);
288
2
    }
289

            
290
    #[test]
291
2
    fn test_with_crc() {
292
2
        let pdu_conf = common_pdu_conf(CrcFlag::WithCrc, LargeFileFlag::Normal);
293
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
294
2
        let ack_pdu = AckPdu::new_for_finished_pdu(
295
2
            pdu_header,
296
2
            ConditionCode::NoError,
297
2
            TransactionStatus::Active,
298
2
        );
299
2
        let ack_vec = ack_pdu.to_vec().unwrap();
300
2
        assert_eq!(ack_vec.len(), ack_pdu.len_written());
301
2
        assert_eq!(ack_vec.len(), 12);
302
2
        let ack_deserialized =
303
2
            AckPdu::from_bytes(&ack_vec).expect("ACK PDU deserialization failed");
304
2
        assert_eq!(ack_deserialized, ack_pdu);
305
2
    }
306

            
307
    #[test]
308
2
    fn test_for_eof_pdu() {
309
2
        let pdu_conf = common_pdu_conf(CrcFlag::WithCrc, LargeFileFlag::Normal);
310
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
311
2
        let ack_pdu = AckPdu::new_for_eof_pdu(
312
2
            pdu_header,
313
2
            ConditionCode::NoError,
314
2
            TransactionStatus::Active,
315
2
        );
316
2
        assert_eq!(
317
2
            ack_pdu.directive_code_of_acked_pdu(),
318
2
            FileDirectiveType::EofPdu
319
2
        );
320
2
        verify_state(&ack_pdu, CrcFlag::WithCrc, Direction::TowardsSender);
321
2
    }
322

            
323
    #[test]
324
    #[cfg(feature = "serde")]
325
2
    fn test_ack_pdu_serialization() {
326
2
        let pdu_conf = common_pdu_conf(CrcFlag::WithCrc, LargeFileFlag::Normal);
327
2
        let pdu_header = PduHeader::new_no_file_data(pdu_conf, 0);
328
2
        let ack_pdu = AckPdu::new_for_eof_pdu(
329
2
            pdu_header,
330
2
            ConditionCode::NoError,
331
2
            TransactionStatus::Active,
332
2
        );
333
2
        generic_serde_test(ack_pdu);
334
2
    }
335
}