use crate::config::ComponentConfig;
use bincode::de::{Decode, Decoder};
use bincode::enc::{Encode, Encoder};
use bincode::error::{DecodeError, EncodeError};
use compact_str::{CompactString, ToCompactString};
use cu29_clock::{PartialCuTimeRange, RobotClock, Tov};
use cu29_traits::{
CuCompactString, CuMsgMetadataTrait, CuResult, ErasedCuStampedData, Metadata,
COMPACT_STRING_CAPACITY,
};
use serde::{Deserialize, Serialize};
#[cfg(not(feature = "std"))]
mod imp {
pub use alloc::fmt::Result as FmtResult;
pub use alloc::fmt::{Debug, Display, Formatter};
}
#[cfg(feature = "std")]
mod imp {
pub use std::fmt::Result as FmtResult;
pub use std::fmt::{Debug, Display, Formatter};
}
use imp::*;
pub trait CuMsgPayload: Default + Debug + Clone + Encode + Decode<()> + Serialize + Sized {}
pub trait CuMsgPack {}
impl<T: Default + Debug + Clone + Encode + Decode<()> + Serialize + Sized> CuMsgPayload for T {}
macro_rules! impl_cu_msg_pack {
($($name:ident),+) => {
impl<'cl, $($name),+> CuMsgPack for ($(&CuMsg<$name>,)+)
where
$($name: CuMsgPayload),+
{}
};
}
impl<T: CuMsgPayload> CuMsgPack for CuMsg<T> {}
impl<T: CuMsgPayload> CuMsgPack for &CuMsg<T> {}
impl<T: CuMsgPayload> CuMsgPack for (&CuMsg<T>,) {}
impl CuMsgPack for () {}
impl_cu_msg_pack!(T1, T2);
impl_cu_msg_pack!(T1, T2, T3);
impl_cu_msg_pack!(T1, T2, T3, T4);
impl_cu_msg_pack!(T1, T2, T3, T4, T5);
#[macro_export]
macro_rules! input_msg {
($lt:lifetime, $first:ty, $($rest:ty),+) => {
( & $lt CuMsg<$first>, $( & $lt CuMsg<$rest> ),+ )
};
($lt:lifetime, $ty:ty) => {
CuMsg<$ty> };
($ty:ty) => {
CuMsg<$ty>
};
}
#[macro_export]
macro_rules! output_msg {
($ty:ty) => {
CuMsg<$ty>
};
($lt:lifetime, $ty:ty) => {
CuMsg<$ty> };
}
#[derive(Debug, Clone, bincode::Encode, bincode::Decode, Serialize, Deserialize)]
pub struct CuMsgMetadata {
pub process_time: PartialCuTimeRange,
pub status_txt: CuCompactString,
}
impl Metadata for CuMsgMetadata {}
impl CuMsgMetadata {
pub fn set_status(&mut self, status: impl ToCompactString) {
self.status_txt = CuCompactString(status.to_compact_string());
}
}
impl CuMsgMetadataTrait for CuMsgMetadata {
fn process_time(&self) -> PartialCuTimeRange {
self.process_time
}
fn status_txt(&self) -> &CuCompactString {
&self.status_txt
}
}
impl Display for CuMsgMetadata {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
write!(
f,
"process_time start: {}, process_time end: {}",
self.process_time.start, self.process_time.end
)
}
}
#[derive(Default, Debug, Clone, bincode::Encode, bincode::Decode, Serialize)]
pub struct CuStampedData<T, M>
where
T: CuMsgPayload,
M: Metadata,
{
payload: Option<T>,
pub tov: Tov,
pub metadata: M,
}
impl Default for CuMsgMetadata {
fn default() -> Self {
CuMsgMetadata {
process_time: PartialCuTimeRange::default(),
status_txt: CuCompactString(CompactString::with_capacity(COMPACT_STRING_CAPACITY)),
}
}
}
impl<T, M> CuStampedData<T, M>
where
T: CuMsgPayload,
M: Metadata,
{
pub fn new(payload: Option<T>) -> Self {
CuStampedData {
payload,
tov: Tov::default(),
metadata: M::default(),
}
}
pub fn payload(&self) -> Option<&T> {
self.payload.as_ref()
}
pub fn set_payload(&mut self, payload: T) {
self.payload = Some(payload);
}
pub fn clear_payload(&mut self) {
self.payload = None;
}
pub fn payload_mut(&mut self) -> &mut Option<T> {
&mut self.payload
}
}
impl<T, M> ErasedCuStampedData for CuStampedData<T, M>
where
T: CuMsgPayload,
M: CuMsgMetadataTrait + Metadata,
{
fn payload(&self) -> Option<&dyn erased_serde::Serialize> {
self.payload
.as_ref()
.map(|p| p as &dyn erased_serde::Serialize)
}
fn tov(&self) -> Tov {
self.tov
}
fn metadata(&self) -> &dyn CuMsgMetadataTrait {
&self.metadata
}
}
pub type CuMsg<T> = CuStampedData<T, CuMsgMetadata>;
pub trait Freezable {
fn freeze<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
Encode::encode(&(), encoder) }
fn thaw<D: Decoder>(&mut self, _decoder: &mut D) -> Result<(), DecodeError> {
Ok(())
}
}
pub struct BincodeAdapter<'a, T: Freezable + ?Sized>(pub &'a T);
impl<'a, T: Freezable + ?Sized> Encode for BincodeAdapter<'a, T> {
fn encode<E: Encoder>(&self, encoder: &mut E) -> Result<(), EncodeError> {
self.0.freeze(encoder)
}
}
pub trait CuSrcTask: Freezable {
type Output<'m>: CuMsgPayload;
fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
where
Self: Sized;
fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
fn process<'o>(&mut self, clock: &RobotClock, new_msg: &mut Self::Output<'o>) -> CuResult<()>;
fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
}
pub trait CuTask: Freezable {
type Input<'m>: CuMsgPack;
type Output<'m>: CuMsgPayload;
fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
where
Self: Sized;
fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
fn process<'i, 'o>(
&mut self,
_clock: &RobotClock,
input: &Self::Input<'i>,
output: &mut Self::Output<'o>,
) -> CuResult<()>;
fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
}
pub trait CuSinkTask: Freezable {
type Input<'m>: CuMsgPack;
fn new(_config: Option<&ComponentConfig>) -> CuResult<Self>
where
Self: Sized;
fn start(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
fn preprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
fn process<'i>(&mut self, _clock: &RobotClock, input: &Self::Input<'i>) -> CuResult<()>;
fn postprocess(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
fn stop(&mut self, _clock: &RobotClock) -> CuResult<()> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use bincode::{config, decode_from_slice, encode_to_vec};
#[test]
fn test_cucompactstr_encode_decode() {
let cstr = CuCompactString(CompactString::from("hello"));
let config = config::standard();
let encoded = encode_to_vec(&cstr, config).expect("Encoding failed");
let (decoded, _): (CuCompactString, usize) =
decode_from_slice(&encoded, config).expect("Decoding failed");
assert_eq!(cstr.0, decoded.0);
}
}