leodos_protocols/transport/cfdp/pdu/file_directive/
finished.rs1use crate::transport::cfdp::pdu::Pdu;
2use crate::transport::cfdp::pdu::file_directive::ConditionCode;
3use crate::transport::cfdp::pdu::header::TransmissionMode;
4use crate::transport::cfdp::CfdpError;
5use crate::transport::cfdp::pdu::EntityId;
6use crate::transport::cfdp::pdu::TransactionSeqNum;
7use crate::transport::cfdp::pdu::file_directive::DirectiveCode;
8use crate::transport::cfdp::pdu::file_directive::FileDirectivePdu;
9use crate::transport::cfdp::pdu::header::Direction;
10use crate::transport::cfdp::pdu::header::PduHeaderFixedPart;
11use crate::transport::cfdp::pdu::header::PduType;
12use crate::transport::cfdp::pdu::tlv::TlvIterator;
13use crate::transport::cfdp::pdu::tlv::TlvType;
14use crate::utils::get_bits_u8;
15use crate::utils::set_bits_u8;
16
17use bon::bon;
18use zerocopy::FromBytes;
19use zerocopy::Immutable;
20use zerocopy::IntoBytes;
21use zerocopy::KnownLayout;
22use zerocopy::Unaligned;
23
24#[repr(C)]
48#[derive(Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
49pub struct FinishedPdu {
50 packed_flags: u8,
53 rest: [u8],
55}
56
57#[rustfmt::skip]
58mod bitmasks {
60 pub const FINISHED_CONDITION_CODE_MASK: u8 = 0b_11110000;
62 pub const _FINISHED_RESERVED_MASK: u8 = 0b_00001000;
64 pub const FINISHED_DELIVERY_CODE_MASK: u8 = 0b_00000100;
66 pub const FINISHED_FILE_STATUS_MASK: u8 = 0b_00000011;
68}
69
70use bitmasks::*;
71
72#[repr(u8)]
74#[derive(Debug, PartialEq, Eq, Copy, Clone)]
75pub enum FileStatus {
76 DiscardedDeliberately = 0b00,
78 DiscardedFileStoreRejection = 0b01,
80 Retained = 0b10,
82 Unreported = 0b11,
84}
85
86impl TryFrom<u8> for FileStatus {
87 type Error = ();
88
89 fn try_from(val: u8) -> Result<Self, ()> {
90 let res = match val {
91 0b00 => FileStatus::DiscardedDeliberately,
92 0b01 => FileStatus::DiscardedFileStoreRejection,
93 0b10 => FileStatus::Retained,
94 0b11 => FileStatus::Unreported,
95 _ => return Err(()),
96 };
97 Ok(res)
98 }
99}
100
101impl FinishedPdu {
102 pub fn condition_code(&self) -> Result<ConditionCode, CfdpError> {
104 ConditionCode::try_from(get_bits_u8(self.packed_flags, FINISHED_CONDITION_CODE_MASK))
105 }
106 pub fn set_condition_code(&mut self, code: ConditionCode) {
108 set_bits_u8(&mut self.packed_flags, FINISHED_CONDITION_CODE_MASK, code as u8);
109 }
110
111 pub fn delivery_code(&self) -> bool {
113 get_bits_u8(self.packed_flags, FINISHED_DELIVERY_CODE_MASK) == 0
116 }
117 pub fn set_delivery_code(&mut self, complete: bool) {
119 set_bits_u8(
120 &mut self.packed_flags,
121 FINISHED_DELIVERY_CODE_MASK,
122 (!complete) as u8,
123 );
124 }
125
126 pub fn file_status(&self) -> Option<FileStatus> {
128 FileStatus::try_from(get_bits_u8(self.packed_flags, FINISHED_FILE_STATUS_MASK)).ok()
129 }
130 pub fn set_file_status(&mut self, status: FileStatus) {
132 set_bits_u8(&mut self.packed_flags, FINISHED_FILE_STATUS_MASK, status as u8);
133 }
134
135 pub fn tlvs(&self) -> TlvIterator<'_> {
137 TlvIterator { buffer: &self.rest }
138 }
139
140 pub fn filestore_responses(&self) -> impl Iterator<Item = &[u8]> + '_ {
143 self.tlvs()
144 .filter(|tlv| tlv.tlv_type() == Ok(TlvType::FilestoreResponse))
145 .map(|tlv| tlv.value())
146 }
147
148 pub fn fault_location(&self) -> Option<&[u8]> {
150 self.tlvs()
151 .find(|tlv| tlv.tlv_type() == Ok(TlvType::EntityId))
152 .map(|tlv| tlv.value())
153 }
154
155 pub fn set_tlvs(
157 &mut self,
158 filestore_responses: Option<&[u8]>,
159 fault_location: Option<&[u8]>,
160 ) -> Result<(), CfdpError> {
161 let mut cursor = 0;
162 if let Some(responses) = filestore_responses {
163 let len = responses.len();
164 self.rest
165 .get_mut(cursor..cursor + len)
166 .ok_or_else(|| CfdpError::Custom("Insufficient space for Filestore Responses"))?
167 .copy_from_slice(responses);
168 cursor += len;
169 }
170 if let Some(location) = fault_location {
171 let len = location.len();
172 self.rest
173 .get_mut(cursor..cursor + len)
174 .ok_or_else(|| CfdpError::Custom("Insufficient space for Fault Location"))?
175 .copy_from_slice(location);
176 }
177 Ok(())
178 }
179
180 pub fn rest(&self) -> &[u8] {
182 &self.rest
183 }
184}
185
186#[bon]
187impl FinishedPdu {
188 #[builder]
190 pub fn new<'a>(
191 buffer: &'a mut [u8],
192 source_entity_id: EntityId,
193 dest_entity_id: EntityId,
194 transaction_seq_num: TransactionSeqNum,
195 transmission_mode: TransmissionMode,
196 large_file_flag: bool,
197 crc_flag: bool,
198 condition_code: ConditionCode,
199 delivery_code_complete: bool,
200 file_status: FileStatus,
201 filestore_responses: Option<&'a [u8]>,
202 fault_location: Option<&'a [u8]>,
203 ) -> Result<&'a mut Pdu, CfdpError> {
204 let fixed_part_len = size_of::<u8>();
205 let fs_responses_len = filestore_responses.map_or(0, |r| r.len());
206 let fault_loc_len = fault_location.map_or(0, |loc| loc.len());
207 let specific_data_len = fixed_part_len + fs_responses_len + fault_loc_len;
208 let data_field_len = (1 + specific_data_len) as u16;
209
210 let header = PduHeaderFixedPart::builder()
211 .version(1)
212 .pdu_type(PduType::FileDirective)
213 .direction(Direction::TowardSender)
214 .tx_mode(transmission_mode)
215 .crc_flag(crc_flag)
216 .large_file_flag(large_file_flag)
217 .data_field_len(data_field_len)
218 .seg_ctrl(false)
219 .seg_meta_flag(false)
220 .build()?;
221
222 let pdu = Pdu::builder()
223 .buffer(buffer)
224 .header_fixed(header)
225 .source_entity_id(source_entity_id)
226 .destination_entity_id(dest_entity_id)
227 .transaction_seq_num(transaction_seq_num)
228 .build()?;
229
230 let data_field = pdu.data_field_mut()?;
231 let directive_pdu = FileDirectivePdu::mut_from_bytes(data_field)
232 .map_err(|_| CfdpError::Custom("Failed to parse File Directive PDU from data field"))?;
233 directive_pdu.set_directive_code(DirectiveCode::Finished);
234 let remaining_len = directive_pdu.rest.len();
235
236 let rest_len = fs_responses_len + fault_loc_len;
237 let (fin_pdu, _rest) =
238 FinishedPdu::mut_from_prefix_with_elems(&mut directive_pdu.rest, rest_len)
239 .map_err(|_| CfdpError::BufferTooSmall {
240 provided: remaining_len,
241 required: specific_data_len,
242 })?;
243 fin_pdu.set_condition_code(condition_code);
244 fin_pdu.set_delivery_code(delivery_code_complete);
245 fin_pdu.set_file_status(file_status);
246 fin_pdu.set_tlvs(filestore_responses, fault_location)?;
247
248 Ok(pdu)
249 }
250}