1use crate::config::ComponentConfig;
5use crate::reflect::Reflect;
6use bincode::de::{Decode, Decoder};
7use bincode::enc::{Encode, Encoder};
8use bincode::error::{DecodeError, EncodeError};
9use compact_str::{CompactString, ToCompactString};
10use core::any::{TypeId, type_name};
11use cu29_clock::{PartialCuTimeRange, RobotClock, Tov};
12use cu29_traits::{
13 COMPACT_STRING_CAPACITY, CuCompactString, CuError, CuMsgMetadataTrait, CuResult,
14 ErasedCuStampedData, Metadata,
15};
16use serde::de::DeserializeOwned;
17use serde::{Deserialize, Serialize};
18
19use alloc::format;
20use core::fmt::{Debug, Display, Formatter, Result as FmtResult};
21
22pub trait CuMsgPayload:
25 Default + Debug + Clone + Encode + Decode<()> + Serialize + DeserializeOwned + Sized
26{
27}
28
29pub trait CuMsgPack {}
30
31impl<T> CuMsgPayload for T where
33 T: Default + Debug + Clone + Encode + Decode<()> + Serialize + DeserializeOwned + Sized
34{
35}
36
37macro_rules! impl_cu_msg_pack {
38 ($($name:ident),+) => {
39 impl<'cl, $($name),+> CuMsgPack for ($(&CuMsg<$name>,)+)
40 where
41 $($name: CuMsgPayload),+
42 {}
43 };
44}
45
46impl<T: CuMsgPayload> CuMsgPack for CuMsg<T> {}
47impl<T: CuMsgPayload> CuMsgPack for &CuMsg<T> {}
48impl<T: CuMsgPayload> CuMsgPack for (&CuMsg<T>,) {}
49impl CuMsgPack for () {}
50
51impl_cu_msg_pack!(T1, T2);
53impl_cu_msg_pack!(T1, T2, T3);
54impl_cu_msg_pack!(T1, T2, T3, T4);
55impl_cu_msg_pack!(T1, T2, T3, T4, T5);
56
57#[macro_export]
60macro_rules! input_msg {
61 ($lt:lifetime, $first:ty, $($rest:ty),+) => {
62 ( & $lt CuMsg<$first>, $( & $lt CuMsg<$rest> ),+ )
63 };
64 ($lt:lifetime, $ty:ty) => {
65 CuMsg<$ty> };
67 ($ty:ty) => {
68 CuMsg<$ty>
69 };
70}
71
72#[macro_export]
74macro_rules! output_msg {
75 ($lt:lifetime, $first:ty, $($rest:ty),+) => {
76 ( CuMsg<$first>, $( CuMsg<$rest> ),+ )
77 };
78 ($first:ty, $($rest:ty),+) => {
79 ( CuMsg<$first>, $( CuMsg<$rest> ),+ )
80 };
81 ($ty:ty) => {
82 CuMsg<$ty>
83 };
84 ($lt:lifetime, $ty:ty) => {
85 CuMsg<$ty> };
87}
88
89#[derive(Debug, Clone, bincode::Encode, bincode::Decode, Serialize, Deserialize)]
91pub struct CuMsgMetadata {
92 pub process_time: PartialCuTimeRange,
94 pub status_txt: CuCompactString,
97}
98
99impl Metadata for CuMsgMetadata {}
100
101impl CuMsgMetadata {
102 pub fn set_status(&mut self, status: impl ToCompactString) {
103 self.status_txt = CuCompactString(status.to_compact_string());
104 }
105}
106
107impl CuMsgMetadataTrait for CuMsgMetadata {
108 fn process_time(&self) -> PartialCuTimeRange {
109 self.process_time
110 }
111
112 fn status_txt(&self) -> &CuCompactString {
113 &self.status_txt
114 }
115}
116
117impl Display for CuMsgMetadata {
118 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
119 write!(
120 f,
121 "process_time start: {}, process_time end: {}",
122 self.process_time.start, self.process_time.end
123 )
124 }
125}
126
127#[derive(Default, Debug, Clone, bincode::Encode, bincode::Decode, Serialize, Deserialize)]
129#[serde(bound(
130 serialize = "T: Serialize, M: Serialize",
131 deserialize = "T: DeserializeOwned, M: DeserializeOwned"
132))]
133pub struct CuStampedData<T, M>
134where
135 T: CuMsgPayload,
136 M: Metadata,
137{
138 payload: Option<T>,
140
141 pub tov: Tov,
144
145 pub metadata: M,
147}
148
149impl Default for CuMsgMetadata {
150 fn default() -> Self {
151 CuMsgMetadata {
152 process_time: PartialCuTimeRange::default(),
153 status_txt: CuCompactString(CompactString::with_capacity(COMPACT_STRING_CAPACITY)),
154 }
155 }
156}
157
158impl<T, M> CuStampedData<T, M>
159where
160 T: CuMsgPayload,
161 M: Metadata,
162{
163 pub fn new(payload: Option<T>) -> Self {
164 CuStampedData {
165 payload,
166 tov: Tov::default(),
167 metadata: M::default(),
168 }
169 }
170 pub fn payload(&self) -> Option<&T> {
171 self.payload.as_ref()
172 }
173
174 pub fn set_payload(&mut self, payload: T) {
175 self.payload = Some(payload);
176 }
177
178 pub fn clear_payload(&mut self) {
179 self.payload = None;
180 }
181
182 pub fn payload_mut(&mut self) -> &mut Option<T> {
183 &mut self.payload
184 }
185}
186
187impl<T, M> ErasedCuStampedData for CuStampedData<T, M>
188where
189 T: CuMsgPayload,
190 M: CuMsgMetadataTrait + Metadata,
191{
192 fn payload(&self) -> Option<&dyn erased_serde::Serialize> {
193 self.payload
194 .as_ref()
195 .map(|p| p as &dyn erased_serde::Serialize)
196 }
197
198 fn tov(&self) -> Tov {
199 self.tov
200 }
201
202 fn metadata(&self) -> &dyn CuMsgMetadataTrait {
203 &self.metadata
204 }
205}
206
207pub type CuMsg<T> = CuStampedData<T, CuMsgMetadata>;
210
211impl<T: CuMsgPayload> CuStampedData<T, CuMsgMetadata> {
212 pub unsafe fn assume_payload<U: CuMsgPayload>(&self) -> &CuMsg<U> {
219 unsafe { &*(self as *const CuMsg<T> as *const CuMsg<U>) }
221 }
222
223 pub unsafe fn assume_payload_mut<U: CuMsgPayload>(&mut self) -> &mut CuMsg<U> {
230 unsafe { &mut *(self as *mut CuMsg<T> as *mut CuMsg<U>) }
232 }
233}
234
235impl<T: CuMsgPayload + 'static> CuStampedData<T, CuMsgMetadata> {
236 fn downcast_err<U: CuMsgPayload + 'static>() -> CuError {
237 CuError::from(format!(
238 "CuMsg payload mismatch: {} cannot be reinterpreted as {}",
239 type_name::<T>(),
240 type_name::<U>()
241 ))
242 }
243
244 pub fn downcast_ref<U: CuMsgPayload + 'static>(&self) -> CuResult<&CuMsg<U>> {
246 if TypeId::of::<T>() == TypeId::of::<U>() {
247 Ok(unsafe { self.assume_payload::<U>() })
249 } else {
250 Err(Self::downcast_err::<U>())
251 }
252 }
253
254 pub fn downcast_mut<U: CuMsgPayload + 'static>(&mut self) -> CuResult<&mut CuMsg<U>> {
256 if TypeId::of::<T>() == TypeId::of::<U>() {
257 Ok(unsafe { self.assume_payload_mut::<U>() })
259 } else {
260 Err(Self::downcast_err::<U>())
261 }
262 }
263}
264
265pub trait Freezable {
268 fn freeze<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
272 Encode::encode(&(), encoder) }
274
275 fn thaw<D: Decoder>(&mut self, _decoder: &mut D) -> Result<(), DecodeError> {
278 Ok(())
279 }
280}
281
282pub struct BincodeAdapter<'a, T: Freezable + ?Sized>(pub &'a T);
285
286impl<'a, T: Freezable + ?Sized> Encode for BincodeAdapter<'a, T> {
287 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
288 self.0.freeze(encoder)
289 }
290}
291
292pub trait CuSrcTask: Freezable + Reflect {
297 type Output<'m>: CuMsgPayload;
298 type Resources<'r>;
300
301 fn new(_config: Option<&ComponentConfig>, _resources: Self::Resources<'_>) -> CuResult<Self>
304 where
305 Self: Sized;
306
307 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
309 Ok(())
310 }
311
312 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
316 Ok(())
317 }
318
319 fn process<'o>(&mut self, clock: &RobotClock, new_msg: &mut Self::Output<'o>) -> CuResult<()>;
323
324 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
328 Ok(())
329 }
330
331 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
333 Ok(())
334 }
335}
336
337pub trait CuTask: Freezable + Reflect {
339 type Input<'m>: CuMsgPack;
340 type Output<'m>: CuMsgPayload;
341 type Resources<'r>;
343
344 fn new(_config: Option<&ComponentConfig>, _resources: Self::Resources<'_>) -> CuResult<Self>
347 where
348 Self: Sized;
349
350 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
352 Ok(())
353 }
354
355 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
359 Ok(())
360 }
361
362 fn process<'i, 'o>(
366 &mut self,
367 _clock: &RobotClock,
368 input: &Self::Input<'i>,
369 output: &mut Self::Output<'o>,
370 ) -> CuResult<()>;
371
372 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
376 Ok(())
377 }
378
379 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
381 Ok(())
382 }
383}
384
385pub trait CuSinkTask: Freezable + Reflect {
387 type Input<'m>: CuMsgPack;
388 type Resources<'r>;
390
391 fn new(_config: Option<&ComponentConfig>, _resources: Self::Resources<'_>) -> CuResult<Self>
394 where
395 Self: Sized;
396
397 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
399 Ok(())
400 }
401
402 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
406 Ok(())
407 }
408
409 fn process<'i>(&mut self, _clock: &RobotClock, input: &Self::Input<'i>) -> CuResult<()>;
413
414 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
418 Ok(())
419 }
420
421 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
423 Ok(())
424 }
425}
426
427#[cfg(test)]
428mod tests {
429 use super::*;
430 use bincode::{config, decode_from_slice, encode_to_vec};
431
432 #[test]
433 fn test_cucompactstr_encode_decode() {
434 let cstr = CuCompactString(CompactString::from("hello"));
435 let config = config::standard();
436 let encoded = encode_to_vec(&cstr, config).expect("Encoding failed");
437 let (decoded, _): (CuCompactString, usize) =
438 decode_from_slice(&encoded, config).expect("Decoding failed");
439 assert_eq!(cstr.0, decoded.0);
440 }
441}