* Fixed failing `ros2 param ...` on r2r nodes for Jazzy (#120)
Add `/set_parameters_atomically` service to `make_parameter_handler_internal` in `nodes.rs` to fix failing `ros2 param ...` on r2r nodes for Jazzy.
* Atomic behavior for `set_parameters_atomically` (#121).
---------
Co-authored-by: Desmond Germans <desmond@germansmedia.nl>
GUI tools for working with parameters (at least Foxglove Studio, rqt
and rig_reconfigure) show the parameters in the same order as returned
by the ListParameters service. r2r stores the parameters in a HashMap,
which iterates keys and values in arbitrary order. The result is that
the GUI tools show the parameters in different order after every node
invocation, which can be quite annoying.
To make the order deterministic, we change the parameter storage from
HashMap to IndexMap, which iterates the map in insertion order.
According to the indexmap documentation, IndexMap is a drop-in
replacement of HashMap so this change should not require code changes
in applications using r2r. At least r2r examples and my projects
needed no changes.
With this change, adding doc comments to fields of structures used
with `#[derive(RosParams)]` results in those comments being used as
parameter description.
See r2r/examples/parameters_derive.rs for how to use and test this
feature.
*BREAKING CHANGE*
This commit changes r2r public API. Previously Node::params contained
HashMap<String, ParameterValue>, now it contains HashMap<String, Parameter>.
If you previously used the ParameterValue from this HashMap, now you
can get the same by using the .value field of the Parameter structure.
With this, declaring and handling node parameters becomes easy. One
just needs to define a structure(s) containing the parameters such as:
#[derive(RosParams, Default, Debug)]
struct Params {
par1: f64,
par2: i32,
str: String,
}
And then instantiate and register it with:
let params = Arc::new(Mutex::new(Params::default()));
let (paramater_handler, _) = node.make_derived_parameter_handler(params.clone())?;
This will add three parameters `par1`, `par2` and `str` to the node.
Their type will be `Double`, `Integer` and `String` respectively.
Other Rust types such as `f32` or differently sized integers, e.g.
`u16` are also supported and registered as appropriate ROS parameter
types.
After spawning the handler, e.g.:
spawner.spawn_local(paramater_handler)?;
changing a parameter with external ROS tools (e.g. `ros2 param set`)
will result in changing the appropriate field in the `Params`
structure. Type conversion is handled automatically. For example,
setting an `i8` field (represented as `Integer` ROS parameter) will
succeed if the value is in range -128 to 127 and fail with appropriate
error message for other values.
The other direction also works: Changing a value in the `Params`
structure will be visible outside of the Node via the `get_parameters`
service.
It is also possible to organize the parameters as several nested
structures with parameters. Then, parameter names of different nesting
levels will be separated by `.`. For example `nested.par3`. See the
full example in `parameters_derive.rs`.