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::{type_name, TypeId};
10use cu29_clock::{PartialCuTimeRange, RobotClock, Tov};
11use cu29_traits::{
12 CuCompactString, CuError, CuMsgMetadataTrait, CuResult, ErasedCuStampedData, Metadata,
13 COMPACT_STRING_CAPACITY,
14};
15use serde::{Deserialize, Serialize};
16
17#[cfg(not(feature = "std"))]
18mod imp {
19 pub use alloc::fmt::Result as FmtResult;
20 pub use alloc::fmt::{Debug, Display, Formatter};
21 pub use alloc::format;
22}
23
24#[cfg(feature = "std")]
25mod imp {
26 pub use std::fmt::Result as FmtResult;
27 pub use std::fmt::{Debug, Display, Formatter};
28}
29
30use imp::*;
31
32pub trait CuMsgPayload: Default + Debug + Clone + Encode + Decode<()> + Serialize + Sized {}
35
36pub trait CuMsgPack {}
37
38impl<T: Default + Debug + Clone + Encode + Decode<()> + Serialize + Sized> CuMsgPayload for T {}
40
41macro_rules! impl_cu_msg_pack {
42 ($($name:ident),+) => {
43 impl<'cl, $($name),+> CuMsgPack for ($(&CuMsg<$name>,)+)
44 where
45 $($name: CuMsgPayload),+
46 {}
47 };
48}
49
50impl<T: CuMsgPayload> CuMsgPack for CuMsg<T> {}
51impl<T: CuMsgPayload> CuMsgPack for &CuMsg<T> {}
52impl<T: CuMsgPayload> CuMsgPack for (&CuMsg<T>,) {}
53impl CuMsgPack for () {}
54
55impl_cu_msg_pack!(T1, T2);
57impl_cu_msg_pack!(T1, T2, T3);
58impl_cu_msg_pack!(T1, T2, T3, T4);
59impl_cu_msg_pack!(T1, T2, T3, T4, T5);
60
61#[macro_export]
64macro_rules! input_msg {
65 ($lt:lifetime, $first:ty, $($rest:ty),+) => {
66 ( & $lt CuMsg<$first>, $( & $lt CuMsg<$rest> ),+ )
67 };
68 ($lt:lifetime, $ty:ty) => {
69 CuMsg<$ty> };
71 ($ty:ty) => {
72 CuMsg<$ty>
73 };
74}
75
76#[macro_export]
78macro_rules! output_msg {
79 ($ty:ty) => {
80 CuMsg<$ty>
81 };
82 ($lt:lifetime, $ty:ty) => {
83 CuMsg<$ty> };
85}
86
87#[derive(Debug, Clone, bincode::Encode, bincode::Decode, Serialize, Deserialize)]
89pub struct CuMsgMetadata {
90 pub process_time: PartialCuTimeRange,
92 pub status_txt: CuCompactString,
95}
96
97impl Metadata for CuMsgMetadata {}
98
99impl CuMsgMetadata {
100 pub fn set_status(&mut self, status: impl ToCompactString) {
101 self.status_txt = CuCompactString(status.to_compact_string());
102 }
103}
104
105impl CuMsgMetadataTrait for CuMsgMetadata {
106 fn process_time(&self) -> PartialCuTimeRange {
107 self.process_time
108 }
109
110 fn status_txt(&self) -> &CuCompactString {
111 &self.status_txt
112 }
113}
114
115impl Display for CuMsgMetadata {
116 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
117 write!(
118 f,
119 "process_time start: {}, process_time end: {}",
120 self.process_time.start, self.process_time.end
121 )
122 }
123}
124
125#[derive(Default, Debug, Clone, bincode::Encode, bincode::Decode, Serialize)]
127pub struct CuStampedData<T, M>
128where
129 T: CuMsgPayload,
130 M: Metadata,
131{
132 payload: Option<T>,
134
135 pub tov: Tov,
138
139 pub metadata: M,
141}
142
143impl Default for CuMsgMetadata {
144 fn default() -> Self {
145 CuMsgMetadata {
146 process_time: PartialCuTimeRange::default(),
147 status_txt: CuCompactString(CompactString::with_capacity(COMPACT_STRING_CAPACITY)),
148 }
149 }
150}
151
152impl<T, M> CuStampedData<T, M>
153where
154 T: CuMsgPayload,
155 M: Metadata,
156{
157 pub fn new(payload: Option<T>) -> Self {
158 CuStampedData {
159 payload,
160 tov: Tov::default(),
161 metadata: M::default(),
162 }
163 }
164 pub fn payload(&self) -> Option<&T> {
165 self.payload.as_ref()
166 }
167
168 pub fn set_payload(&mut self, payload: T) {
169 self.payload = Some(payload);
170 }
171
172 pub fn clear_payload(&mut self) {
173 self.payload = None;
174 }
175
176 pub fn payload_mut(&mut self) -> &mut Option<T> {
177 &mut self.payload
178 }
179}
180
181impl<T, M> ErasedCuStampedData for CuStampedData<T, M>
182where
183 T: CuMsgPayload,
184 M: CuMsgMetadataTrait + Metadata,
185{
186 fn payload(&self) -> Option<&dyn erased_serde::Serialize> {
187 self.payload
188 .as_ref()
189 .map(|p| p as &dyn erased_serde::Serialize)
190 }
191
192 fn tov(&self) -> Tov {
193 self.tov
194 }
195
196 fn metadata(&self) -> &dyn CuMsgMetadataTrait {
197 &self.metadata
198 }
199}
200
201pub type CuMsg<T> = CuStampedData<T, CuMsgMetadata>;
204
205impl<T: CuMsgPayload> CuStampedData<T, CuMsgMetadata> {
206 pub unsafe fn assume_payload<U: CuMsgPayload>(&self) -> &CuMsg<U> {
213 &*(self as *const CuMsg<T> as *const CuMsg<U>)
214 }
215
216 pub unsafe fn assume_payload_mut<U: CuMsgPayload>(&mut self) -> &mut CuMsg<U> {
223 &mut *(self as *mut CuMsg<T> as *mut CuMsg<U>)
224 }
225}
226
227impl<T: CuMsgPayload + 'static> CuStampedData<T, CuMsgMetadata> {
228 fn downcast_err<U: CuMsgPayload + 'static>() -> CuError {
229 CuError::from(format!(
230 "CuMsg payload mismatch: {} cannot be reinterpreted as {}",
231 type_name::<T>(),
232 type_name::<U>()
233 ))
234 }
235
236 pub fn downcast_ref<U: CuMsgPayload + 'static>(&self) -> CuResult<&CuMsg<U>> {
238 if TypeId::of::<T>() == TypeId::of::<U>() {
239 Ok(unsafe { self.assume_payload::<U>() })
241 } else {
242 Err(Self::downcast_err::<U>())
243 }
244 }
245
246 pub fn downcast_mut<U: CuMsgPayload + 'static>(&mut self) -> CuResult<&mut CuMsg<U>> {
248 if TypeId::of::<T>() == TypeId::of::<U>() {
249 Ok(unsafe { self.assume_payload_mut::<U>() })
250 } else {
251 Err(Self::downcast_err::<U>())
252 }
253 }
254}
255
256pub trait Freezable {
259 fn freeze<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
263 Encode::encode(&(), encoder) }
265
266 fn thaw<D: Decoder>(&mut self, _decoder: &mut D) -> Result<(), DecodeError> {
269 Ok(())
270 }
271}
272
273pub struct BincodeAdapter<'a, T: Freezable + ?Sized>(pub &'a T);
276
277impl<'a, T: Freezable + ?Sized> Encode for BincodeAdapter<'a, T> {
278 fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
279 self.0.freeze(encoder)
280 }
281}
282
283pub trait CuSrcTask: Freezable {
288 type Output<'m>: CuMsgPayload;
289
290 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
293 where
294 Self: Sized;
295
296 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
298 Ok(())
299 }
300
301 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
305 Ok(())
306 }
307
308 fn process<'o>(&mut self, clock: &RobotClock, new_msg: &mut Self::Output<'o>) -> CuResult<()>;
312
313 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
317 Ok(())
318 }
319
320 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
322 Ok(())
323 }
324}
325
326pub trait CuTask: Freezable {
328 type Input<'m>: CuMsgPack;
329 type Output<'m>: CuMsgPayload;
330
331 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
334 where
335 Self: Sized;
336
337 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
339 Ok(())
340 }
341
342 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
346 Ok(())
347 }
348
349 fn process<'i, 'o>(
353 &mut self,
354 _clock: &RobotClock,
355 input: &Self::Input<'i>,
356 output: &mut Self::Output<'o>,
357 ) -> CuResult<()>;
358
359 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
363 Ok(())
364 }
365
366 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
368 Ok(())
369 }
370}
371
372pub trait CuSinkTask: Freezable {
374 type Input<'m>: CuMsgPack;
375
376 fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
379 where
380 Self: Sized;
381
382 fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
384 Ok(())
385 }
386
387 fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
391 Ok(())
392 }
393
394 fn process<'i>(&mut self, _clock: &RobotClock, input: &Self::Input<'i>) -> CuResult<()>;
398
399 fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
403 Ok(())
404 }
405
406 fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
408 Ok(())
409 }
410}
411
412#[cfg(test)]
413mod tests {
414 use super::*;
415 use bincode::{config, decode_from_slice, encode_to_vec};
416
417 #[test]
418 fn test_cucompactstr_encode_decode() {
419 let cstr = CuCompactString(CompactString::from("hello"));
420 let config = config::standard();
421 let encoded = encode_to_vec(&cstr, config).expect("Encoding failed");
422 let (decoded, _): (CuCompactString, usize) =
423 decode_from_slice(&encoded, config).expect("Decoding failed");
424 assert_eq!(cstr.0, decoded.0);
425 }
426}