Skip to main content

leodos_protocols/transport/cfdp/pdu/tlv/
fault_handler_override.rs

1use zerocopy::FromBytes;
2use zerocopy::Immutable;
3use zerocopy::IntoBytes;
4use zerocopy::KnownLayout;
5use zerocopy::Unaligned;
6
7use crate::transport::cfdp::pdu::file_directive::ConditionCode;
8use crate::transport::cfdp::CfdpError;
9use crate::utils::get_bits_u8;
10use crate::utils::set_bits_u8;
11
12/// A zero-copy view of the Value field of a Fault Handler Override TLV.
13#[repr(C)]
14#[derive(Copy, Clone, Debug, FromBytes, IntoBytes, Unaligned, KnownLayout, Immutable)]
15pub struct TlvFaultHandlerOverride {
16    /// Packed byte with condition code (upper 4 bits) and handler code (lower 4 bits).
17    packed: u8,
18}
19
20#[rustfmt::ignore]
21/// Bit masks for the fault handler override's packed byte.
22mod bitmasks {
23    /// Mask for the 4-bit condition code.
24    pub const CONDITION_CODE_MASK: u8 = 0b_11110000;
25    /// Mask for the 4-bit handler code.
26    pub const HANDLER_CODE_MASK: u8 = 0b_00001111;
27}
28
29use bitmasks::*;
30
31/// Handler codes for Fault Handler Override TLV (Table 5-19)
32#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
33#[repr(u8)]
34pub enum HandlerCode {
35    /// Cancel the transaction (default).
36    #[default]
37    Cancel = 0x01,
38    /// Suspend the transaction.
39    Suspend = 0x02,
40    /// Ignore the fault and continue.
41    Ignore = 0x03,
42    /// Abandon the transaction without further PDUs.
43    Abandon = 0x04,
44}
45
46impl TryFrom<u8> for HandlerCode {
47    type Error = CfdpError;
48    fn try_from(value: u8) -> Result<Self, Self::Error> {
49        match value {
50            0x01 => Ok(Self::Cancel),
51            0x02 => Ok(Self::Suspend),
52            0x03 => Ok(Self::Ignore),
53            0x04 => Ok(Self::Abandon),
54            _ => Err(CfdpError::Custom("Invalid HandlerCode")),
55        }
56    }
57}
58
59impl TlvFaultHandlerOverride {
60    /// The fault condition to be handled (Table 5-5).
61    pub fn condition_code(&self) -> Result<ConditionCode, CfdpError> {
62        get_bits_u8(self.packed, CONDITION_CODE_MASK).try_into()
63    }
64    /// Sets the fault condition code.
65    pub fn set_condition_code(&mut self, code: ConditionCode) {
66        set_bits_u8(&mut self.packed, CONDITION_CODE_MASK, code as u8);
67    }
68
69    /// The action to be taken (Table 5-19).
70    pub fn handler_code(&self) -> Result<HandlerCode, CfdpError> {
71        get_bits_u8(self.packed, HANDLER_CODE_MASK).try_into()
72    }
73    /// Sets the handler action code.
74    pub fn set_handler_code(&mut self, code: HandlerCode) {
75        set_bits_u8(&mut self.packed, HANDLER_CODE_MASK, code as u8);
76    }
77}
78
79/// A bit-packed structure to store the fault handler action for all 16
80/// possible CFDP condition codes in a single `u32`.
81///
82/// Each handler requires 2 bits to represent the 4 possible actions.
83/// `16 conditions * 2 bits/condition = 32 bits`.
84#[derive(Debug, Copy, Clone, PartialEq, Eq)]
85pub struct FaultHandlerSet(u32);
86
87impl Default for FaultHandlerSet {
88    /// Creates a new `FaultHandlerSet` with the default handler
89    /// (`HandlerCode::default()`, which is `Cancel`) for all conditions.
90    fn default() -> Self {
91        let default_handler_bits = handler_to_bits(HandlerCode::default());
92        let mut packed_value = 0u32;
93        // Pre-fill all 16 slots with the default handler.
94        for i in 0..16 {
95            let shift = i * 2;
96            packed_value |= default_handler_bits << shift;
97        }
98        Self(packed_value)
99    }
100}
101
102impl FaultHandlerSet {
103    /// Creates a new `FaultHandlerSet` where all handlers are set to the
104    /// specified default action.
105    pub fn new(default_handler: HandlerCode) -> Self {
106        let handler_bits = handler_to_bits(default_handler);
107        let mut packed_value = 0u32;
108        for i in 0..16 {
109            let shift = i * 2;
110            packed_value |= handler_bits << shift;
111        }
112        Self(packed_value)
113    }
114
115    /// Sets the handler for a specific condition code.
116    pub fn set_handler(&mut self, condition: ConditionCode, handler: HandlerCode) {
117        // ConditionCode enum values are their integer representations.
118        let condition_index = condition as u8;
119        let shift = condition_index * 2;
120        let handler_bits = handler_to_bits(handler);
121
122        // 1. Create a mask to clear the 2 bits for this condition.
123        // e.g., for index 3, shift=6, mask = !(0b11 << 6) = !(11000000) = 00111111
124        let mask = !(0b11 << shift);
125
126        // 2. Clear the existing bits.
127        self.0 &= mask;
128
129        // 3. Set the new bits.
130        self.0 |= handler_bits << shift;
131    }
132
133    /// Gets the handler for a specific condition code.
134    pub fn get_handler(&self, condition: ConditionCode) -> HandlerCode {
135        let condition_index = condition as u8;
136        let shift = condition_index * 2;
137
138        // 1. Shift the relevant bits to the LSB position.
139        let bits = (self.0 >> shift) & 0b11;
140
141        // 2. Convert the 2-bit value back to a HandlerCode.
142        bits_to_handler(bits)
143    }
144}
145
146// Private helper functions to map between HandlerCode and its 2-bit representation.
147// Using this mapping:
148// 00 -> Cancel (as it's more common to default to a safe state)
149// 01 -> Suspend
150// 10 -> Ignore
151// 11 -> Abandon
152// NOTE: `HandlerCode` enum values are 1, 2, 3, 4. We map them to 0, 1, 2, 3.
153/// Maps a `HandlerCode` to its 2-bit packed representation.
154const fn handler_to_bits(handler: HandlerCode) -> u32 {
155    match handler {
156        HandlerCode::Cancel => 0b00,
157        HandlerCode::Suspend => 0b01,
158        HandlerCode::Ignore => 0b10,
159        HandlerCode::Abandon => 0b11,
160    }
161}
162
163/// Maps a 2-bit packed value back to a `HandlerCode`.
164const fn bits_to_handler(bits: u32) -> HandlerCode {
165    match bits {
166        0b00 => HandlerCode::Cancel,
167        0b01 => HandlerCode::Suspend,
168        0b10 => HandlerCode::Ignore,
169        0b11 => HandlerCode::Abandon,
170        // This case is unreachable if the mask is correct, but defensive.
171        _ => HandlerCode::Cancel,
172    }
173}
174
175#[cfg(test)]
176mod tests {
177    use super::*;
178
179    #[test]
180    fn test_fault_handler_set_default() {
181        let set = FaultHandlerSet::default();
182        // Check a few condition codes to ensure they are all the default.
183        assert_eq!(
184            set.get_handler(ConditionCode::NoError),
185            HandlerCode::default()
186        );
187        assert_eq!(
188            set.get_handler(ConditionCode::FileChecksumFailure),
189            HandlerCode::default()
190        );
191        assert_eq!(
192            set.get_handler(ConditionCode::CancelReceived),
193            HandlerCode::default()
194        );
195    }
196
197    #[test]
198    fn test_fault_handler_set_and_get() {
199        let mut set = FaultHandlerSet::default();
200
201        // Ensure default is Cancel
202        assert_eq!(
203            set.get_handler(ConditionCode::FileChecksumFailure),
204            HandlerCode::Cancel
205        );
206
207        // Set a new handler
208        set.set_handler(ConditionCode::FileChecksumFailure, HandlerCode::Ignore);
209        assert_eq!(
210            set.get_handler(ConditionCode::FileChecksumFailure),
211            HandlerCode::Ignore
212        );
213
214        // Ensure other handlers are unaffected
215        assert_eq!(
216            set.get_handler(ConditionCode::NakLimitReached),
217            HandlerCode::Cancel
218        );
219
220        // Set another handler and verify both
221        set.set_handler(ConditionCode::NakLimitReached, HandlerCode::Suspend);
222        assert_eq!(
223            set.get_handler(ConditionCode::FileChecksumFailure),
224            HandlerCode::Ignore
225        );
226        assert_eq!(
227            set.get_handler(ConditionCode::NakLimitReached),
228            HandlerCode::Suspend
229        );
230
231        // Overwrite a handler
232        set.set_handler(ConditionCode::FileChecksumFailure, HandlerCode::Abandon);
233        assert_eq!(
234            set.get_handler(ConditionCode::FileChecksumFailure),
235            HandlerCode::Abandon
236        );
237    }
238}