leodos_protocols/transport/cfdp/pdu/file_directive/
ack.rs1use bon::bon;
2use zerocopy::FromBytes;
3use zerocopy::Immutable;
4use zerocopy::IntoBytes;
5use zerocopy::KnownLayout;
6use zerocopy::Unaligned;
7
8use crate::transport::cfdp::pdu::CfdpError;
9use crate::transport::cfdp::pdu::EntityId;
10use crate::transport::cfdp::pdu::Pdu;
11use crate::transport::cfdp::pdu::TransactionSeqNum;
12use crate::transport::cfdp::pdu::file_directive::ConditionCode;
13use crate::transport::cfdp::pdu::file_directive::DirectiveCode;
14use crate::transport::cfdp::pdu::file_directive::FileDirectivePdu;
15use crate::transport::cfdp::pdu::header::Direction;
16use crate::transport::cfdp::pdu::header::PduHeaderFixedPart;
17use crate::transport::cfdp::pdu::header::PduType;
18use crate::transport::cfdp::pdu::header::TransmissionMode;
19use crate::utils::get_bits_u8;
20use crate::utils::set_bits_u8;
21
22#[repr(C)]
41#[derive(FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable, Debug, PartialEq, Eq)]
42pub struct AckPdu {
43 packed_codes: u8,
46 packed_status: u8,
49}
50
51#[rustfmt::skip]
52mod bitmasks {
54 pub const ACK_DIR_CODE_MASK: u8 = 0b_11110000;
56 pub const ACK_DIR_SUBTYPE_CODE_MASK: u8 = 0b_00001111;
58 pub const ACK_CC_MASK: u8 = 0b_11110000;
60 pub const _ACK_RESERVED_MASK: u8 = 0b_00001100;
62 pub const ACK_TRANSACTION_STATUS_MASK: u8 = 0b_00000011;
64}
65
66use bitmasks::*;
67
68#[repr(u8)]
70#[derive(Debug, PartialEq, Eq, Copy, Clone)]
71pub enum TransactionStatus {
72 Undefined = 0b00,
74 Active = 0b01,
76 Terminated = 0b10,
78 Unrecognized = 0b11,
80}
81
82impl TryFrom<u8> for TransactionStatus {
83 type Error = CfdpError;
84 fn try_from(val: u8) -> Result<Self, Self::Error> {
85 let val = match val {
86 0b00 => TransactionStatus::Undefined,
87 0b01 => TransactionStatus::Active,
88 0b10 => TransactionStatus::Terminated,
89 0b11 => TransactionStatus::Unrecognized,
90 _ => return Err(CfdpError::Custom("Invalid TransactionStatus value")),
91 };
92 Ok(val)
93 }
94}
95
96#[derive(Debug, PartialEq, Eq, Copy, Clone)]
98pub enum AckedDirectiveCode {
99 Eof,
101 Finished,
103}
104
105impl TryFrom<u8> for AckedDirectiveCode {
106 type Error = CfdpError;
107 fn try_from(val: u8) -> Result<Self, Self::Error> {
108 let val = match val {
109 0x04 => AckedDirectiveCode::Eof,
110 0x05 => AckedDirectiveCode::Finished,
111 _ => return Err(CfdpError::Custom("Invalid AckedPduDirectiveCode value")),
112 };
113 Ok(val)
114 }
115}
116
117impl AckPdu {
118 fn directive_code_of_acked_pdu(&self) -> Result<AckedDirectiveCode, CfdpError> {
120 AckedDirectiveCode::try_from(get_bits_u8(self.packed_codes, ACK_DIR_CODE_MASK))
121 }
122 fn set_directive_code_of_acked_pdu(&mut self, code: AckedDirectiveCode) {
124 set_bits_u8(&mut self.packed_codes, ACK_DIR_CODE_MASK, code as u8);
125 }
126
127 fn directive_subtype_code(&self) -> u8 {
129 get_bits_u8(self.packed_codes, ACK_DIR_SUBTYPE_CODE_MASK)
130 }
131 fn set_directive_subtype_code(&mut self, subtype: u8) {
133 set_bits_u8(&mut self.packed_codes, ACK_DIR_SUBTYPE_CODE_MASK, subtype);
134 }
135
136 pub fn acked_directive_code(&self) -> Result<AckedDirectiveCode, CfdpError> {
138 let directive_code = self.directive_code_of_acked_pdu()?;
139 let subtype_code = self.directive_subtype_code();
140 match (directive_code, subtype_code) {
141 (AckedDirectiveCode::Eof, 0) => Ok(AckedDirectiveCode::Eof),
142 (AckedDirectiveCode::Finished, 1) => Ok(AckedDirectiveCode::Finished),
143 _ => Err(CfdpError::Custom("Unknown acknowledged PDU type")),
144 }
145 }
146
147 pub fn condition_code(&self) -> Result<ConditionCode, CfdpError> {
149 ConditionCode::try_from(get_bits_u8(self.packed_status, ACK_CC_MASK))
150 }
151 pub fn set_condition_code(&mut self, code: ConditionCode) {
153 set_bits_u8(&mut self.packed_status, ACK_CC_MASK, code as u8);
154 }
155
156 pub fn transaction_status(&self) -> Result<TransactionStatus, CfdpError> {
158 TransactionStatus::try_from(get_bits_u8(self.packed_status, ACK_TRANSACTION_STATUS_MASK))
159 }
160 pub fn set_transaction_status(&mut self, status: TransactionStatus) {
162 set_bits_u8(
163 &mut self.packed_status,
164 ACK_TRANSACTION_STATUS_MASK,
165 status as u8,
166 );
167 }
168}
169
170#[bon]
171impl AckPdu {
172 #[builder]
174 pub fn new<'a>(
175 buffer: &'a mut [u8],
176 source_entity_id: EntityId,
178 dest_entity_id: EntityId,
179 transaction_seq_num: TransactionSeqNum,
180 transmission_mode: TransmissionMode,
181 crc_flag: bool,
182 acked_directive_code: AckedDirectiveCode,
184 condition_code: ConditionCode,
185 transaction_status: TransactionStatus,
186 ) -> Result<&'a mut Pdu, CfdpError> {
187 let data_field_len = (DirectiveCode::size() + size_of::<Self>()) as u16;
188
189 let header = PduHeaderFixedPart::builder()
190 .version(1)
191 .pdu_type(PduType::FileDirective)
192 .direction(Direction::TowardSender)
193 .tx_mode(transmission_mode)
194 .crc_flag(crc_flag)
195 .large_file_flag(false) .data_field_len(data_field_len)
197 .seg_ctrl(false)
198 .seg_meta_flag(false)
199 .build()?;
200
201 let pdu = Pdu::builder()
202 .buffer(buffer)
203 .header_fixed(header)
204 .source_entity_id(source_entity_id)
205 .destination_entity_id(dest_entity_id)
206 .transaction_seq_num(transaction_seq_num)
207 .build()?;
208
209 let data_field = pdu.data_field_mut().unwrap();
210 let directive_pdu = FileDirectivePdu::mut_from_bytes(data_field)
211 .map_err(|_| CfdpError::Custom("Failed to create FileDirectivePdu"))?;
212 directive_pdu.set_directive_code(DirectiveCode::Ack);
213
214 let ack_pdu = AckPdu::mut_from_bytes(&mut directive_pdu.rest)
215 .map_err(|_| CfdpError::Custom("Failed to create AckPdu"))?;
216 ack_pdu.set_directive_code_of_acked_pdu(acked_directive_code);
217 match acked_directive_code {
218 AckedDirectiveCode::Eof => ack_pdu.set_directive_subtype_code(0),
219 AckedDirectiveCode::Finished => ack_pdu.set_directive_subtype_code(1),
220 }
221 ack_pdu.set_condition_code(condition_code);
222 ack_pdu.set_transaction_status(transaction_status);
223
224 Ok(pdu)
225 }
226}