-
Notifications
You must be signed in to change notification settings - Fork 208
Expand file tree
/
Copy pathoverride_map.rs
More file actions
266 lines (250 loc) · 9.13 KB
/
Copy pathoverride_map.rs
File metadata and controls
266 lines (250 loc) · 9.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
use std::{collections::BTreeMap, ffi::CStr, os::raw::c_char};
use crate::{rcl_bindings::*, ParameterValue, RclrsError, ToResult};
// Internal helper struct, iterator for rcl_params_t
struct RclParamsIter<'a> {
node_name_ptrs: &'a [*mut c_char],
rcl_node_params: &'a [rcl_node_params_t],
}
// Internal helper struct, iterator for rcl_node_params_t
struct RclNodeParamsIter<'a> {
param_name_ptrs: &'a [*mut c_char],
rcl_variants: &'a [rcl_variant_t],
}
impl<'a> Iterator for RclParamsIter<'a> {
type Item = (String, &'a rcl_node_params_t);
fn next(&mut self) -> Option<Self::Item> {
let (next_node_param, rest) = self.rcl_node_params.split_first()?;
self.rcl_node_params = rest;
let (next_node_name_ptr, rest) = self.node_name_ptrs.split_first()?;
self.node_name_ptrs = rest;
debug_assert!(!next_node_name_ptr.is_null());
// SAFETY: Pointer can be assumed to be valid. No lifetime issues due to immediate
// conversion to owned string.
let mut next_node_name = unsafe {
CStr::from_ptr(*next_node_name_ptr)
.to_string_lossy()
.into_owned()
};
if !next_node_name.starts_with('/') {
next_node_name.insert(0, '/');
}
Some((next_node_name, next_node_param))
}
}
impl RclParamsIter<'_> {
// This function is unsafe since the rcl_params argument might contain incorrect array sizes or
// dangling pointers.
pub unsafe fn new(rcl_params: *const rcl_params_t) -> Self {
// The params will be null if none were given on the command line.
// So, semantically, this can be treated like a struct with no parameters.
if rcl_params.is_null() {
Self {
node_name_ptrs: &[],
rcl_node_params: &[],
}
} else {
unsafe {
let node_name_ptrs =
rcl_from_raw_parts((*rcl_params).node_names, (*rcl_params).num_nodes);
let rcl_node_params =
rcl_from_raw_parts((*rcl_params).params, (*rcl_params).num_nodes);
Self {
node_name_ptrs,
rcl_node_params,
}
}
}
}
}
impl<'a> Iterator for RclNodeParamsIter<'a> {
type Item = (String, &'a rcl_variant_t);
fn next(&mut self) -> Option<Self::Item> {
let (next_param_name_ptr, rest) = self.param_name_ptrs.split_first()?;
self.param_name_ptrs = rest;
let (next_rcl_variant, rest) = self.rcl_variants.split_first()?;
self.rcl_variants = rest;
// SAFETY: Pointer can be assumed to be valid. No lifetime issues due to immediate
// conversion to owned string.
let next_param_name = unsafe {
CStr::from_ptr(*next_param_name_ptr)
.to_string_lossy()
.into_owned()
};
Some((next_param_name, next_rcl_variant))
}
}
impl<'a> RclNodeParamsIter<'a> {
// This function is unsafe since the rcl_node_params argument might contain incorrect array
// sizes or dangling pointers.
pub unsafe fn new(rcl_node_params: &'a rcl_node_params_t) -> Self {
let param_name_ptrs = unsafe {
rcl_from_raw_parts(rcl_node_params.parameter_names, rcl_node_params.num_params)
};
let rcl_variants = unsafe {
rcl_from_raw_parts(rcl_node_params.parameter_values, rcl_node_params.num_params)
};
Self {
param_name_ptrs,
rcl_variants,
}
}
}
/// A map of parameters for this node.
/// The key for this map is the parameter name.
/// A parameter override "overrides" the default value set from within the node, hence the name.
/// A BTreeMap is used because it supports range queries, making it easy to fetch all parameters
/// with a given prefix.
pub(crate) type ParameterOverrideMap = BTreeMap<String, ParameterValue>;
/// Similar to `rclcpp::detail::resolve_parameter_overrides`, but returns a map instead of
/// a vector.
///
/// This function is unsafe since the rcl_global_arguments argument might contain incorrect array
/// sizes or dangling pointers.
pub(crate) unsafe fn resolve_parameter_overrides(
node_fqn: &str,
rcl_node_arguments: &rcl_arguments_t,
rcl_global_arguments: &rcl_arguments_t,
) -> Result<ParameterOverrideMap, RclrsError> {
unsafe {
let mut map = BTreeMap::new();
for rcl_arguments in [rcl_global_arguments, rcl_node_arguments] {
let mut rcl_params = std::ptr::null_mut();
rcl_arguments_get_param_overrides(rcl_arguments, &mut rcl_params).ok()?;
// Check for the /** node first, and later overwrite with the more specific node
// parameters, if they exist
for name_to_match in ["/**", node_fqn] {
for (node_name, node_params) in RclParamsIter::new(rcl_params) {
if node_name == name_to_match {
for (param_name, variant) in RclNodeParamsIter::new(node_params) {
// Skip ambiguous variants (e.g. empty arrays whose element type the
// rcl YAML parser could not infer). The user will get a clearer
// error later if they actually require this parameter via
// `declare_parameter`.
if let Some(value) = ParameterValue::from_rcl_variant(variant) {
map.insert(param_name, value);
}
}
}
}
}
rcl_yaml_node_struct_fini(rcl_params);
}
Ok(map)
}
}
#[cfg(test)]
mod tests {
use std::{error::Error, ffi::CString, io::Write};
use tempfile::NamedTempFile;
use super::*;
// These files have values for every possible four-bit number, with the four bits being
// * `/**` global params
// * named global params
// * `/**` node params
// * named node params
const GLOBAL_PARAMS_FILE: &str = r#"
/**:
ros__parameters:
a: 1
b: 1
c: 1
d: 1
e: 1
f: 1
g: 1
h: 1
/my_ns/my_node:
ros__parameters:
a: 2
b: 2
c: 2
d: 2
i: 2
j: 2
k: 2
l: 2
"#;
const NODE_PARAMS_FILE: &str = r#"
/my_ns/my_node:
ros__parameters:
a: 3
b: 3
e: 3
f: 3
i: 3
j: 3
m: 3
n: 3
/my_ns/my_node:
ros__parameters:
a: 4
c: 4
e: 4
g: 4
i: 4
k: 4
m: 4
o: 4
"#;
// The returned rcl_arguments need to be manually finalized with rcl_arguments_fini
fn convert_to_rcl_arguments(
param_file_contents: &str,
) -> Result<rcl_arguments_t, Box<dyn Error>> {
let mut params_file = NamedTempFile::new()?;
write!(params_file, "{}", param_file_contents)?;
let params_filename = params_file.path().display().to_string();
let args = ["--ros-args", "--params-file", ¶ms_filename]
.into_iter()
.map(CString::new)
.collect::<Result<Vec<_>, _>>()?;
let args_ptrs = args.iter().map(|s| s.as_ptr()).collect::<Vec<_>>();
let mut rcl_arguments = unsafe { rcl_get_zero_initialized_arguments() };
unsafe {
rcl_parse_arguments(
args_ptrs.len() as i32,
args_ptrs.as_ptr(),
rcutils_get_default_allocator(),
&mut rcl_arguments,
);
}
Ok(rcl_arguments)
}
#[test]
fn test_resolve_parameter_overrides() -> Result<(), Box<dyn Error>> {
let node_fqn = "/my_ns/my_node";
let mut rcl_node_arguments = convert_to_rcl_arguments(NODE_PARAMS_FILE)?;
let mut rcl_global_arguments = convert_to_rcl_arguments(GLOBAL_PARAMS_FILE)?;
let overrides_map = unsafe {
resolve_parameter_overrides(node_fqn, &rcl_node_arguments, &rcl_global_arguments)?
};
unsafe {
rcl_arguments_fini(&mut rcl_node_arguments);
rcl_arguments_fini(&mut rcl_global_arguments);
}
let values: Vec<_> = overrides_map
.iter()
.map(|(k, v)| (k.as_str(), v.clone()))
.collect();
assert_eq!(
values,
vec![
("a", ParameterValue::Integer(4)),
("b", ParameterValue::Integer(3)),
("c", ParameterValue::Integer(4)),
("d", ParameterValue::Integer(2)),
("e", ParameterValue::Integer(4)),
("f", ParameterValue::Integer(3)),
("g", ParameterValue::Integer(4)),
("h", ParameterValue::Integer(1)),
("i", ParameterValue::Integer(4)),
("j", ParameterValue::Integer(3)),
("k", ParameterValue::Integer(4)),
("l", ParameterValue::Integer(2)),
("m", ParameterValue::Integer(4)),
("n", ParameterValue::Integer(3)),
("o", ParameterValue::Integer(4)),
]
);
Ok(())
}
}