Skip to content

Commit dcccd03

Browse files
committed
Implement functions to get parameters from global arguments
1 parent 2ee4d7a commit dcccd03

File tree

7 files changed

+347
-18
lines changed

7 files changed

+347
-18
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/mod.rs

Lines changed: 34 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ pub use self::publisher::*;
44
pub use self::subscription::*;
55

66
use crate::rcl_bindings::*;
7-
use crate::{Context, QoSProfile, RclrsError, ToResult};
7+
use crate::{
8+
resolve_parameter_overrides, Context, ParameterOverrideMap, QoSProfile, RclrsError, ToResult,
9+
};
810
use std::ffi::{CStr, CString};
911

1012
use std::cmp::PartialEq;
@@ -42,6 +44,7 @@ pub struct Node {
4244
handle: Arc<Mutex<rcl_node_t>>,
4345
pub(crate) context: Arc<Mutex<rcl_context_t>>,
4446
pub(crate) subscriptions: Vec<Weak<dyn SubscriptionBase>>,
47+
_parameter_map: ParameterOverrideMap,
4548
}
4649

4750
impl Eq for Node {}
@@ -149,12 +152,19 @@ impl Node {
149152
.ok()?;
150153
}
151154

155+
let _parameter_map = unsafe {
156+
let fqn =
157+
run_string_getter_with_handle(&node_handle, rcl_node_get_fully_qualified_name);
158+
resolve_parameter_overrides(fqn, &context_handle.global_arguments)?
159+
};
160+
152161
let handle = Arc::new(Mutex::new(node_handle));
153162

154163
Ok(Node {
155164
handle,
156165
context: context.handle.clone(),
157-
subscriptions: std::vec![],
166+
subscriptions: vec![],
167+
_parameter_map,
158168
})
159169
}
160170

@@ -178,7 +188,7 @@ impl Node {
178188
/// # Ok::<(), RclrsError>(())
179189
/// ```
180190
pub fn name(&self) -> String {
181-
self.get_string(rcl_node_get_name)
191+
self.run_string_getter(rcl_node_get_name)
182192
}
183193

184194
/// Returns the namespace of the node.
@@ -201,7 +211,7 @@ impl Node {
201211
/// # Ok::<(), RclrsError>(())
202212
/// ```
203213
pub fn namespace(&self) -> String {
204-
self.get_string(rcl_node_get_namespace)
214+
self.run_string_getter(rcl_node_get_namespace)
205215
}
206216

207217
/// Returns the fully qualified name of the node.
@@ -218,26 +228,15 @@ impl Node {
218228
/// # Ok::<(), RclrsError>(())
219229
/// ```
220230
pub fn fully_qualified_name(&self) -> String {
221-
self.get_string(rcl_node_get_fully_qualified_name)
231+
self.run_string_getter(rcl_node_get_fully_qualified_name)
222232
}
223233

224234
// Helper for name(), namespace(), fully_qualified_name()
225-
fn get_string(
235+
fn run_string_getter(
226236
&self,
227237
getter: unsafe extern "C" fn(*const rcl_node_t) -> *const c_char,
228238
) -> String {
229-
let char_ptr = unsafe {
230-
// SAFETY: The node handle is valid.
231-
getter(&*self.handle.lock())
232-
};
233-
debug_assert!(!char_ptr.is_null());
234-
let cstr = unsafe {
235-
// SAFETY: The returned CStr is immediately converted to an owned string,
236-
// so the lifetime is no issue. The ptr is valid as per the documentation
237-
// of rcl_node_get_name.
238-
CStr::from_ptr(char_ptr)
239-
};
240-
cstr.to_string_lossy().into_owned()
239+
unsafe { run_string_getter_with_handle(&*self.handle.lock(), getter) }
241240
}
242241

243242
/// Creates a [`Publisher`][1].
@@ -316,3 +315,20 @@ impl Node {
316315
domain_id
317316
}
318317
}
318+
319+
// Helper used to implement run_string_getter(), but also used to get the FQN in the Node::new()
320+
// function, which is why it's not merged into Node::run_string_getter().
321+
// This function is unsafe since it's possible to pass in an rcl_node_t with dangling
322+
// pointers etc.
323+
unsafe fn run_string_getter_with_handle(
324+
rcl_node: &rcl_node_t,
325+
getter: unsafe extern "C" fn(*const rcl_node_t) -> *const c_char,
326+
) -> String {
327+
let char_ptr = getter(rcl_node);
328+
debug_assert!(!char_ptr.is_null());
329+
// SAFETY: The returned CStr is immediately converted to an owned string,
330+
// so the lifetime is no issue. The ptr is valid as per the documentation
331+
// of rcl_node_get_name.
332+
let cstr = CStr::from_ptr(char_ptr);
333+
cstr.to_string_lossy().into_owned()
334+
}

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)