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::de::DeserializeOwned;
16use serde::{Deserialize, Serialize};
17
18use alloc::format;
19use core::fmt::{Debug, Display, Formatter, Result as FmtResult};
20
21pub trait CuMsgPayload:
24 Default + Debug + Clone + Encode + Decode<()> + Serialize + DeserializeOwned + Sized
25{
26}
27
28pub trait CuMsgPack {}
29
30impl<T> CuMsgPayload for T where
32 T: Default + Debug + Clone + Encode + Decode<()> + Serialize + DeserializeOwned + Sized
33{
34}
35
36macro_rules! impl_cu_msg_pack {
37 ($($name:ident),+) => {
38 impl<'cl, $($name),+> CuMsgPack for ($(&CuMsg<$name>,)+)
39 where
40 $($name: CuMsgPayload),+
41 {}
42 };
43}
44
45impl<T: CuMsgPayload> CuMsgPack for CuMsg<T> {}
46impl<T: CuMsgPayload> CuMsgPack for &CuMsg<T> {}
47impl<T: CuMsgPayload> CuMsgPack for (&CuMsg<T>,) {}
48impl CuMsgPack for () {}
49
50impl_cu_msg_pack!(T1, T2);
52impl_cu_msg_pack!(T1, T2, T3);
53impl_cu_msg_pack!(T1, T2, T3, T4);
54impl_cu_msg_pack!(T1, T2, T3, T4, T5);
55
56#[macro_export]
59macro_rules! input_msg {
60 ($lt:lifetime, $first:ty, $($rest:ty),+) => {
61 ( & $lt CuMsg<$first>, $( & $lt CuMsg<$rest> ),+ )
62 };
63 ($lt:lifetime, $ty:ty) => {
64 CuMsg<$ty> };
66 ($ty:ty) => {
67 CuMsg<$ty>
68 };
69}
70
71#[macro_export]
73macro_rules! output_msg {
74 ($lt:lifetime, $first:ty, $($rest:ty),+) => {
75 ( CuMsg<$first>, $( CuMsg<$rest> ),+ )
76 };
77 ($first:ty, $($rest:ty),+) => {
78 ( CuMsg<$first>, $( CuMsg<$rest> ),+ )
79 };
80 ($ty:ty) => {
81 CuMsg<$ty>
82 };
83 ($lt:lifetime, $ty:ty) => {
84 CuMsg<$ty> };
86}
87
88#[derive(Debug, Clone, bincode::Encode, bincode::Decode, Serialize, Deserialize)]
90pub struct CuMsgMetadata {
91 pub process_time: PartialCuTimeRange,
93 pub status_txt: CuCompactString,
96}
97
98impl Metadata for CuMsgMetadata {}
99
100impl CuMsgMetadata {
101 pub fn set_status(&mut self, status: impl ToCompactString) {
102 self.status_txt = CuCompactString(status.to_compact_string());
103 }
104}
105
106impl CuMsgMetadataTrait for CuMsgMetadata {
107 fn process_time(&self) -> PartialCuTimeRange {
108 self.process_time
109 }
110
111 fn status_txt(&self) -> &CuCompactString {
112 &self.status_txt
113 }
114}
115
116impl Display for CuMsgMetadata {
117 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
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, Deserialize)]
128#[serde(bound(
129 serialize = "T: Serialize, M: Serialize",
130 deserialize = "T: DeserializeOwned, M: DeserializeOwned"
131))]
132pub struct CuStampedData<T, M>
133where
134 T: CuMsgPayload,
135 M: Metadata,
136{
137 payload: Option<T>,
139
140 pub tov: Tov,
143
144 pub metadata: M,
146}
147
148impl Default for CuMsgMetadata {
149 fn default() -> Self {
150 CuMsgMetadata {
151 process_time: PartialCuTimeRange::default(),
152 status_txt: CuCompactString(CompactString::with_capacity(COMPACT_STRING_CAPACITY)),
153 }
154 }
155}
156
157impl<T, M> CuStampedData<T, M>
158where
159 T: CuMsgPayload,
160 M: Metadata,
161{
162 pub fn new(payload: Option<T>) -> Self {
163 CuStampedData {
164 payload,
165 tov: Tov::default(),
166 metadata: M::default(),
167 }
168 }
169 pub fn payload(&self) -> Option<&T> {
170 self.payload.as_ref()
171 }
172
173 pub fn set_payload(&mut self, payload: T) {
174 self.payload = Some(payload);
175 }
176
177 pub fn clear_payload(&mut self) {
178 self.payload = None;
179 }
180
181 pub fn payload_mut(&mut self) -> &mut Option<T> {
182 &mut self.payload
183 }
184}
185
186impl<T, M> ErasedCuStampedData for CuStampedData<T, M>
187where
188 T: CuMsgPayload,
189 M: CuMsgMetadataTrait + Metadata,
190{
191 fn payload(&self) -> Option<&dyn erased_serde::Serialize> {
192 self.payload
193 .as_ref()
194 .map(|p| p as &dyn erased_serde::Serialize)
195 }
196
197 fn tov(&self) -> Tov {
198 self.tov
199 }
200
201 fn metadata(&self) -> &dyn CuMsgMetadataTrait {
202 &self.metadata
203 }
204}
205
206pub type CuMsg<T> = CuStampedData<T, CuMsgMetadata>;
209
210impl<T: CuMsgPayload> CuStampedData<T, CuMsgMetadata> {
211 pub unsafe fn assume_payload<U: CuMsgPayload>(&self) -> &CuMsg<U> {
218 unsafe { &*(self as *const CuMsg<T> as *const CuMsg<U>) }
219 }
220
221 pub unsafe fn assume_payload_mut<U: CuMsgPayload>(&mut self) -> &mut CuMsg<U> {
228 unsafe { &mut *(self as *mut CuMsg<T> as *mut CuMsg<U>) }
229 }
230}
231
232impl<T: CuMsgPayload + 'static> CuStampedData<T, CuMsgMetadata> {
233 fn downcast_err<U: CuMsgPayload + 'static>() -> CuError {
234 CuError::from(format!(
235 "CuMsg payload mismatch: {} cannot be reinterpreted as {}",
236 type_name::<T>(),
237 type_name::<U>()
238 ))
239 }
240
241 pub fn downcast_ref<U: CuMsgPayload + 'static>(&self) -> CuResult<&CuMsg<U>> {
243 if TypeId::of::<T>() == TypeId::of::<U>() {
244 Ok(unsafe { self.assume_payload::<U>() })
246 } else {
247 Err(Self::downcast_err::<U>())
248 }
249 }
250
251 pub fn downcast_mut<U: CuMsgPayload + 'static>(&mut self) -> CuResult<&mut CuMsg<U>> {
253 if TypeId::of::<T>() == TypeId::of::<U>() {
254 Ok(unsafe { self.assume_payload_mut::<U>() })
255 } else {
256 Err(Self::downcast_err::<U>())
257 }
258 }
259}
260
261pub trait Freezable {
264 fn freeze<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
268 Encode::encode(&(), encoder) }
270
271 fn thaw<D: Decoder>(&mut self, _decoder: &mut D) -> Result<(), DecodeError> {
274 Ok(())
275 }
276}
277
278pub struct BincodeAdapter<'a, T: Freezable + ?Sized>(pub &'a T);
281
282impl<'a, T: Freezable + ?Sized> Encode for BincodeAdapter<'a, T> {
283 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
284 self.0.freeze(encoder)
285 }
286}
287
288pub trait CuSrcTask: Freezable {
293 type Output<'m>: CuMsgPayload;
294 type Resources<'r>;
296
297 fn new(_config: Option<&ComponentConfig>, _resources: Self::Resources<'_>) -> CuResult<Self>
300 where
301 Self: Sized;
302
303 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
305 Ok(())
306 }
307
308 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
312 Ok(())
313 }
314
315 fn process<'o>(&mut self, clock: &RobotClock, new_msg: &mut Self::Output<'o>) -> CuResult<()>;
319
320 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
324 Ok(())
325 }
326
327 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
329 Ok(())
330 }
331}
332
333pub trait CuTask: Freezable {
335 type Input<'m>: CuMsgPack;
336 type Output<'m>: CuMsgPayload;
337 type Resources<'r>;
339
340 fn new(_config: Option<&ComponentConfig>, _resources: Self::Resources<'_>) -> CuResult<Self>
343 where
344 Self: Sized;
345
346 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
348 Ok(())
349 }
350
351 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
355 Ok(())
356 }
357
358 fn process<'i, 'o>(
362 &mut self,
363 _clock: &RobotClock,
364 input: &Self::Input<'i>,
365 output: &mut Self::Output<'o>,
366 ) -> CuResult<()>;
367
368 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
372 Ok(())
373 }
374
375 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
377 Ok(())
378 }
379}
380
381pub trait CuSinkTask: Freezable {
383 type Input<'m>: CuMsgPack;
384 type Resources<'r>;
386
387 fn new(_config: Option<&ComponentConfig>, _resources: Self::Resources<'_>) -> CuResult<Self>
390 where
391 Self: Sized;
392
393 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
395 Ok(())
396 }
397
398 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
402 Ok(())
403 }
404
405 fn process<'i>(&mut self, _clock: &RobotClock, input: &Self::Input<'i>) -> CuResult<()>;
409
410 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
414 Ok(())
415 }
416
417 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
419 Ok(())
420 }
421}
422
423#[cfg(test)]
424mod tests {
425 use super::*;
426 use bincode::{config, decode_from_slice, encode_to_vec};
427
428 #[test]
429 fn test_cucompactstr_encode_decode() {
430 let cstr = CuCompactString(CompactString::from("hello"));
431 let config = config::standard();
432 let encoded = encode_to_vec(&cstr, config).expect("Encoding failed");
433 let (decoded, _): (CuCompactString, usize) =
434 decode_from_slice(&encoded, config).expect("Decoding failed");
435 assert_eq!(cstr.0, decoded.0);
436 }
437}