Skip to main content

oxidros_msg/
lib.rs

1#![allow(dead_code)]
2#![allow(non_upper_case_globals)]
3#![allow(non_camel_case_types)]
4#![allow(deref_nullptr)]
5#![allow(non_snake_case)]
6#![allow(improper_ctypes)]
7#![allow(unused_imports)]
8#![allow(clippy::upper_case_acronyms)]
9#![allow(clippy::too_many_arguments)]
10#![allow(clippy::manual_c_str_literals)]
11#![allow(clippy::useless_conversion)]
12
13//! Generated ROS2 message types for oxidros.
14//!
15//! This crate provides Rust bindings for ROS2 messages, services, and actions.
16//! Enable the `rcl` feature for FFI support with ROS2 C libraries.
17//!
18//! Messages are generated at compile time using ros2msg and ros2-types-derive.
19
20// Re-export rcl types for generated code (only available with rcl feature)
21#[cfg(feature = "rcl")]
22pub mod rcl {
23    // Re-export C types from runtime_c
24    pub use crate::runtime_c::*;
25}
26
27pub mod primitives;
28pub mod strings;
29
30// Include runtime C bindings first (provides rosidl_runtime_c types)
31#[cfg(feature = "rcl")]
32mod runtime_c {
33    include!(concat!(env!("OUT_DIR"), "/runtime_c.rs"));
34}
35
36// Re-export runtime_c types
37#[cfg(feature = "rcl")]
38pub use runtime_c::*;
39
40// Re-export msg module utilities for generated code
41pub mod msg {
42    pub use crate::primitives::{
43        BoolSeq, ByteSeq, F32Seq, F64Seq, I8Seq, I16Seq, I32Seq, I64Seq, U8Seq, U16Seq, U32Seq,
44        U64Seq,
45    };
46    pub use crate::strings::{RosString, RosStringSeq, RosWString, RosWStringSeq};
47    pub use oxidros_core::TypeSupport;
48}
49
50// Re-export builtin_interfaces types
51pub mod builtin_interfaces {
52    pub use oxidros_core::{UnsafeDuration, UnsafeTime};
53}
54
55// Re-export ros2-types traits and macros for generated code
56pub use ros2_types::{
57    Ros2Msg, SequenceRaw, ServiceMsg, TryClone, TypeSupport, ros2_action, ros2_service,
58};
59
60// Include generated message modules from OUT_DIR/generated/
61// build.rs always populates OUT_DIR (either by generating fresh or copying pre-committed files)
62#[rustfmt::skip]
63pub mod common_interfaces {
64    //! Common ROS2 interface messages (geometry_msgs, sensor_msgs, etc.)
65    include!(concat!(env!("OUT_DIR"), "/generated/common_interfaces/mod.rs"));
66}
67
68#[rustfmt::skip]
69pub mod interfaces {
70    //! ROS2 core interfaces (rcl_interfaces, action_msgs, etc.)
71    include!(concat!(env!("OUT_DIR"), "/generated/interfaces/mod.rs"));
72}
73
74#[rustfmt::skip]
75pub mod ros2msg {
76    //! Additional ROS2 messages (unique_identifier_msgs, etc.)
77    include!(concat!(env!("OUT_DIR"), "/generated/ros2msg/mod.rs"));
78}
79
80// Re-export commonly used items
81pub use ros2msg::*;
82
83// Re-export oxidros_core module so generated code can use crate::oxidros_core::TypeSupport
84pub use oxidros_core;
85
86// Re-export traits from oxidros-core at the top level for convenience
87pub use oxidros_core::{
88    ActionGoal, ActionMsg, ActionResult, GetUUID, GoalResponse, ResultResponse,
89};
90
91// Re-export UnsafeTime and UnsafeDuration from oxidros-core
92pub use oxidros_core::{UnsafeDuration, UnsafeTime};
93
94use crate::interfaces::rcl_interfaces::msg::ParameterValue;
95use crate::msg::{BoolSeq, ByteSeq, F64Seq, I64Seq, RosString, RosStringSeq};
96use oxidros_core::Value;
97
98impl From<&oxidros_core::parameter::IntegerRange>
99    for interfaces::rcl_interfaces::msg::IntegerRange
100{
101    fn from(range: &oxidros_core::parameter::IntegerRange) -> Self {
102        interfaces::rcl_interfaces::msg::IntegerRange {
103            from_value: range.min,
104            to_value: range.max,
105            step: range.step as u64,
106        }
107    }
108}
109
110impl From<&oxidros_core::parameter::FloatingPointRange>
111    for interfaces::rcl_interfaces::msg::FloatingPointRange
112{
113    fn from(range: &oxidros_core::parameter::FloatingPointRange) -> Self {
114        interfaces::rcl_interfaces::msg::FloatingPointRange {
115            from_value: range.min,
116            to_value: range.max,
117            step: range.step,
118        }
119    }
120}
121
122impl From<&ParameterValue> for Value {
123    fn from(var: &ParameterValue) -> Self {
124        match var.r#type {
125            1 => Value::Bool(var.bool_value),
126            2 => Value::I64(var.integer_value),
127            3 => Value::F64(var.double_value),
128            4 => Value::String(var.string_value.to_string()),
129            5 => {
130                let mut v = Vec::new();
131                var.byte_array_value.iter().for_each(|x| v.push(*x));
132                Value::VecU8(v)
133            }
134            6 => {
135                let mut v = Vec::new();
136                var.bool_array_value.iter().for_each(|x| v.push(*x));
137                Value::VecBool(v)
138            }
139            7 => {
140                let mut v = Vec::new();
141                var.integer_array_value.iter().for_each(|x| v.push(*x));
142                Value::VecI64(v)
143            }
144            8 => {
145                let mut v = Vec::new();
146                var.double_array_value.iter().for_each(|x| v.push(*x));
147                Value::VecF64(v)
148            }
149            9 => {
150                let mut v = Vec::new();
151                var.string_array_value
152                    .iter()
153                    .for_each(|x| v.push(x.to_string()));
154                Value::VecString(v)
155            }
156            _ => Value::NotSet,
157        }
158    }
159}
160
161impl From<&Value> for ParameterValue {
162    fn from(var: &Value) -> Self {
163        let mut result = ParameterValue::new().unwrap();
164        match var {
165            Value::NotSet => result.r#type = 0,
166            Value::Bool(val) => {
167                result.r#type = 1;
168                result.bool_value = *val;
169            }
170            Value::I64(val) => {
171                result.r#type = 2;
172                result.integer_value = *val;
173            }
174            Value::F64(val) => {
175                result.r#type = 3;
176                result.double_value = *val;
177            }
178            Value::String(val) => {
179                result.r#type = 4;
180                result.string_value = RosString::new(val).unwrap_or_else(|| {
181                    log::error!("{}:{}: failed allocation", file!(), line!());
182                    RosString::null()
183                });
184            }
185            Value::VecU8(val) => {
186                result.r#type = 5;
187                result.byte_array_value = ByteSeq::new(val.len()).unwrap_or_else(|| {
188                    log::error!("{}:{}: failed allocation", file!(), line!());
189                    ByteSeq::null()
190                });
191                result
192                    .byte_array_value
193                    .iter_mut()
194                    .zip(val.iter())
195                    .for_each(|(dst, src)| *dst = *src);
196            }
197            Value::VecBool(val) => {
198                result.r#type = 6;
199                result.bool_array_value = BoolSeq::new(val.len()).unwrap_or_else(|| {
200                    log::error!("{}:{}: failed allocation", file!(), line!());
201                    BoolSeq::null()
202                });
203                result
204                    .bool_array_value
205                    .iter_mut()
206                    .zip(val.iter())
207                    .for_each(|(dst, src)| *dst = *src);
208            }
209            Value::VecI64(val) => {
210                result.r#type = 7;
211                result.integer_array_value = I64Seq::new(val.len()).unwrap_or_else(|| {
212                    log::error!("{}:{}: failed allocation", file!(), line!());
213                    I64Seq::null()
214                });
215                result
216                    .integer_array_value
217                    .iter_mut()
218                    .zip(val.iter())
219                    .for_each(|(dst, src)| *dst = *src);
220            }
221            Value::VecF64(val) => {
222                result.r#type = 8;
223                result.double_array_value = F64Seq::new(val.len()).unwrap_or_else(|| {
224                    log::error!("{}:{}: failed allocation", file!(), line!());
225                    F64Seq::null()
226                });
227                result
228                    .double_array_value
229                    .iter_mut()
230                    .zip(val.iter())
231                    .for_each(|(dst, src)| *dst = *src);
232            }
233            Value::VecString(val) => {
234                result.r#type = 9;
235                result.string_array_value = RosStringSeq::new(val.len()).unwrap_or_else(|| {
236                    log::error!("{}:{}: failed allocation", file!(), line!());
237                    RosStringSeq::null()
238                });
239                result
240                    .string_array_value
241                    .iter_mut()
242                    .zip(val.iter())
243                    .for_each(|(dst, src)| {
244                        dst.assign(src);
245                    });
246            }
247        }
248        result
249    }
250}
251
252#[cfg(not(feature = "rcl"))]
253#[cfg(test)]
254mod tests {
255    use oxidros_core::ServiceTypeDescription;
256
257    /// Expected type hashes from ROS2 Jazzy (validated against ros2 CLI)
258    /// These hashes must match exactly for interoperability with ros2 param commands
259    const EXPECTED_HASHES: &[(&str, &str)] = &[
260        (
261            "rcl_interfaces/srv/ListParameters",
262            "RIHS01_3e6062bfbb27bfb8730d4cef2558221f51a11646d78e7bb30a1e83afac3aad9d",
263        ),
264        (
265            "rcl_interfaces/srv/GetParameters",
266            "RIHS01_bf9803d5c74cf989a5de3e0c2e99444599a627c7ff75f97b8c05b01003675cbc",
267        ),
268        (
269            "rcl_interfaces/srv/SetParameters",
270            "RIHS01_56eed9a67e169f9cb6c1f987bc88f868c14a8fc9f743a263bc734c154015d7e0",
271        ),
272        (
273            "rcl_interfaces/srv/SetParametersAtomically",
274            "RIHS01_0e192ef259c07fc3c07a13191d27002222e65e00ccec653ca05e856f79285fcd",
275        ),
276        (
277            "rcl_interfaces/srv/DescribeParameters",
278            "RIHS01_845b484d71eb0673dae682f2e3ba3c4851a65a3dcfb97bddd82c5b57e91e4cff",
279        ),
280        (
281            "rcl_interfaces/srv/GetParameterTypes",
282            "RIHS01_da199c878688b3e530bdfe3ca8f74cb9fa0c303101e980a9e8f260e25e1c80ca",
283        ),
284    ];
285
286    #[test]
287    fn test_list_parameters_type_hash() {
288        use super::interfaces::rcl_interfaces::srv::ListParameters;
289        let hash = ListParameters::compute_hash().expect("failed to compute hash");
290        assert_eq!(
291            hash, EXPECTED_HASHES[0].1,
292            "ListParameters type hash mismatch - interop with ros2 param will fail"
293        );
294    }
295
296    #[test]
297    fn test_get_parameters_type_hash() {
298        use super::interfaces::rcl_interfaces::srv::GetParameters;
299        let td = GetParameters::type_description();
300        println!("GetParameters type description: {:?}", td);
301        let hash = GetParameters::compute_hash().expect("failed to compute hash");
302        assert_eq!(
303            hash, EXPECTED_HASHES[1].1,
304            "GetParameters type hash mismatch - interop with ros2 param will fail"
305        );
306    }
307
308    #[test]
309    fn test_set_parameters_type_hash() {
310        use super::interfaces::rcl_interfaces::srv::SetParameters;
311        let hash = SetParameters::compute_hash().expect("failed to compute hash");
312        assert_eq!(
313            hash, EXPECTED_HASHES[2].1,
314            "SetParameters type hash mismatch - interop with ros2 param will fail"
315        );
316    }
317
318    #[test]
319    fn test_set_parameters_atomically_type_hash() {
320        use super::interfaces::rcl_interfaces::srv::SetParametersAtomically;
321        let hash = SetParametersAtomically::compute_hash().expect("failed to compute hash");
322        assert_eq!(
323            hash, EXPECTED_HASHES[3].1,
324            "SetParametersAtomically type hash mismatch - interop with ros2 param will fail"
325        );
326    }
327
328    #[test]
329    fn test_describe_parameters_type_hash() {
330        use super::interfaces::rcl_interfaces::srv::DescribeParameters;
331        let hash = DescribeParameters::compute_hash().expect("failed to compute hash");
332        assert_eq!(
333            hash, EXPECTED_HASHES[4].1,
334            "DescribeParameters type hash mismatch - interop with ros2 param will fail"
335        );
336    }
337
338    #[test]
339    fn test_get_parameter_types_type_hash() {
340        use super::interfaces::rcl_interfaces::srv::GetParameterTypes;
341        let hash = GetParameterTypes::compute_hash().expect("failed to compute hash");
342        assert_eq!(
343            hash, EXPECTED_HASHES[5].1,
344            "GetParameterTypes type hash mismatch - interop with ros2 param will fail"
345        );
346    }
347
348    #[test]
349    fn test_all_parameter_service_hashes() {
350        // Test all parameter service hashes in one place for easy validation
351        use super::interfaces::rcl_interfaces::srv::{
352            DescribeParameters, GetParameterTypes, GetParameters, ListParameters, SetParameters,
353            SetParametersAtomically,
354        };
355
356        let services: Vec<(&str, String)> = vec![
357            (
358                "ListParameters",
359                ListParameters::compute_hash().expect("hash"),
360            ),
361            (
362                "GetParameters",
363                GetParameters::compute_hash().expect("hash"),
364            ),
365            (
366                "SetParameters",
367                SetParameters::compute_hash().expect("hash"),
368            ),
369            (
370                "SetParametersAtomically",
371                SetParametersAtomically::compute_hash().expect("hash"),
372            ),
373            (
374                "DescribeParameters",
375                DescribeParameters::compute_hash().expect("hash"),
376            ),
377            (
378                "GetParameterTypes",
379                GetParameterTypes::compute_hash().expect("hash"),
380            ),
381        ];
382
383        let mut all_match = true;
384        for (i, (name, hash)) in services.iter().enumerate() {
385            let expected = EXPECTED_HASHES[i].1;
386            if hash != expected {
387                eprintln!("MISMATCH: {} - got {} expected {}", name, hash, expected);
388                all_match = false;
389            }
390        }
391
392        assert!(
393            all_match,
394            "One or more parameter service type hashes do not match ROS2 expectations"
395        );
396    }
397}