Skip to content

Commit b0b7bab

Browse files
committed
Implement functions to get parameters from global arguments
1 parent 5436d57 commit b0b7bab

File tree

8 files changed

+349
-20
lines changed

8 files changed

+349
-20
lines changed

rclrs/build.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ fn main() {
9292
}
9393

9494
println!("cargo:rustc-link-lib=dylib=rcl");
95+
println!("cargo:rustc-link-lib=dylib=rcl_yaml_param_parser");
9596
println!("cargo:rustc-link-lib=dylib=rcutils");
9697
println!("cargo:rustc-link-lib=dylib=rmw");
9798
println!("cargo:rustc-link-lib=dylib=rmw_implementation");

rclrs/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
mod context;
99
mod error;
1010
mod node;
11+
mod parameter;
1112
mod qos;
1213
mod wait;
1314

@@ -16,6 +17,7 @@ mod rcl_bindings;
1617
pub use context::*;
1718
pub use error::*;
1819
pub use node::*;
20+
pub use parameter::*;
1921
pub use qos::*;
2022
pub use wait::*;
2123

rclrs/src/node.rs

Lines changed: 25 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ pub use self::publisher::*;
66
pub use self::subscription::*;
77

88
use crate::rcl_bindings::*;
9-
use crate::{Context, QoSProfile, RclrsError, ToResult};
10-
use std::ffi::CStr;
9+
use crate::{Context, ParameterOverrideMap, QoSProfile, RclrsError, ToResult};
1110

1211
use std::cmp::PartialEq;
12+
use std::ffi::CStr;
1313
use std::fmt;
1414
use std::sync::{Arc, Weak};
1515
use std::vec::Vec;
@@ -69,6 +69,7 @@ pub struct Node {
6969
rcl_node_mtx: Arc<Mutex<rcl_node_t>>,
7070
pub(crate) rcl_context_mtx: Arc<Mutex<rcl_context_t>>,
7171
pub(crate) subscriptions: Vec<Weak<dyn SubscriptionBase>>,
72+
_parameter_map: ParameterOverrideMap,
7273
}
7374

7475
impl Eq for Node {}
@@ -116,7 +117,7 @@ impl Node {
116117
/// # Ok::<(), RclrsError>(())
117118
/// ```
118119
pub fn name(&self) -> String {
119-
self.get_string(rcl_node_get_name)
120+
self.run_string_getter(rcl_node_get_name)
120121
}
121122

122123
/// Returns the namespace of the node.
@@ -142,7 +143,7 @@ impl Node {
142143
/// # Ok::<(), RclrsError>(())
143144
/// ```
144145
pub fn namespace(&self) -> String {
145-
self.get_string(rcl_node_get_namespace)
146+
self.run_string_getter(rcl_node_get_namespace)
146147
}
147148

148149
/// Returns the fully qualified name of the node.
@@ -162,26 +163,15 @@ impl Node {
162163
/// # Ok::<(), RclrsError>(())
163164
/// ```
164165
pub fn fully_qualified_name(&self) -> String {
165-
self.get_string(rcl_node_get_fully_qualified_name)
166+
self.run_string_getter(rcl_node_get_fully_qualified_name)
166167
}
167168

168169
// Helper for name(), namespace(), fully_qualified_name()
169-
fn get_string(
170+
fn run_string_getter(
170171
&self,
171172
getter: unsafe extern "C" fn(*const rcl_node_t) -> *const c_char,
172173
) -> String {
173-
let char_ptr = unsafe {
174-
// SAFETY: The rcl_node is valid.
175-
getter(&*self.rcl_node_mtx.lock())
176-
};
177-
debug_assert!(!char_ptr.is_null());
178-
let cstr = unsafe {
179-
// SAFETY: The returned CStr is immediately converted to an owned string,
180-
// so the lifetime is no issue. The ptr is valid as per the documentation
181-
// of rcl_node_get_name.
182-
CStr::from_ptr(char_ptr)
183-
};
184-
cstr.to_string_lossy().into_owned()
174+
unsafe { run_string_getter_with_handle(&*self.rcl_node_mtx.lock(), getter) }
185175
}
186176

187177
/// Creates a [`Publisher`][1].
@@ -279,3 +269,20 @@ impl Node {
279269
NodeBuilder::new(context, node_name)
280270
}
281271
}
272+
273+
// Helper used to implement run_string_getter(), but also used to get the FQN in the Node::new()
274+
// function, which is why it's not merged into Node::run_string_getter().
275+
// This function is unsafe since it's possible to pass in an rcl_node_t with dangling
276+
// pointers etc.
277+
unsafe fn run_string_getter_with_handle(
278+
rcl_node: &rcl_node_t,
279+
getter: unsafe extern "C" fn(*const rcl_node_t) -> *const c_char,
280+
) -> String {
281+
let char_ptr = getter(rcl_node);
282+
debug_assert!(!char_ptr.is_null());
283+
// SAFETY: The returned CStr is immediately converted to an owned string,
284+
// so the lifetime is no issue. The ptr is valid as per the documentation
285+
// of rcl_node_get_name.
286+
let cstr = CStr::from_ptr(char_ptr);
287+
cstr.to_string_lossy().into_owned()
288+
}

rclrs/src/node/builder.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
use crate::rcl_bindings::*;
2-
use crate::{Context, Node, RclrsError, ToResult};
2+
use crate::{
3+
node::run_string_getter_with_handle, resolve_parameter_overrides, Context, Node, RclrsError,
4+
ToResult,
5+
};
36

47
use std::ffi::CString;
8+
use std::sync::Arc;
59

610
use parking_lot::Mutex;
7-
use std::sync::Arc;
811

912
/// A builder for creating a [`Node`][1].
1013
///
@@ -263,12 +266,18 @@ impl NodeBuilder {
263266
.ok()?;
264267
};
265268

269+
let _parameter_map = unsafe {
270+
let fqn =
271+
run_string_getter_with_handle(&rcl_node, rcl_node_get_fully_qualified_name);
272+
resolve_parameter_overrides(fqn, &rcl_context.global_arguments)?
273+
};
266274
let rcl_node_mtx = Arc::new(Mutex::new(rcl_node));
267275

268276
Ok(Node {
269277
rcl_node_mtx,
270278
rcl_context_mtx: self.context.clone(),
271279
subscriptions: std::vec![],
280+
_parameter_map,
272281
})
273282
}
274283

rclrs/src/parameter.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mod override_map;
2+
mod value;
3+
4+
pub(crate) use override_map::*;
5+
pub use value::*;

rclrs/src/parameter/override_map.rs

Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
use crate::rcl_bindings::*;
2+
use crate::{ParameterValue, RclrsError, ToResult};
3+
use libc::c_char;
4+
use std::collections::BTreeMap;
5+
use std::ffi::CStr;
6+
7+
// Internal helper struct, iterator for rcl_params_t
8+
struct RclParamsIter<'a> {
9+
node_name_ptrs: &'a [*mut c_char],
10+
rcl_node_params: &'a [rcl_node_params_t],
11+
}
12+
13+
// Internal helper struct, iterator for rcl_node_params_t
14+
struct RclNodeParamsIter<'a> {
15+
param_name_ptrs: &'a [*mut c_char],
16+
rcl_variants: &'a [rcl_variant_t],
17+
}
18+
19+
impl<'a> Iterator for RclParamsIter<'a> {
20+
type Item = (String, &'a rcl_node_params_t);
21+
fn next(&mut self) -> Option<Self::Item> {
22+
let (next_node_param, rest) = self.rcl_node_params.split_first()?;
23+
self.rcl_node_params = rest;
24+
let (next_node_name_ptr, rest) = self.node_name_ptrs.split_first()?;
25+
self.node_name_ptrs = rest;
26+
debug_assert!(!next_node_name_ptr.is_null());
27+
// SAFETY: Pointer can be assumed to be valid. No lifetime issues due to immediate
28+
// conversion to owned string.
29+
let mut next_node_name = unsafe {
30+
CStr::from_ptr(*next_node_name_ptr)
31+
.to_string_lossy()
32+
.into_owned()
33+
};
34+
if !next_node_name.starts_with('/') {
35+
next_node_name.insert(0, '/');
36+
}
37+
Some((next_node_name, next_node_param))
38+
}
39+
}
40+
41+
impl RclParamsIter<'_> {
42+
// This function is unsafe since the rcl_params argument might contain incorrect array sizes or
43+
// dangling pointers.
44+
pub unsafe fn new(rcl_params: *const rcl_params_t) -> Self {
45+
// The params will be null if none were given on the command line.
46+
// So, semantically, this can be treated like a struct with no parameters.
47+
if rcl_params.is_null() {
48+
Self {
49+
node_name_ptrs: &[],
50+
rcl_node_params: &[],
51+
}
52+
} else {
53+
let node_name_ptrs =
54+
std::slice::from_raw_parts((*rcl_params).node_names, (*rcl_params).num_nodes);
55+
let rcl_node_params =
56+
std::slice::from_raw_parts((*rcl_params).params, (*rcl_params).num_nodes);
57+
Self {
58+
node_name_ptrs,
59+
rcl_node_params,
60+
}
61+
}
62+
}
63+
}
64+
65+
impl<'a> Iterator for RclNodeParamsIter<'a> {
66+
type Item = (String, &'a rcl_variant_t);
67+
fn next(&mut self) -> Option<Self::Item> {
68+
let (next_param_name_ptr, rest) = self.param_name_ptrs.split_first()?;
69+
self.param_name_ptrs = rest;
70+
let (next_rcl_variant, rest) = self.rcl_variants.split_first()?;
71+
self.rcl_variants = rest;
72+
// SAFETY: Pointer can be assumed to be valid. No lifetime issues due to immediate
73+
// conversion to owned string.
74+
let next_param_name = unsafe {
75+
CStr::from_ptr(*next_param_name_ptr)
76+
.to_string_lossy()
77+
.into_owned()
78+
};
79+
Some((next_param_name, next_rcl_variant))
80+
}
81+
}
82+
83+
impl<'a> RclNodeParamsIter<'a> {
84+
// This function is unsafe since the rcl_node_params argument might contain incorrect array
85+
// sizes or dangling pointers.
86+
pub unsafe fn new(rcl_node_params: &'a rcl_node_params_t) -> Self {
87+
let param_name_ptrs =
88+
std::slice::from_raw_parts(rcl_node_params.parameter_names, rcl_node_params.num_params);
89+
let rcl_variants = std::slice::from_raw_parts(
90+
rcl_node_params.parameter_values,
91+
rcl_node_params.num_params,
92+
);
93+
Self {
94+
param_name_ptrs,
95+
rcl_variants,
96+
}
97+
}
98+
}
99+
100+
/// A map of parameters for this node.
101+
/// The key for this map is the parameter name.
102+
/// A parameter override "overrides" the default value set from within the node, hence the name.
103+
pub(crate) type ParameterOverrideMap = BTreeMap<String, ParameterValue>;
104+
105+
/// Similar to `rclcpp::detail::resolve_parameter_overrides`, but returns a map instead of
106+
/// a vector.
107+
///
108+
/// This function is unsafe since the rcl_global_arguments argument might contain incorrect array
109+
/// sizes or dangling pointers.
110+
pub(crate) unsafe fn resolve_parameter_overrides(
111+
node_fqn: String,
112+
rcl_global_arguments: &rcl_arguments_t,
113+
) -> Result<ParameterOverrideMap, RclrsError> {
114+
let mut map = BTreeMap::new();
115+
let mut rcl_params = std::ptr::null_mut();
116+
rcl_arguments_get_param_overrides(rcl_global_arguments, &mut rcl_params).ok()?;
117+
// Check for the /** node first, and later overwrite with the more specific node
118+
// parameters, if they exist
119+
for name_to_match in ["/**", node_fqn.as_str()] {
120+
for (node_name, node_params) in RclParamsIter::new(rcl_params) {
121+
if node_name == name_to_match {
122+
for (param_name, variant) in RclNodeParamsIter::new(node_params) {
123+
let value = ParameterValue::from_rcl_variant(variant);
124+
map.insert(param_name, value);
125+
}
126+
}
127+
}
128+
}
129+
rcl_yaml_node_struct_fini(rcl_params);
130+
Ok(map)
131+
}

0 commit comments

Comments
 (0)