Skip to main content

cu_ros2_payloads/
lib.rs

1pub mod builtin;
2pub mod sensor_msgs;
3pub mod std_msgs;
4
5use core::fmt::Display;
6use serde::Serialize;
7use serde::de::DeserializeOwned;
8use std::convert::From;
9
10// By default use Rust type as ROS type
11#[macro_export]
12macro_rules! ros_type_name {
13    ($t:ty) => {{ std::any::type_name::<$t>().rsplit("::").next().unwrap() }};
14}
15
16/// ROS adaptation trait to convert payload data to ROS compatible message.
17/// The output type must match the structure of the related "msg" file.
18/// The namespace relates to the ROS namespace (such as "std_msgs")
19/// and the type name is the same as the message filename.
20pub trait RosMsgAdapter<'a>: Sized {
21    type Output: Serialize + for<'b> From<&'b Self>;
22
23    /// Validates that this payload can be represented by the selected ROS compatibility profile.
24    ///
25    /// Most adapters are always representable. Payloads with distro-specific restrictions can
26    /// override this hook so bridges fail before conversion and serialization.
27    fn validate_ros_message(&self) -> Result<(), String> {
28        Ok(())
29    }
30
31    /// The namespace of the ROS message, such as "std_msgs" or "sensor_msgs".
32    fn namespace() -> &'a str;
33
34    /// The type name of the ROS message, such as "Int8" or "PointCloud2".
35    fn type_name() -> &'a str {
36        ros_type_name!(Self::Output)
37    }
38
39    /// This hash is generated from an SHA256 from the IDL.
40    /// It is obscure.
41    /// For example Int8 is "RIHS01_26525065a403d972cb672f0777e333f0c799ad444ae5fcd79e43d1e73bd0f440"
42    /// This will only be used in ROS 2 Iron and later versions.
43    fn type_hash() -> &'static str;
44}
45
46/// Bidirectional ROS adaptation trait used by transport bridges.
47///
48/// Implement this for Copper payloads that can be encoded to and decoded from a ROS message
49/// representation.
50pub trait RosBridgeAdapter: Sized + 'static {
51    type RosMessage: Serialize + DeserializeOwned + 'static;
52
53    /// Validates that this payload can be represented by the selected ROS compatibility profile.
54    fn validate_ros_message(&self) -> Result<(), String> {
55        Ok(())
56    }
57
58    fn namespace() -> &'static str;
59
60    fn type_name() -> &'static str {
61        ros_type_name!(Self::RosMessage)
62    }
63
64    fn type_hash() -> &'static str;
65
66    fn to_ros_message(&self) -> Self::RosMessage;
67
68    fn from_ros_message(msg: Self::RosMessage) -> Result<Self, String>;
69}
70
71impl<T> RosBridgeAdapter for T
72where
73    T: RosMsgAdapter<'static> + TryFrom<<T as RosMsgAdapter<'static>>::Output> + 'static,
74    T::Output: Serialize + DeserializeOwned + 'static,
75    <T as TryFrom<<T as RosMsgAdapter<'static>>::Output>>::Error: Display,
76{
77    type RosMessage = <T as RosMsgAdapter<'static>>::Output;
78
79    fn validate_ros_message(&self) -> Result<(), String> {
80        <T as RosMsgAdapter<'static>>::validate_ros_message(self)
81    }
82
83    fn namespace() -> &'static str {
84        <T as RosMsgAdapter<'static>>::namespace()
85    }
86
87    fn type_name() -> &'static str {
88        <T as RosMsgAdapter<'static>>::type_name()
89    }
90
91    #[cfg(not(feature = "humble"))]
92    fn type_hash() -> &'static str {
93        <T as RosMsgAdapter<'static>>::type_hash()
94    }
95
96    #[cfg(feature = "humble")]
97    fn type_hash() -> &'static str {
98        "TypeHashNotSupported"
99    }
100
101    fn to_ros_message(&self) -> Self::RosMessage {
102        self.into()
103    }
104
105    fn from_ros_message(msg: Self::RosMessage) -> Result<Self, String> {
106        T::try_from(msg).map_err(|e| e.to_string())
107    }
108}