leodos_protocols/network/cfe/
tc.rs1use crate::network::spp;
7use crate::network::spp::Apid;
8use crate::network::spp::PacketType;
9use crate::network::spp::PrimaryHeader;
10use crate::network::spp::SecondaryHeaderFlag;
11use crate::network::spp::SequenceCount;
12use crate::network::spp::SequenceFlag;
13use crate::network::spp::SpacePacket;
14use crate::utils::checksum_u8;
15use crate::utils::validate_checksum_u8;
16use bon::bon;
17use core::mem::size_of;
18use core::ops::Deref;
19use core::ops::DerefMut;
20use zerocopy::FromBytes;
21use zerocopy::Immutable;
22use zerocopy::IntoBytes;
23use zerocopy::KnownLayout;
24use zerocopy::Unaligned;
25
26#[repr(C)]
49#[derive(FromBytes, IntoBytes, Unaligned, Immutable, KnownLayout)]
50pub struct Telecommand {
51 pub primary: PrimaryHeader,
53 pub secondary: TelecommandSecondaryHeader,
55 pub payload: [u8],
57}
58#[repr(C)]
60#[derive(IntoBytes, FromBytes, Unaligned, Immutable, KnownLayout, Default, Copy, Clone, Debug)]
61pub struct TelecommandSecondaryHeader {
62 function_code: u8,
63 checksum: u8,
64}
65
66impl TelecommandSecondaryHeader {
67 pub fn function_code(&self) -> u8 {
69 self.function_code
70 }
71
72 pub fn set_function_code(&mut self, function_code: u8) {
74 self.function_code = function_code;
75 }
76
77 pub fn checksum(&self) -> u8 {
79 self.checksum
80 }
81
82 pub fn set_checksum(&mut self, checksum: u8) {
84 self.checksum = checksum;
85 }
86}
87
88#[derive(Debug, Copy, Clone, Eq, PartialEq, thiserror::Error)]
90pub enum TelecommandError {
91 #[error("Buffer too small: required {required_len} bytes, but provided {provided_len} bytes")]
93 BufferTooSmall {
94 required_len: usize,
96 provided_len: usize,
98 },
99 #[error("SpacePacket build error: {0}")]
101 SpacePacketBuildError(#[from] spp::BuildError),
102 #[error("SpacePacket parse error: {0}")]
104 SpacePacketParseError(#[from] spp::ParseError),
105 #[error("Secondary header flag is not set to Present")]
107 MissingSecondaryHeader,
108 #[error("Packet data field does not match expected layout")]
110 PayloadMismatch,
111 #[error("Packet type is not Telecommand")]
113 TypeMismatch,
114}
115
116impl Deref for Telecommand {
117 type Target = SpacePacket;
118
119 fn deref(&self) -> &Self::Target {
120 SpacePacket::ref_from_bytes(self.as_bytes())
121 .expect("Telecommand should always be a valid SpacePacket")
122 }
123}
124
125impl DerefMut for Telecommand {
126 fn deref_mut(&mut self) -> &mut Self::Target {
127 SpacePacket::mut_from_bytes(self.as_mut_bytes())
128 .expect("Telecommand should always be a valid SpacePacket")
129 }
130}
131
132impl<'a> TryFrom<&'a SpacePacket> for &'a Telecommand {
133 type Error = TelecommandError;
134
135 fn try_from(sp: &'a SpacePacket) -> Result<Self, Self::Error> {
136 if sp.secondary_header_flag() != SecondaryHeaderFlag::Present {
137 return Err(TelecommandError::MissingSecondaryHeader);
138 }
139
140 let bytes = sp.as_bytes();
141
142 match sp.packet_type() {
143 PacketType::Telecommand => {
144 Telecommand::ref_from_bytes(bytes).map_err(|_| TelecommandError::PayloadMismatch)
145 }
146 PacketType::Telemetry => Err(TelecommandError::TypeMismatch),
147 }
148 }
149}
150
151#[bon]
152impl Telecommand {
153 #[builder]
154 pub fn new<'a>(
156 buffer: &'a mut [u8],
157 apid: Apid,
158 sequence_count: SequenceCount,
159 function_code: u8,
160 payload_len: usize,
161 ) -> Result<&'a mut Telecommand, TelecommandError> {
162 let sp = SpacePacket::builder()
163 .buffer(buffer)
164 .apid(apid)
165 .packet_type(PacketType::Telecommand)
166 .sequence_count(sequence_count)
167 .secondary_header(SecondaryHeaderFlag::Present)
168 .sequence_flag(SequenceFlag::Unsegmented)
169 .data_len(size_of::<TelecommandSecondaryHeader>() + payload_len)
170 .build()
171 .map_err(TelecommandError::SpacePacketBuildError)?;
172
173 let buffer = sp.as_mut_bytes();
174 let provided_len = buffer.len();
175 let required_len = payload_len;
176 let tc = Telecommand::mut_from_bytes_with_elems(buffer, required_len).map_err(|_| {
177 TelecommandError::BufferTooSmall {
178 required_len,
179 provided_len,
180 }
181 })?;
182
183 tc.set_function_code(function_code);
184 tc.set_cfe_checksum();
185
186 Ok(tc)
187 }
188
189 pub const fn size_minimum() -> usize {
191 size_of::<PrimaryHeader>() + size_of::<TelecommandSecondaryHeader>()
192 }
193
194 pub fn function_code(&self) -> u8 {
196 self.secondary.function_code()
197 }
198 pub fn set_function_code(&mut self, function_code: u8) {
200 self.secondary.set_function_code(function_code);
201 }
202
203 pub fn set_cfe_checksum(&mut self) {
208 self.secondary.set_checksum(0);
210 self.secondary.set_checksum(checksum_u8(self.as_bytes()));
211 }
212
213 pub fn validate_cfe_checksum(&self) -> bool {
217 validate_checksum_u8(self.as_bytes())
218 }
219
220 pub fn payload(&self) -> &[u8] {
222 &self.payload
223 }
224
225 pub fn payload_mut(&mut self) -> &mut [u8] {
227 &mut self.payload
228 }
229
230 pub fn parse<'a>(bytes: &'a [u8]) -> Result<&'a Telecommand, TelecommandError> {
232 let sp = SpacePacket::parse(bytes).map_err(TelecommandError::SpacePacketParseError)?;
233 <&'a Telecommand>::try_from(sp)
234 }
235
236 pub fn as_spacepacket(&self) -> &SpacePacket {
238 &**self
239 }
240}