Skip to main content

ros2_client/
parameters.rs

1//! Rust-like representation of ROS2 Parameters
2//!
3//! Parameters are key-value paris that can be set in application code, on the
4//! command line (not yet implemented), from environment variables (not
5//! implemented), or remotely.
6//!
7//! Paramters can be queried and set remotely using e.g. the `ros2 param` or
8//! `rqt` tools from ROS 2. This only works for [`Node`](crate::Node)s that have
9//! enabled Parameter Services and are running a `Spinner`.
10
11/// Named parameter
12#[derive(Debug, Clone)]
13pub struct Parameter {
14  pub name: String,
15  pub value: ParameterValue,
16}
17
18/// Rust-like representation of ROS2
19/// [ParameterValue](https://github.com/ros2/rcl_interfaces/blob/master/rcl_interfaces/msg/ParameterValue.msg)
20#[derive(Debug, Clone, PartialEq, PartialOrd)]
21pub enum ParameterValue {
22  NotSet,
23  Boolean(bool),
24  Integer(i64),
25  Double(f64),
26  String(String),
27  ByteArray(Vec<u8>),
28  BooleanArray(Vec<bool>),
29  IntegerArray(Vec<i64>),
30  DoubleArray(Vec<f64>),
31  StringArray(Vec<String>),
32}
33
34/// List of Parameter types supported by ROS 2.
35/// <https://github.com/ros2/rcl_interfaces/blob/humble/rcl_interfaces/msg/ParameterType.msg>
36#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
37pub enum ParameterType {
38  NotSet = 0,
39  Bool = 1,
40  Integer = 2,
41  Double = 3,
42  String = 4,
43  ByteArray = 5,
44  BoolArray = 6,
45  IntegerArray = 7,
46  DoubleArray = 8,
47  StringArray = 9,
48}
49
50impl ParameterValue {
51  // https://github.com/ros2/rcl_interfaces/blob/rolling/rcl_interfaces/msg/ParameterType.msg
52  pub fn to_parameter_type(&self) -> ParameterType {
53    match self {
54      ParameterValue::NotSet => ParameterType::NotSet,
55      ParameterValue::Boolean(_) => ParameterType::Bool,
56      ParameterValue::Integer(_) => ParameterType::Integer,
57      ParameterValue::Double(_d) => ParameterType::Double,
58      ParameterValue::String(_s) => ParameterType::String,
59      ParameterValue::ByteArray(_a) => ParameterType::ByteArray,
60      ParameterValue::BooleanArray(_a) => ParameterType::BoolArray,
61      ParameterValue::IntegerArray(_a) => ParameterType::IntegerArray,
62      ParameterValue::DoubleArray(_a) => ParameterType::DoubleArray,
63      ParameterValue::StringArray(_a) => ParameterType::StringArray,
64    }
65  }
66
67  pub fn to_parameter_type_raw(p: &ParameterValue) -> u8 {
68    Self::to_parameter_type(p) as u8
69  }
70}
71
72impl From<raw::Parameter> for Parameter {
73  fn from(rp: raw::Parameter) -> Self {
74    Parameter {
75      name: rp.name,
76      value: rp.value.into(),
77    }
78  }
79}
80
81impl From<raw::ParameterValue> for ParameterValue {
82  fn from(rpv: raw::ParameterValue) -> ParameterValue {
83    match rpv.ptype {
84      raw::ParameterType::NOT_SET => ParameterValue::NotSet,
85      raw::ParameterType::BOOL => ParameterValue::Boolean(rpv.boolean_value),
86      raw::ParameterType::INTEGER => ParameterValue::Integer(rpv.int_value),
87      raw::ParameterType::DOUBLE => ParameterValue::Double(rpv.double_value),
88      raw::ParameterType::STRING => ParameterValue::String(rpv.string_value),
89
90      raw::ParameterType::BYTE_ARRAY => ParameterValue::ByteArray(rpv.byte_array),
91      raw::ParameterType::BOOL_ARRAY => ParameterValue::BooleanArray(rpv.bool_array),
92      raw::ParameterType::INTEGER_ARRAY => ParameterValue::IntegerArray(rpv.int_array),
93      raw::ParameterType::DOUBLE_ARRAY => ParameterValue::DoubleArray(rpv.double_array),
94      raw::ParameterType::STRING_ARRAY => ParameterValue::StringArray(rpv.string_array),
95
96      _ =>
97      // This may be an unspecified case.
98      // TODO: Do something better, at least log a warning.
99      {
100        ParameterValue::NotSet
101      }
102    }
103  }
104}
105
106impl From<Parameter> for raw::Parameter {
107  fn from(p: Parameter) -> raw::Parameter {
108    raw::Parameter {
109      name: p.name,
110      value: p.value.into(),
111    }
112  }
113}
114
115impl From<ParameterValue> for raw::ParameterValue {
116  fn from(p: ParameterValue) -> raw::ParameterValue {
117    let mut value = raw::ParameterValue {
118      ptype: raw::ParameterType::NOT_SET,
119      boolean_value: false,
120      int_value: 0,
121      double_value: 0.0,
122      string_value: String::new(),
123      byte_array: Vec::new(),
124      int_array: Vec::new(),
125      bool_array: Vec::new(),
126      double_array: Vec::new(),
127      string_array: Vec::new(),
128    };
129    match p {
130      ParameterValue::NotSet => (), // already there
131      ParameterValue::Boolean(b) => {
132        value.ptype = raw::ParameterType::BOOL;
133        value.boolean_value = b;
134      }
135      ParameterValue::Integer(i) => {
136        value.ptype = raw::ParameterType::INTEGER;
137        value.int_value = i;
138      }
139      ParameterValue::Double(d) => {
140        value.ptype = raw::ParameterType::DOUBLE;
141        value.double_value = d;
142      }
143      ParameterValue::String(s) => {
144        value.ptype = raw::ParameterType::STRING;
145        value.string_value = s;
146      }
147      ParameterValue::ByteArray(a) => {
148        value.ptype = raw::ParameterType::BYTE_ARRAY;
149        value.byte_array = a;
150      }
151      ParameterValue::BooleanArray(a) => {
152        value.ptype = raw::ParameterType::BOOL_ARRAY;
153        value.bool_array = a;
154      }
155      ParameterValue::IntegerArray(a) => {
156        value.ptype = raw::ParameterType::INTEGER_ARRAY;
157        value.int_array = a;
158      }
159      ParameterValue::DoubleArray(a) => {
160        value.ptype = raw::ParameterType::DOUBLE_ARRAY;
161        value.double_array = a;
162      }
163      ParameterValue::StringArray(a) => {
164        value.ptype = raw::ParameterType::STRING_ARRAY;
165        value.string_array = a;
166      }
167    }
168    value
169  }
170} // impl From
171
172/// Result from attempt to set a Parameter value (remotely).
173pub type SetParametersResult = Result<(), String>;
174
175impl From<SetParametersResult> for raw::SetParametersResult {
176  fn from(s: SetParametersResult) -> raw::SetParametersResult {
177    match s {
178      Ok(_) => raw::SetParametersResult {
179        successful: true,
180        reason: "".to_string(),
181      },
182      Err(reason) => raw::SetParametersResult {
183        successful: false,
184        reason,
185      },
186    }
187  }
188}
189
190/// Documentation and constraints for a [`Parameter`]
191pub struct ParameterDescriptor {
192  pub name: String,
193  pub param_type: ParameterType, // ParameterType.msg defines enum
194  pub description: String,       /* Description of the parameter, visible from
195                                  * introspection tools. */
196  pub additional_constraints: String, /* Plain English description of additional constraints
197                                       * which cannot be expressed.. */
198  pub read_only: bool, // If 'true' then the value cannot change after it has been initialized.
199  pub dynamic_typing: bool, // If true, the parameter is allowed to change type.
200  pub range: NumericRange,
201}
202
203impl ParameterDescriptor {
204  pub fn unknown(name: &str) -> Self {
205    ParameterDescriptor {
206      name: name.to_string(),
207      param_type: ParameterType::NotSet,
208      description: "unknown parameter".to_string(),
209      additional_constraints: "".to_string(),
210      read_only: true,
211      dynamic_typing: false,
212      range: NumericRange::NotSpecified,
213    }
214  }
215
216  pub fn from_value(name: &str, value: &ParameterValue) -> Self {
217    ParameterDescriptor {
218      name: name.to_string(),
219      param_type: value.to_parameter_type(),
220      description: "(description missing, not implemented)".to_string(),
221      additional_constraints: "".to_string(),
222      read_only: false,
223      dynamic_typing: false,
224      range: NumericRange::NotSpecified,
225    }
226  }
227}
228
229/// Optional Limits for a numeric [`Parameter`]
230pub enum NumericRange {
231  NotSpecified,
232  IntegerRange {
233    from_value: i64,
234    to_value: i64,
235    step: i64,
236  },
237  FloatingPointRange {
238    from_value: f64,
239    to_value: f64,
240    step: f64,
241  },
242}
243
244impl From<ParameterDescriptor> for raw::ParameterDescriptor {
245  fn from(p: ParameterDescriptor) -> raw::ParameterDescriptor {
246    let (integer_range, floating_point_range) = match p.range {
247      NumericRange::NotSpecified => (vec![], vec![]),
248
249      NumericRange::IntegerRange {
250        from_value,
251        to_value,
252        step,
253      } => (
254        vec![raw::IntegerRange {
255          from_value,
256          to_value,
257          step,
258        }],
259        vec![],
260      ),
261
262      NumericRange::FloatingPointRange {
263        from_value,
264        to_value,
265        step,
266      } => (
267        vec![],
268        vec![raw::FloatingPointRange {
269          from_value,
270          to_value,
271          step,
272        }],
273      ),
274    };
275
276    raw::ParameterDescriptor {
277      name: p.name,
278      r#type: p.param_type as u8,
279      description: p.description,
280      additional_constraints: p.additional_constraints,
281      read_only: p.read_only,
282      dynamic_typing: p.dynamic_typing,
283      integer_range,
284      floating_point_range,
285    }
286  }
287}
288
289/// Raw, ROS2-compatible Parameters for sending over the wire.
290/// Not for use in a Rust application.
291pub mod raw {
292  use rustdds::*;
293  use serde::{Deserialize, Serialize};
294
295  /// ROS2 [ParameterEvent](https://github.com/ros2/rcl_interfaces/blob/master/rcl_interfaces/msg/ParameterEvent.msg)
296  #[derive(Debug, Clone, Serialize, Deserialize)]
297  pub struct ParameterEvent {
298    pub timestamp: Timestamp,
299    // fully qualified path
300    pub node: String,
301    pub new_parameters: Vec<Parameter>,
302    pub changed_parameters: Vec<Parameter>,
303    pub deleted_parameters: Vec<Parameter>,
304  }
305
306  /// [Parameter](https://github.com/ros2/rcl_interfaces/blob/master/rcl_interfaces/msg/Parameter.msg)
307  #[derive(Debug, Clone, Serialize, Deserialize)]
308  pub struct Parameter {
309    pub name: String,
310    pub value: ParameterValue,
311  }
312
313  /// [ParameterValue](https://github.com/ros2/rcl_interfaces/blob/master/rcl_interfaces/msg/ParameterValue.msg)
314  #[derive(Debug, Clone, Serialize, Deserialize)]
315  pub struct ParameterValue {
316    pub ptype: u8,
317    pub boolean_value: bool,
318    pub int_value: i64,
319    pub double_value: f64,
320    pub string_value: String,
321    pub byte_array: Vec<u8>,
322    pub bool_array: Vec<bool>,
323    pub int_array: Vec<i64>,
324    pub double_array: Vec<f64>,
325    pub string_array: Vec<String>,
326  }
327
328  /// ROS2 defines this as an empty .msg
329  /// [ParameterType](https://github.com/ros2/rcl_interfaces/blob/master/rcl_interfaces/msg/ParameterType.msg)
330  pub struct ParameterType {}
331
332  impl ParameterType {
333    pub const NOT_SET: u8 = 0;
334
335    pub const BOOL: u8 = 1;
336    pub const INTEGER: u8 = 2;
337    pub const DOUBLE: u8 = 3;
338    pub const STRING: u8 = 4;
339    pub const BYTE_ARRAY: u8 = 5;
340    pub const BOOL_ARRAY: u8 = 6;
341    pub const INTEGER_ARRAY: u8 = 7;
342    pub const DOUBLE_ARRAY: u8 = 8;
343    pub const STRING_ARRAY: u8 = 9;
344  }
345
346  /// [SetParameersResult](https://github.com/ros2/rcl_interfaces/blob/rolling/rcl_interfaces/msg/SetParametersResult.msg)
347  #[derive(Debug, Clone, Serialize, Deserialize)]
348  pub struct SetParametersResult {
349    pub successful: bool,
350    pub reason: String,
351  }
352
353  /// [ParameterDescriptor](https://github.com/ros2/rcl_interfaces/blob/humble/rcl_interfaces/msg/ParameterDescriptor.msg)
354  #[derive(Debug, Clone, Serialize, Deserialize)]
355  pub struct ParameterDescriptor {
356    pub name: String,
357    pub r#type: u8, // ParameterType.msg defines enum
358    pub description: String, /* Description of the parameter, visible from
359                     * introspection tools. */
360    pub additional_constraints: String, /* Plain English description of additional constraints
361                                         * which cannot be expressed.. */
362    pub read_only: bool, // If 'true' then the value cannot change after it has been initialized.
363    pub dynamic_typing: bool, // If true, the parameter is allowed to change type.
364    pub floating_point_range: Vec<FloatingPointRange>,
365    pub integer_range: Vec<IntegerRange>,
366  }
367
368  /// [IntegerRange](https://github.com/ros2/rcl_interfaces/blob/humble/rcl_interfaces/msg/IntegerRange.msg)
369  #[derive(Debug, Clone, Serialize, Deserialize)]
370  pub struct IntegerRange {
371    pub from_value: i64,
372    pub to_value: i64,
373    pub step: i64,
374  }
375
376  /// [FloatingPointRange](https://github.com/ros2/rcl_interfaces/blob/humble/rcl_interfaces/msg/FloatingPointRange.msg)
377  #[derive(Debug, Clone, Serialize, Deserialize)]
378  pub struct FloatingPointRange {
379    pub from_value: f64,
380    pub to_value: f64,
381    pub step: f64,
382  }
383}