1use crate::config::ComponentConfig;
5use bincode::de::{Decode, Decoder};
6use bincode::enc::{Encode, Encoder};
7use bincode::error::{DecodeError, EncodeError};
8use compact_str::{CompactString, ToCompactString};
9use cu29_clock::{PartialCuTimeRange, RobotClock, Tov};
10use cu29_traits::{
11 CuCompactString, CuMsgMetadataTrait, CuResult, ErasedCuStampedData, Metadata,
12 COMPACT_STRING_CAPACITY,
13};
14use serde::{Deserialize, Serialize};
15
16#[cfg(not(feature = "std"))]
17mod imp {
18 pub use alloc::fmt::Result as FmtResult;
19 pub use alloc::fmt::{Debug, Display, Formatter};
20}
21
22#[cfg(feature = "std")]
23mod imp {
24 pub use std::fmt::Result as FmtResult;
25 pub use std::fmt::{Debug, Display, Formatter};
26}
27
28use imp::*;
29
30pub trait CuMsgPayload: Default + Debug + Clone + Encode + Decode<()> + Serialize + Sized {}
33
34pub trait CuMsgPack {}
35
36impl<T: Default + Debug + Clone + Encode + Decode<()> + Serialize + Sized> CuMsgPayload for T {}
38
39macro_rules! impl_cu_msg_pack {
40 ($($name:ident),+) => {
41 impl<'cl, $($name),+> CuMsgPack for ($(&CuMsg<$name>,)+)
42 where
43 $($name: CuMsgPayload),+
44 {}
45 };
46}
47
48impl<T: CuMsgPayload> CuMsgPack for CuMsg<T> {}
49impl<T: CuMsgPayload> CuMsgPack for &CuMsg<T> {}
50impl<T: CuMsgPayload> CuMsgPack for (&CuMsg<T>,) {}
51impl CuMsgPack for () {}
52
53impl_cu_msg_pack!(T1, T2);
55impl_cu_msg_pack!(T1, T2, T3);
56impl_cu_msg_pack!(T1, T2, T3, T4);
57impl_cu_msg_pack!(T1, T2, T3, T4, T5);
58
59#[macro_export]
62macro_rules! input_msg {
63 ($lt:lifetime, $first:ty, $($rest:ty),+) => {
64 ( & $lt CuMsg<$first>, $( & $lt CuMsg<$rest> ),+ )
65 };
66 ($lt:lifetime, $ty:ty) => {
67 CuMsg<$ty> };
69 ($ty:ty) => {
70 CuMsg<$ty>
71 };
72}
73
74#[macro_export]
76macro_rules! output_msg {
77 ($ty:ty) => {
78 CuMsg<$ty>
79 };
80 ($lt:lifetime, $ty:ty) => {
81 CuMsg<$ty> };
83}
84
85#[derive(Debug, Clone, bincode::Encode, bincode::Decode, Serialize, Deserialize)]
87pub struct CuMsgMetadata {
88 pub process_time: PartialCuTimeRange,
90 pub status_txt: CuCompactString,
93}
94
95impl Metadata for CuMsgMetadata {}
96
97impl CuMsgMetadata {
98 pub fn set_status(&mut self, status: impl ToCompactString) {
99 self.status_txt = CuCompactString(status.to_compact_string());
100 }
101}
102
103impl CuMsgMetadataTrait for CuMsgMetadata {
104 fn process_time(&self) -> PartialCuTimeRange {
105 self.process_time
106 }
107
108 fn status_txt(&self) -> &CuCompactString {
109 &self.status_txt
110 }
111}
112
113impl Display for CuMsgMetadata {
114 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
115 write!(
116 f,
117 "process_time start: {}, process_time end: {}",
118 self.process_time.start, self.process_time.end
119 )
120 }
121}
122
123#[derive(Default, Debug, Clone, bincode::Encode, bincode::Decode, Serialize)]
125pub struct CuStampedData<T, M>
126where
127 T: CuMsgPayload,
128 M: Metadata,
129{
130 payload: Option<T>,
132
133 pub tov: Tov,
136
137 pub metadata: M,
139}
140
141impl Default for CuMsgMetadata {
142 fn default() -> Self {
143 CuMsgMetadata {
144 process_time: PartialCuTimeRange::default(),
145 status_txt: CuCompactString(CompactString::with_capacity(COMPACT_STRING_CAPACITY)),
146 }
147 }
148}
149
150impl<T, M> CuStampedData<T, M>
151where
152 T: CuMsgPayload,
153 M: Metadata,
154{
155 pub fn new(payload: Option<T>) -> Self {
156 CuStampedData {
157 payload,
158 tov: Tov::default(),
159 metadata: M::default(),
160 }
161 }
162 pub fn payload(&self) -> Option<&T> {
163 self.payload.as_ref()
164 }
165
166 pub fn set_payload(&mut self, payload: T) {
167 self.payload = Some(payload);
168 }
169
170 pub fn clear_payload(&mut self) {
171 self.payload = None;
172 }
173
174 pub fn payload_mut(&mut self) -> &mut Option<T> {
175 &mut self.payload
176 }
177}
178
179impl<T, M> ErasedCuStampedData for CuStampedData<T, M>
180where
181 T: CuMsgPayload,
182 M: CuMsgMetadataTrait + Metadata,
183{
184 fn payload(&self) -> Option<&dyn erased_serde::Serialize> {
185 self.payload
186 .as_ref()
187 .map(|p| p as &dyn erased_serde::Serialize)
188 }
189
190 fn tov(&self) -> Tov {
191 self.tov
192 }
193
194 fn metadata(&self) -> &dyn CuMsgMetadataTrait {
195 &self.metadata
196 }
197}
198
199pub type CuMsg<T> = CuStampedData<T, CuMsgMetadata>;
202
203pub trait Freezable {
206 fn freeze<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
210 Encode::encode(&(), encoder) }
212
213 fn thaw<D: Decoder>(&mut self, _decoder: &mut D) -> Result<(), DecodeError> {
216 Ok(())
217 }
218}
219
220pub struct BincodeAdapter<'a, T: Freezable + ?Sized>(pub &'a T);
223
224impl<'a, T: Freezable + ?Sized> Encode for BincodeAdapter<'a, T> {
225 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
226 self.0.freeze(encoder)
227 }
228}
229
230pub trait CuSrcTask: Freezable {
235 type Output<'m>: CuMsgPayload;
236
237 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
240 where
241 Self: Sized;
242
243 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
245 Ok(())
246 }
247
248 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
252 Ok(())
253 }
254
255 fn process<'o>(&mut self, clock: &RobotClock, new_msg: &mut Self::Output<'o>) -> CuResult<()>;
259
260 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
264 Ok(())
265 }
266
267 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
269 Ok(())
270 }
271}
272
273pub trait CuTask: Freezable {
275 type Input<'m>: CuMsgPack;
276 type Output<'m>: CuMsgPayload;
277
278 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
281 where
282 Self: Sized;
283
284 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
286 Ok(())
287 }
288
289 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
293 Ok(())
294 }
295
296 fn process<'i, 'o>(
300 &mut self,
301 _clock: &RobotClock,
302 input: &Self::Input<'i>,
303 output: &mut Self::Output<'o>,
304 ) -> CuResult<()>;
305
306 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
310 Ok(())
311 }
312
313 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
315 Ok(())
316 }
317}
318
319pub trait CuSinkTask: Freezable {
321 type Input<'m>: CuMsgPack;
322
323 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
326 where
327 Self: Sized;
328
329 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
331 Ok(())
332 }
333
334 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
338 Ok(())
339 }
340
341 fn process<'i>(&mut self, _clock: &RobotClock, input: &Self::Input<'i>) -> CuResult<()>;
345
346 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
350 Ok(())
351 }
352
353 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
355 Ok(())
356 }
357}
358
359#[cfg(test)]
360mod tests {
361 use super::*;
362 use bincode::{config, decode_from_slice, encode_to_vec};
363
364 #[test]
365 fn test_cucompactstr_encode_decode() {
366 let cstr = CuCompactString(CompactString::from("hello"));
367 let config = config::standard();
368 let encoded = encode_to_vec(&cstr, config).expect("Encoding failed");
369 let (decoded, _): (CuCompactString, usize) =
370 decode_from_slice(&encoded, config).expect("Decoding failed");
371 assert_eq!(cstr.0, decoded.0);
372 }
373}