1use crate::config::ComponentConfig;
5use bincode::de::Decoder;
6use bincode::de::{BorrowDecoder, Decode};
7use bincode::enc::Encode;
8use bincode::enc::Encoder;
9use bincode::error::{DecodeError, EncodeError};
10use bincode::BorrowDecode;
11use compact_str::{CompactString, ToCompactString};
12use cu29_clock::{PartialCuTimeRange, RobotClock, Tov};
13use cu29_traits::{CuResult, ErasedCuMsg};
14use serde::{Deserialize, Serialize};
15use std::fmt;
16use std::fmt::{Debug, Display, Formatter};
17
18pub trait CuMsgPayload: Default + Debug + Clone + Encode + Decode<()> + Serialize + Sized {}
20
21pub trait CuMsgPack<'cl> {}
22
23impl<T: Default + Debug + Clone + Encode + Decode<()> + Serialize + Sized> CuMsgPayload for T {}
25
26macro_rules! impl_cu_msg_pack {
27 ($(($($ty:ident),*)),*) => {
28 $(
29 impl<'cl, $($ty: CuMsgPayload + 'cl),*> CuMsgPack<'cl> for ( $( &'cl CuMsg<$ty>, )* ) {}
30 )*
31 };
32}
33
34impl<'cl, T: CuMsgPayload> CuMsgPack<'cl> for (&'cl CuMsg<T>,) {}
35impl<'cl, T: CuMsgPayload> CuMsgPack<'cl> for &'cl CuMsg<T> {}
36impl<'cl, T: CuMsgPayload> CuMsgPack<'cl> for (&'cl mut CuMsg<T>,) {}
37impl<'cl, T: CuMsgPayload> CuMsgPack<'cl> for &'cl mut CuMsg<T> {}
38impl CuMsgPack<'_> for () {}
39
40impl_cu_msg_pack! {
42 (T1, T2), (T1, T2, T3), (T1, T2, T3, T4), (T1, T2, T3, T4, T5) }
44
45#[macro_export]
48macro_rules! input_msg {
49 ($lifetime:lifetime, $ty:ty) => {
50 &$lifetime CuMsg<$ty>
51 };
52 ($lifetime:lifetime, $($ty:ty),*) => {
53 (
54 $( &$lifetime CuMsg<$ty>, )*
55 )
56 };
57}
58
59#[macro_export]
61macro_rules! output_msg {
62 ($lifetime:lifetime, $ty:ty) => {
63 &$lifetime mut CuMsg<$ty>
64 };
65}
66
67const COMPACT_STRING_CAPACITY: usize = size_of::<String>();
70
71#[derive(Debug, Clone, Default, Serialize, Deserialize, PartialEq, Eq)]
72pub struct CuCompactString(pub CompactString);
73
74impl Encode for CuCompactString {
75 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
76 let CuCompactString(ref compact_string) = self;
77 let bytes = compact_string.as_bytes();
78 bytes.encode(encoder)
79 }
80}
81
82impl<Context> Decode<Context> for CuCompactString {
83 fn decode<D: Decoder>(decoder: &mut D) -> Result<Self, DecodeError> {
84 let bytes = <Vec<u8> as Decode<D::Context>>::decode(decoder)?; let compact_string =
86 CompactString::from_utf8(bytes).map_err(|e| DecodeError::Utf8 { inner: e })?;
87 Ok(CuCompactString(compact_string))
88 }
89}
90
91impl<'de, Context> BorrowDecode<'de, Context> for CuCompactString {
92 fn borrow_decode<D: BorrowDecoder<'de>>(decoder: &mut D) -> Result<Self, DecodeError> {
93 CuCompactString::decode(decoder)
94 }
95}
96
97#[derive(Debug, Clone, bincode::Encode, bincode::Decode, Serialize, Deserialize)]
99pub struct CuMsgMetadata {
100 pub process_time: PartialCuTimeRange,
102 pub tov: Tov,
105 pub status_txt: CuCompactString,
108}
109
110impl CuMsgMetadata {
111 pub fn set_status(&mut self, status: impl ToCompactString) {
112 self.status_txt = CuCompactString(status.to_compact_string());
113 }
114}
115
116impl Display for CuMsgMetadata {
117 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
118 write!(
119 f,
120 "process_time start: {}, process_time end: {}",
121 self.process_time.start, self.process_time.end
122 )
123 }
124}
125
126#[derive(Default, Debug, Clone, bincode::Encode, bincode::Decode, Serialize)]
128pub struct CuMsg<T>
129where
130 T: CuMsgPayload,
131{
132 payload: Option<T>,
134
135 pub metadata: CuMsgMetadata,
137}
138
139impl Default for CuMsgMetadata {
140 fn default() -> Self {
141 CuMsgMetadata {
142 process_time: PartialCuTimeRange::default(),
143 tov: Tov::default(),
144 status_txt: CuCompactString(CompactString::with_capacity(COMPACT_STRING_CAPACITY)),
145 }
146 }
147}
148
149impl<T> CuMsg<T>
150where
151 T: CuMsgPayload,
152{
153 pub fn new(payload: Option<T>) -> Self {
154 CuMsg {
155 payload,
156 metadata: CuMsgMetadata::default(),
157 }
158 }
159 pub fn payload(&self) -> Option<&T> {
160 self.payload.as_ref()
161 }
162
163 pub fn set_payload(&mut self, payload: T) {
164 self.payload = Some(payload);
165 }
166
167 pub fn clear_payload(&mut self) {
168 self.payload = None;
169 }
170
171 pub fn payload_mut(&mut self) -> &mut Option<T> {
172 &mut self.payload
173 }
174}
175
176impl<T> ErasedCuMsg for CuMsg<T>
177where
178 T: CuMsgPayload,
179{
180 fn erased_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
191pub trait Freezable {
194 fn freeze<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
198 Encode::encode(&(), encoder) }
200
201 fn thaw<D: Decoder>(&mut self, _decoder: &mut D) -> Result<(), DecodeError> {
204 Ok(())
205 }
206}
207
208pub struct BincodeAdapter<'a, T: Freezable + ?Sized>(pub &'a T);
211
212impl<'a, T: Freezable + ?Sized> Encode for BincodeAdapter<'a, T> {
213 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
214 self.0.freeze(encoder)
215 }
216}
217
218pub trait CuSrcTask<'cl>: Freezable {
223 type Output: CuMsgPack<'cl>;
224
225 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
228 where
229 Self: Sized;
230
231 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
233 Ok(())
234 }
235
236 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
240 Ok(())
241 }
242
243 fn process(&mut self, clock: &RobotClock, new_msg: Self::Output) -> CuResult<()>;
247
248 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
252 Ok(())
253 }
254
255 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
257 Ok(())
258 }
259}
260
261pub trait CuTask<'cl>: Freezable {
263 type Input: CuMsgPack<'cl>;
264 type Output: CuMsgPack<'cl>;
265
266 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
269 where
270 Self: Sized;
271
272 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
274 Ok(())
275 }
276
277 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
281 Ok(())
282 }
283
284 fn process(
288 &mut self,
289 _clock: &RobotClock,
290 input: Self::Input,
291 output: Self::Output,
292 ) -> CuResult<()>;
293
294 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
298 Ok(())
299 }
300
301 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
303 Ok(())
304 }
305}
306
307pub trait CuSinkTask<'cl>: Freezable {
309 type Input: CuMsgPack<'cl>;
310
311 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
314 where
315 Self: Sized;
316
317 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
319 Ok(())
320 }
321
322 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
326 Ok(())
327 }
328
329 fn process(&mut self, _clock: &RobotClock, input: Self::Input) -> CuResult<()>;
333
334 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
338 Ok(())
339 }
340
341 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
343 Ok(())
344 }
345}
346
347#[cfg(test)]
348mod tests {
349 use super::*;
350 use bincode::{config, decode_from_slice, encode_to_vec};
351
352 #[test]
353 fn test_cucompactstr_encode_decode() {
354 let cstr = CuCompactString(CompactString::from("hello"));
355 let config = config::standard();
356 let encoded = encode_to_vec(&cstr, config).expect("Encoding failed");
357 let (decoded, _): (CuCompactString, usize) =
358 decode_from_slice(&encoded, config).expect("Decoding failed");
359 assert_eq!(cstr.0, decoded.0);
360 }
361}