1use crate::{Error, Result};
2use std::ffi::CStr;
3
4use crate::msg_types::generated_msgs::rcl_interfaces;
5use indexmap::IndexMap;
6use r2r_rcl::*;
7
8#[derive(Debug, PartialEq, Clone)]
10pub enum ParameterValue {
11 NotSet,
12 Bool(bool),
13 Integer(i64),
14 Double(f64),
15 String(String),
16 BoolArray(Vec<bool>),
17 ByteArray(Vec<u8>),
18 IntegerArray(Vec<i64>),
19 DoubleArray(Vec<f64>),
20 StringArray(Vec<String>),
21}
22
23#[derive(Debug)]
24pub struct WrongParameterType {
25 pub expected_type_name: &'static str,
26 pub actual_type_name: &'static str,
27}
28
29impl std::error::Error for WrongParameterType {}
30
31impl std::fmt::Display for WrongParameterType {
32 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
33 write!(f, "got `{}`, expected `{}`", self.actual_type_name, self.expected_type_name)
34 }
35}
36
37macro_rules! try_into_template {
38 ($ty:ty, $expected_type_name:literal, $variant:pat => $result:expr) => {
39 impl TryInto<$ty> for ParameterValue {
40 type Error = WrongParameterType;
41
42 fn try_into(self) -> std::prelude::v1::Result<$ty, Self::Error> {
43 match self {
44 $variant => Ok($result),
45 _ => Err(WrongParameterType {
46 expected_type_name: $expected_type_name,
47 actual_type_name: self.type_name(),
48 }),
49 }
50 }
51 }
52 };
53}
54
55try_into_template!((), "not set", ParameterValue::NotSet => ());
56try_into_template!(bool, "boolean", ParameterValue::Bool(value) => value);
57try_into_template!(i64, "integer", ParameterValue::Integer(value) => value);
58try_into_template!(f64, "double", ParameterValue::Double(value) => value);
59try_into_template!(String, "string", ParameterValue::String(value) => value);
60try_into_template!(Vec<bool>, "boolean array", ParameterValue::BoolArray(value) => value);
61try_into_template!(Vec<u8>, "byte array", ParameterValue::ByteArray(value) => value);
62try_into_template!(Vec<i64>, "integer array", ParameterValue::IntegerArray(value) => value);
63try_into_template!(Vec<f64>, "double array", ParameterValue::DoubleArray(value) => value);
64try_into_template!(Vec<String>, "string array", ParameterValue::StringArray(value) => value);
65
66macro_rules! try_into_option_template {
67 ($ty:ty, $expected_type_name:literal, $variant:pat => $result:expr) => {
68 impl TryInto<Option<$ty>> for ParameterValue {
69 type Error = WrongParameterType;
70
71 fn try_into(self) -> std::prelude::v1::Result<Option<$ty>, Self::Error> {
72 match self {
73 $variant => Ok(Some($result)),
74 ParameterValue::NotSet => Ok(None),
75 _ => Err(WrongParameterType {
76 expected_type_name: $expected_type_name,
77 actual_type_name: self.type_name(),
78 }),
79 }
80 }
81 }
82 };
83}
84
85try_into_option_template!(bool, "boolean", ParameterValue::Bool(value) => value);
86try_into_option_template!(i64, "integer", ParameterValue::Integer(value) => value);
87try_into_option_template!(f64, "double", ParameterValue::Double(value) => value);
88try_into_option_template!(String, "string", ParameterValue::String(value) => value);
89try_into_option_template!(Vec<bool>, "boolean array", ParameterValue::BoolArray(value) => value);
90try_into_option_template!(Vec<u8>, "byte array", ParameterValue::ByteArray(value) => value);
91try_into_option_template!(Vec<i64>, "integer array", ParameterValue::IntegerArray(value) => value);
92try_into_option_template!(Vec<f64>, "double array", ParameterValue::DoubleArray(value) => value);
93try_into_option_template!(Vec<String>, "string array", ParameterValue::StringArray(value) => value);
94
95impl ParameterValue {
96 pub fn type_name(&self) -> &'static str {
97 match self {
98 ParameterValue::NotSet => "not set",
99 ParameterValue::Bool(_) => "boolean",
100 ParameterValue::Integer(_) => "integer",
101 ParameterValue::Double(_) => "double",
102 ParameterValue::String(_) => "string",
103 ParameterValue::BoolArray(_) => "boolean array",
104 ParameterValue::ByteArray(_) => "byte array",
105 ParameterValue::IntegerArray(_) => "integer array",
106 ParameterValue::DoubleArray(_) => "double array",
107 ParameterValue::StringArray(_) => "string array",
108 }
109 }
110
111 pub(crate) fn from_rcl(v: &rcl_variant_t) -> Self {
112 if !v.bool_value.is_null() {
113 ParameterValue::Bool(unsafe { *v.bool_value })
114 } else if !v.integer_value.is_null() {
115 ParameterValue::Integer(unsafe { *v.integer_value })
116 } else if !v.double_value.is_null() {
117 ParameterValue::Double(unsafe { *v.double_value })
118 } else if !v.string_value.is_null() {
119 let s = unsafe { CStr::from_ptr(v.string_value) };
120 let string = s.to_str().unwrap_or("").to_owned();
121 ParameterValue::String(string)
122 } else if !v.byte_array_value.is_null() {
123 let vals = unsafe {
124 if (*v.byte_array_value).values == std::ptr::null_mut() {
125 &[]
126 } else {
127 std::slice::from_raw_parts(
128 (*v.byte_array_value).values,
129 (*v.byte_array_value).size,
130 )
131 }
132 };
133 ParameterValue::ByteArray(vals.to_vec())
134 } else if !v.bool_array_value.is_null() {
135 let vals = unsafe {
136 if (*v.bool_array_value).values == std::ptr::null_mut() {
137 &[]
138 } else {
139 std::slice::from_raw_parts(
140 (*v.bool_array_value).values,
141 (*v.bool_array_value).size,
142 )
143 }
144 };
145 ParameterValue::BoolArray(vals.to_vec())
146 } else if !v.integer_array_value.is_null() {
147 let vals = unsafe {
148 if (*v.integer_array_value).values == std::ptr::null_mut() {
149 &[]
150 } else {
151 std::slice::from_raw_parts(
152 (*v.integer_array_value).values,
153 (*v.integer_array_value).size,
154 )
155 }
156 };
157 ParameterValue::IntegerArray(vals.to_vec())
158 } else if !v.double_array_value.is_null() {
159 let vals = unsafe {
160 if (*v.double_array_value).values == std::ptr::null_mut() {
161 &[]
162 } else {
163 std::slice::from_raw_parts(
164 (*v.double_array_value).values,
165 (*v.double_array_value).size,
166 )
167 }
168 };
169 ParameterValue::DoubleArray(vals.to_vec())
170 } else if !v.string_array_value.is_null() {
171 let vals = unsafe {
172 if (*v.string_array_value).data == std::ptr::null_mut() {
173 &[]
174 } else {
175 std::slice::from_raw_parts(
176 (*v.string_array_value).data,
177 (*v.string_array_value).size,
178 )
179 }
180 };
181 let s = vals
182 .iter()
183 .map(|cs| {
184 let s = unsafe { CStr::from_ptr(*cs) };
185 s.to_str().unwrap_or("").to_owned()
186 })
187 .collect();
188 ParameterValue::StringArray(s)
189 } else {
190 ParameterValue::NotSet
191 }
192 }
193
194 pub(crate) fn from_parameter_value_msg(msg: rcl_interfaces::msg::ParameterValue) -> Self {
195 match msg.type_ {
197 0 => ParameterValue::NotSet,
198 1 => ParameterValue::Bool(msg.bool_value),
199 2 => ParameterValue::Integer(msg.integer_value),
200 3 => ParameterValue::Double(msg.double_value),
201 4 => ParameterValue::String(msg.string_value),
202 5 => ParameterValue::ByteArray(msg.byte_array_value),
203 6 => ParameterValue::BoolArray(msg.bool_array_value),
204 7 => ParameterValue::IntegerArray(msg.integer_array_value),
205 8 => ParameterValue::DoubleArray(msg.double_array_value),
206 9 => ParameterValue::StringArray(msg.string_array_value),
207 _ => {
208 log::debug!("warning: malformed parametervalue message");
209 ParameterValue::NotSet
210 }
211 }
212 }
213
214 pub(crate) fn into_parameter_value_msg(self) -> rcl_interfaces::msg::ParameterValue {
215 let mut ret = rcl_interfaces::msg::ParameterValue::default();
216
217 match self {
218 ParameterValue::NotSet => {
219 ret.type_ = 0; }
221 ParameterValue::Bool(b) => {
222 ret.type_ = 1; ret.bool_value = b;
224 }
225 ParameterValue::Integer(i) => {
226 ret.type_ = 2; ret.integer_value = i;
228 }
229 ParameterValue::Double(d) => {
230 ret.type_ = 3; ret.double_value = d;
232 }
233 ParameterValue::String(s) => {
234 ret.type_ = 4; ret.string_value = s;
236 }
237 ParameterValue::ByteArray(ba) => {
238 ret.type_ = 5; ret.byte_array_value = ba;
240 }
241 ParameterValue::BoolArray(ba) => {
242 ret.type_ = 6; ret.bool_array_value = ba;
244 }
245 ParameterValue::IntegerArray(ia) => {
246 ret.type_ = 7; ret.integer_array_value = ia;
248 }
249 ParameterValue::DoubleArray(da) => {
250 ret.type_ = 8; ret.double_array_value = da;
252 }
253 ParameterValue::StringArray(sa) => {
254 ret.type_ = 9; ret.string_array_value = sa;
256 }
257 }
258 ret
259 }
260
261 pub(crate) fn into_parameter_type(&self) -> u8 {
262 match self {
263 ParameterValue::NotSet => 0, ParameterValue::Bool(_) => 1, ParameterValue::Integer(_) => 2, ParameterValue::Double(_) => 3, ParameterValue::String(_) => 4, ParameterValue::ByteArray(_) => 5, ParameterValue::BoolArray(_) => 6, ParameterValue::IntegerArray(_) => 7, ParameterValue::DoubleArray(_) => 8, ParameterValue::StringArray(_) => 9, }
274 }
275}
276
277pub struct Parameter {
279 pub value: ParameterValue,
280 pub description: &'static str,
281 }
284
285impl Parameter {
286 pub fn new(value: ParameterValue) -> Self {
287 Self {
288 value,
289 description: "",
290 }
291 }
292 pub fn empty() -> Self {
293 Self {
294 value: ParameterValue::NotSet,
295 description: "",
296 }
297 }
298}
299
300pub trait RosParams {
306 fn register_parameters(
307 &mut self, prefix: &str, param: Option<Parameter>, params: &mut IndexMap<String, Parameter>,
308 ) -> Result<()>;
309 fn get_parameter(&mut self, param_name: &str) -> Result<ParameterValue>;
310 fn set_parameter(&mut self, param_name: &str, param_val: &ParameterValue) -> Result<()>;
311 fn check_parameter(&self, param_name: &str, param_val: &ParameterValue) -> Result<()>;
312}
313
314macro_rules! impl_ros_params {
316 ($type:path, $param_value_type:path, $to_param_conv:path, $from_param_conv:path) => {
317 impl RosParams for $type {
318 fn register_parameters(
319 &mut self, prefix: &str, param: Option<Parameter>,
320 params: &mut IndexMap<String, Parameter>,
321 ) -> Result<()> {
322 if let Some(cli_param) = params.get(prefix) {
323 self.set_parameter("", &cli_param.value)
325 .map_err(|e| e.update_param_name(prefix))?;
326 params.shift_remove(prefix);
328 }
329 let mut param = param.unwrap();
331 param.value = $param_value_type($to_param_conv(self)?);
332 params.insert(prefix.to_owned(), param);
333 Ok(())
334 }
335
336 fn get_parameter(&mut self, param_name: &str) -> Result<ParameterValue> {
337 match param_name {
338 "" => Ok($param_value_type($to_param_conv(self)?)),
339 _ => Err(Error::InvalidParameterName {
340 name: param_name.to_owned(),
341 }),
342 }
343 }
344
345 fn set_parameter(
346 &mut self, param_name: &str, param_val: &ParameterValue,
347 ) -> Result<()> {
348 if param_name != "" {
349 return Err(Error::InvalidParameterName {
350 name: param_name.to_owned(),
351 });
352 }
353 match param_val {
354 $param_value_type(val) => {
355 *self = $from_param_conv(val)?;
356 Ok(())
357 }
358 _ => Err(Error::InvalidParameterType {
359 name: "".to_string(), ty: std::stringify!($param_value_type),
361 }),
362 }
363 }
364
365 fn check_parameter(
366 &self, param_name: &str, param_val: &ParameterValue,
367 ) -> Result<()> {
368 if param_name != "" {
369 return Err(Error::InvalidParameterName {
370 name: param_name.to_owned(),
371 });
372 }
373 match param_val {
374 $param_value_type(val) => {
375 let _: Self = $from_param_conv(val)?;
376 Ok(())
377 }
378 _ => Err(Error::InvalidParameterType {
379 name: "".to_string(), ty: std::stringify!($param_value_type),
381 }),
382 }
383 }
384 }
385 };
386}
387
388impl_ros_params!(bool, ParameterValue::Bool, noop, noop);
389impl_ros_params!(i8, ParameterValue::Integer, try_conv, try_conv);
390impl_ros_params!(i16, ParameterValue::Integer, try_conv, try_conv);
391impl_ros_params!(i32, ParameterValue::Integer, try_conv, try_conv);
392impl_ros_params!(i64, ParameterValue::Integer, noop, noop);
393impl_ros_params!(u8, ParameterValue::Integer, try_conv, try_conv);
394impl_ros_params!(u16, ParameterValue::Integer, try_conv, try_conv);
395impl_ros_params!(u32, ParameterValue::Integer, try_conv, try_conv);
396impl_ros_params!(f64, ParameterValue::Double, noop, noop);
397impl_ros_params!(f32, ParameterValue::Double, to_f64, to_f32);
398impl_ros_params!(String, ParameterValue::String, to_string, to_string);
399fn noop<T: Copy>(x: &T) -> Result<T> {
403 Ok(*x)
404}
405
406fn to_f32(x: &f64) -> Result<f32> {
407 Ok(*x as f32)
408}
409fn to_f64(x: &f32) -> Result<f64> {
410 Ok(*x as f64)
411}
412
413fn try_conv<T, U>(x: &T) -> Result<U>
414where
415 T: Copy,
416 U: TryFrom<T>,
417 <U as TryFrom<T>>::Error: std::error::Error,
418{
419 U::try_from(*x).map_err(|e| Error::ParameterValueConv {
420 name: "".into(),
421 msg: e.to_string(),
422 })
423}
424
425fn to_string(x: &str) -> Result<String> {
426 Ok(x.to_string())
427}