More convinent one time parameter access (#107)
* Implement TryInto for ParameterValue * Add `get_parameter` method. * Consolidate 'ParameterNotSet' into 'ParamterWrongType' error * Add support for optional parameters * Add example for get_parameter.
This commit is contained in:
parent
09c8a8ae11
commit
d8fe90b61d
|
|
@ -18,6 +18,24 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let ctx = r2r::Context::create()?;
|
let ctx = r2r::Context::create()?;
|
||||||
let mut node = r2r::Node::create(ctx, "to_be_replaced", "to_be_replaced")?;
|
let mut node = r2r::Node::create(ctx, "to_be_replaced", "to_be_replaced")?;
|
||||||
|
|
||||||
|
// if you only need to load a parameter once at startup, it can be done like this.
|
||||||
|
// errors can be propigated with the ? operator and enhanced with the `thiserror` and `anyhow` crates.
|
||||||
|
// we do not use the ? operator here because we want the program to continue, even if the value is not set.
|
||||||
|
let serial_interface_path = node.get_parameter::<String>("serial_interface");
|
||||||
|
match serial_interface_path {
|
||||||
|
Ok(serial_interface) => println!("Serial interface: {serial_interface}"),
|
||||||
|
Err(error) => println!("Failed to get name of serial interface: {error}"),
|
||||||
|
}
|
||||||
|
|
||||||
|
// you can also get parameters as optional types.
|
||||||
|
// this will be None if the parameter is not set. If the parameter is set but to the wrong type, this will
|
||||||
|
// will produce an error.
|
||||||
|
let baud_rate: Option<i64> = node.get_parameter("baud_rate")?;
|
||||||
|
|
||||||
|
// because the baud_rate is an optional type, we can use `unwrap_or` to provide a default value.
|
||||||
|
let baud_rate = baud_rate.unwrap_or(115200);
|
||||||
|
println!("Baud rate: {baud_rate}");
|
||||||
|
|
||||||
// make a parameter handler (once per node).
|
// make a parameter handler (once per node).
|
||||||
// the parameter handler is optional, only spawn one if you need it.
|
// the parameter handler is optional, only spawn one if you need it.
|
||||||
let (paramater_handler, parameter_events) = node.make_parameter_handler()?;
|
let (paramater_handler, parameter_events) = node.make_parameter_handler()?;
|
||||||
|
|
|
||||||
|
|
@ -127,6 +127,15 @@ pub enum Error {
|
||||||
|
|
||||||
#[error("Parameter {name} conversion failed: {msg}")]
|
#[error("Parameter {name} conversion failed: {msg}")]
|
||||||
ParameterValueConv { name: String, msg: String },
|
ParameterValueConv { name: String, msg: String },
|
||||||
|
|
||||||
|
#[error(
|
||||||
|
"Parameter {name} was expected to be of type {expected_type} but was of type {actual_type}"
|
||||||
|
)]
|
||||||
|
ParameterWrongType {
|
||||||
|
name: String,
|
||||||
|
expected_type: &'static str,
|
||||||
|
actual_type: &'static str,
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Error {
|
impl Error {
|
||||||
|
|
|
||||||
|
|
@ -541,6 +541,29 @@ impl Node {
|
||||||
future::ready(())
|
future::ready(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fetch a single ROS parameter.
|
||||||
|
pub fn get_parameter<T>(&self, name: &str) -> Result<T>
|
||||||
|
where
|
||||||
|
ParameterValue: TryInto<T, Error = WrongParameterType>,
|
||||||
|
{
|
||||||
|
let params = self.params.lock().unwrap();
|
||||||
|
let value = params
|
||||||
|
.get(name)
|
||||||
|
.map(|parameter| parameter.value.clone())
|
||||||
|
.unwrap_or(ParameterValue::NotSet);
|
||||||
|
|
||||||
|
let value: T =
|
||||||
|
value
|
||||||
|
.try_into()
|
||||||
|
.map_err(|error: WrongParameterType| Error::ParameterWrongType {
|
||||||
|
name: name.to_string(),
|
||||||
|
expected_type: error.expected_type_name,
|
||||||
|
actual_type: error.actual_type_name,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(value)
|
||||||
|
}
|
||||||
|
|
||||||
/// Subscribe to a ROS topic.
|
/// Subscribe to a ROS topic.
|
||||||
///
|
///
|
||||||
/// This function returns a `Stream` of ros messages.
|
/// This function returns a `Stream` of ros messages.
|
||||||
|
|
|
||||||
|
|
@ -20,7 +20,94 @@ pub enum ParameterValue {
|
||||||
StringArray(Vec<String>),
|
StringArray(Vec<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct WrongParameterType {
|
||||||
|
pub expected_type_name: &'static str,
|
||||||
|
pub actual_type_name: &'static str,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for WrongParameterType {}
|
||||||
|
|
||||||
|
impl std::fmt::Display for WrongParameterType {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
write!(f, "got `{}`, expected `{}`", self.actual_type_name, self.expected_type_name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! try_into_template {
|
||||||
|
($ty:ty, $expected_type_name:literal, $variant:pat => $result:expr) => {
|
||||||
|
impl TryInto<$ty> for ParameterValue {
|
||||||
|
type Error = WrongParameterType;
|
||||||
|
|
||||||
|
fn try_into(self) -> std::prelude::v1::Result<$ty, Self::Error> {
|
||||||
|
match self {
|
||||||
|
$variant => Ok($result),
|
||||||
|
_ => Err(WrongParameterType {
|
||||||
|
expected_type_name: $expected_type_name,
|
||||||
|
actual_type_name: self.type_name(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try_into_template!((), "not set", ParameterValue::NotSet => ());
|
||||||
|
try_into_template!(bool, "boolean", ParameterValue::Bool(value) => value);
|
||||||
|
try_into_template!(i64, "integer", ParameterValue::Integer(value) => value);
|
||||||
|
try_into_template!(f64, "double", ParameterValue::Double(value) => value);
|
||||||
|
try_into_template!(String, "string", ParameterValue::String(value) => value);
|
||||||
|
try_into_template!(Vec<bool>, "boolean array", ParameterValue::BoolArray(value) => value);
|
||||||
|
try_into_template!(Vec<u8>, "byte array", ParameterValue::ByteArray(value) => value);
|
||||||
|
try_into_template!(Vec<i64>, "integer array", ParameterValue::IntegerArray(value) => value);
|
||||||
|
try_into_template!(Vec<f64>, "double array", ParameterValue::DoubleArray(value) => value);
|
||||||
|
try_into_template!(Vec<String>, "string array", ParameterValue::StringArray(value) => value);
|
||||||
|
|
||||||
|
macro_rules! try_into_option_template {
|
||||||
|
($ty:ty, $expected_type_name:literal, $variant:pat => $result:expr) => {
|
||||||
|
impl TryInto<Option<$ty>> for ParameterValue {
|
||||||
|
type Error = WrongParameterType;
|
||||||
|
|
||||||
|
fn try_into(self) -> std::prelude::v1::Result<Option<$ty>, Self::Error> {
|
||||||
|
match self {
|
||||||
|
$variant => Ok(Some($result)),
|
||||||
|
ParameterValue::NotSet => Ok(None),
|
||||||
|
_ => Err(WrongParameterType {
|
||||||
|
expected_type_name: $expected_type_name,
|
||||||
|
actual_type_name: self.type_name(),
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
try_into_option_template!(bool, "boolean", ParameterValue::Bool(value) => value);
|
||||||
|
try_into_option_template!(i64, "integer", ParameterValue::Integer(value) => value);
|
||||||
|
try_into_option_template!(f64, "double", ParameterValue::Double(value) => value);
|
||||||
|
try_into_option_template!(String, "string", ParameterValue::String(value) => value);
|
||||||
|
try_into_option_template!(Vec<bool>, "boolean array", ParameterValue::BoolArray(value) => value);
|
||||||
|
try_into_option_template!(Vec<u8>, "byte array", ParameterValue::ByteArray(value) => value);
|
||||||
|
try_into_option_template!(Vec<i64>, "integer array", ParameterValue::IntegerArray(value) => value);
|
||||||
|
try_into_option_template!(Vec<f64>, "double array", ParameterValue::DoubleArray(value) => value);
|
||||||
|
try_into_option_template!(Vec<String>, "string array", ParameterValue::StringArray(value) => value);
|
||||||
|
|
||||||
impl ParameterValue {
|
impl ParameterValue {
|
||||||
|
pub fn type_name(&self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
ParameterValue::NotSet => "not set",
|
||||||
|
ParameterValue::Bool(_) => "boolean",
|
||||||
|
ParameterValue::Integer(_) => "integer",
|
||||||
|
ParameterValue::Double(_) => "double",
|
||||||
|
ParameterValue::String(_) => "string",
|
||||||
|
ParameterValue::BoolArray(_) => "boolean array",
|
||||||
|
ParameterValue::ByteArray(_) => "byte array",
|
||||||
|
ParameterValue::IntegerArray(_) => "integer array",
|
||||||
|
ParameterValue::DoubleArray(_) => "double array",
|
||||||
|
ParameterValue::StringArray(_) => "string array",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn from_rcl(v: &rcl_variant_t) -> Self {
|
pub(crate) fn from_rcl(v: &rcl_variant_t) -> Self {
|
||||||
if !v.bool_value.is_null() {
|
if !v.bool_value.is_null() {
|
||||||
ParameterValue::Bool(unsafe { *v.bool_value })
|
ParameterValue::Bool(unsafe { *v.bool_value })
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue