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 core::any::{TypeId, type_name};
10use cu29_clock::{PartialCuTimeRange, RobotClock, Tov};
11use cu29_traits::{
12 COMPACT_STRING_CAPACITY, CuCompactString, CuError, CuMsgMetadataTrait, CuResult,
13 ErasedCuStampedData, Metadata,
14};
15use serde::{Deserialize, Serialize};
16
17use alloc::format;
18use core::fmt::{Debug, Display, Formatter, Result as FmtResult};
19
20pub trait CuMsgPayload: Default + Debug + Clone + Encode + Decode<()> + Serialize + Sized {}
23
24pub trait CuMsgPack {}
25
26impl<T: Default + Debug + Clone + Encode + Decode<()> + Serialize + Sized> CuMsgPayload for T {}
28
29macro_rules! impl_cu_msg_pack {
30 ($($name:ident),+) => {
31 impl<'cl, $($name),+> CuMsgPack for ($(&CuMsg<$name>,)+)
32 where
33 $($name: CuMsgPayload),+
34 {}
35 };
36}
37
38impl<T: CuMsgPayload> CuMsgPack for CuMsg<T> {}
39impl<T: CuMsgPayload> CuMsgPack for &CuMsg<T> {}
40impl<T: CuMsgPayload> CuMsgPack for (&CuMsg<T>,) {}
41impl CuMsgPack for () {}
42
43impl_cu_msg_pack!(T1, T2);
45impl_cu_msg_pack!(T1, T2, T3);
46impl_cu_msg_pack!(T1, T2, T3, T4);
47impl_cu_msg_pack!(T1, T2, T3, T4, T5);
48
49#[macro_export]
52macro_rules! input_msg {
53 ($lt:lifetime, $first:ty, $($rest:ty),+) => {
54 ( & $lt CuMsg<$first>, $( & $lt CuMsg<$rest> ),+ )
55 };
56 ($lt:lifetime, $ty:ty) => {
57 CuMsg<$ty> };
59 ($ty:ty) => {
60 CuMsg<$ty>
61 };
62}
63
64#[macro_export]
66macro_rules! output_msg {
67 ($ty:ty) => {
68 CuMsg<$ty>
69 };
70 ($lt:lifetime, $ty:ty) => {
71 CuMsg<$ty> };
73}
74
75#[derive(Debug, Clone, bincode::Encode, bincode::Decode, Serialize, Deserialize)]
77pub struct CuMsgMetadata {
78 pub process_time: PartialCuTimeRange,
80 pub status_txt: CuCompactString,
83}
84
85impl Metadata for CuMsgMetadata {}
86
87impl CuMsgMetadata {
88 pub fn set_status(&mut self, status: impl ToCompactString) {
89 self.status_txt = CuCompactString(status.to_compact_string());
90 }
91}
92
93impl CuMsgMetadataTrait for CuMsgMetadata {
94 fn process_time(&self) -> PartialCuTimeRange {
95 self.process_time
96 }
97
98 fn status_txt(&self) -> &CuCompactString {
99 &self.status_txt
100 }
101}
102
103impl Display for CuMsgMetadata {
104 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
105 write!(
106 f,
107 "process_time start: {}, process_time end: {}",
108 self.process_time.start, self.process_time.end
109 )
110 }
111}
112
113#[derive(Default, Debug, Clone, bincode::Encode, bincode::Decode, Serialize)]
115pub struct CuStampedData<T, M>
116where
117 T: CuMsgPayload,
118 M: Metadata,
119{
120 payload: Option<T>,
122
123 pub tov: Tov,
126
127 pub metadata: M,
129}
130
131impl Default for CuMsgMetadata {
132 fn default() -> Self {
133 CuMsgMetadata {
134 process_time: PartialCuTimeRange::default(),
135 status_txt: CuCompactString(CompactString::with_capacity(COMPACT_STRING_CAPACITY)),
136 }
137 }
138}
139
140impl<T, M> CuStampedData<T, M>
141where
142 T: CuMsgPayload,
143 M: Metadata,
144{
145 pub fn new(payload: Option<T>) -> Self {
146 CuStampedData {
147 payload,
148 tov: Tov::default(),
149 metadata: M::default(),
150 }
151 }
152 pub fn payload(&self) -> Option<&T> {
153 self.payload.as_ref()
154 }
155
156 pub fn set_payload(&mut self, payload: T) {
157 self.payload = Some(payload);
158 }
159
160 pub fn clear_payload(&mut self) {
161 self.payload = None;
162 }
163
164 pub fn payload_mut(&mut self) -> &mut Option<T> {
165 &mut self.payload
166 }
167}
168
169impl<T, M> ErasedCuStampedData for CuStampedData<T, M>
170where
171 T: CuMsgPayload,
172 M: CuMsgMetadataTrait + Metadata,
173{
174 fn payload(&self) -> Option<&dyn erased_serde::Serialize> {
175 self.payload
176 .as_ref()
177 .map(|p| p as &dyn erased_serde::Serialize)
178 }
179
180 fn tov(&self) -> Tov {
181 self.tov
182 }
183
184 fn metadata(&self) -> &dyn CuMsgMetadataTrait {
185 &self.metadata
186 }
187}
188
189pub type CuMsg<T> = CuStampedData<T, CuMsgMetadata>;
192
193impl<T: CuMsgPayload> CuStampedData<T, CuMsgMetadata> {
194 pub unsafe fn assume_payload<U: CuMsgPayload>(&self) -> &CuMsg<U> {
201 unsafe { &*(self as *const CuMsg<T> as *const CuMsg<U>) }
202 }
203
204 pub unsafe fn assume_payload_mut<U: CuMsgPayload>(&mut self) -> &mut CuMsg<U> {
211 unsafe { &mut *(self as *mut CuMsg<T> as *mut CuMsg<U>) }
212 }
213}
214
215impl<T: CuMsgPayload + 'static> CuStampedData<T, CuMsgMetadata> {
216 fn downcast_err<U: CuMsgPayload + 'static>() -> CuError {
217 CuError::from(format!(
218 "CuMsg payload mismatch: {} cannot be reinterpreted as {}",
219 type_name::<T>(),
220 type_name::<U>()
221 ))
222 }
223
224 pub fn downcast_ref<U: CuMsgPayload + 'static>(&self) -> CuResult<&CuMsg<U>> {
226 if TypeId::of::<T>() == TypeId::of::<U>() {
227 Ok(unsafe { self.assume_payload::<U>() })
229 } else {
230 Err(Self::downcast_err::<U>())
231 }
232 }
233
234 pub fn downcast_mut<U: CuMsgPayload + 'static>(&mut self) -> CuResult<&mut CuMsg<U>> {
236 if TypeId::of::<T>() == TypeId::of::<U>() {
237 Ok(unsafe { self.assume_payload_mut::<U>() })
238 } else {
239 Err(Self::downcast_err::<U>())
240 }
241 }
242}
243
244pub trait Freezable {
247 fn freeze<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
251 Encode::encode(&(), encoder) }
253
254 fn thaw<D: Decoder>(&mut self, _decoder: &mut D) -> Result<(), DecodeError> {
257 Ok(())
258 }
259}
260
261pub struct BincodeAdapter<'a, T: Freezable + ?Sized>(pub &'a T);
264
265impl<'a, T: Freezable + ?Sized> Encode for BincodeAdapter<'a, T> {
266 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
267 self.0.freeze(encoder)
268 }
269}
270
271pub trait CuSrcTask: Freezable {
276 type Output<'m>: CuMsgPayload;
277 type Resources<'r>;
279
280 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
282 where
283 Self: Sized,
284 for<'r> Self::Resources<'r>: Default,
285 {
286 Self::new_with(_config, Default::default())
287 }
288
289 fn new_with(
292 _config: Option<&ComponentConfig>,
293 _resources: Self::Resources<'_>,
294 ) -> CuResult<Self>
295 where
296 Self: Sized;
297
298 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
300 Ok(())
301 }
302
303 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
307 Ok(())
308 }
309
310 fn process<'o>(&mut self, clock: &RobotClock, new_msg: &mut Self::Output<'o>) -> CuResult<()>;
314
315 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
319 Ok(())
320 }
321
322 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
324 Ok(())
325 }
326}
327
328pub trait CuTask: Freezable {
330 type Input<'m>: CuMsgPack;
331 type Output<'m>: CuMsgPayload;
332 type Resources<'r>;
334
335 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
337 where
338 Self: Sized,
339 for<'r> Self::Resources<'r>: Default,
340 {
341 Self::new_with(_config, Default::default())
342 }
343
344 fn new_with(
347 _config: Option<&ComponentConfig>,
348 _resources: Self::Resources<'_>,
349 ) -> CuResult<Self>
350 where
351 Self: Sized;
352
353 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
355 Ok(())
356 }
357
358 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
362 Ok(())
363 }
364
365 fn process<'i, 'o>(
369 &mut self,
370 _clock: &RobotClock,
371 input: &Self::Input<'i>,
372 output: &mut Self::Output<'o>,
373 ) -> CuResult<()>;
374
375 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
379 Ok(())
380 }
381
382 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
384 Ok(())
385 }
386}
387
388pub trait CuSinkTask: Freezable {
390 type Input<'m>: CuMsgPack;
391 type Resources<'r>;
393
394 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
396 where
397 Self: Sized,
398 for<'r> Self::Resources<'r>: Default,
399 {
400 Self::new_with(_config, Default::default())
401 }
402
403 fn new_with(
406 _config: Option<&ComponentConfig>,
407 _resources: Self::Resources<'_>,
408 ) -> CuResult<Self>
409 where
410 Self: Sized;
411
412 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
414 Ok(())
415 }
416
417 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
421 Ok(())
422 }
423
424 fn process<'i>(&mut self, _clock: &RobotClock, input: &Self::Input<'i>) -> CuResult<()>;
428
429 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
433 Ok(())
434 }
435
436 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
438 Ok(())
439 }
440}
441
442#[cfg(test)]
443mod tests {
444 use super::*;
445 use bincode::{config, decode_from_slice, encode_to_vec};
446
447 #[test]
448 fn test_cucompactstr_encode_decode() {
449 let cstr = CuCompactString(CompactString::from("hello"));
450 let config = config::standard();
451 let encoded = encode_to_vec(&cstr, config).expect("Encoding failed");
452 let (decoded, _): (CuCompactString, usize) =
453 decode_from_slice(&encoded, config).expect("Decoding failed");
454 assert_eq!(cstr.0, decoded.0);
455 }
456}