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 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).
|
||||
// the parameter handler is optional, only spawn one if you need it.
|
||||
let (paramater_handler, parameter_events) = node.make_parameter_handler()?;
|
||||
|
|
|
|||
|
|
@ -127,6 +127,15 @@ pub enum Error {
|
|||
|
||||
#[error("Parameter {name} conversion failed: {msg}")]
|
||||
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 {
|
||||
|
|
|
|||
|
|
@ -541,6 +541,29 @@ impl Node {
|
|||
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.
|
||||
///
|
||||
/// This function returns a `Stream` of ros messages.
|
||||
|
|
|
|||
|
|
@ -20,7 +20,94 @@ pub enum ParameterValue {
|
|||
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 {
|
||||
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 {
|
||||
if !v.bool_value.is_null() {
|
||||
ParameterValue::Bool(unsafe { *v.bool_value })
|
||||
|
|
|
|||
Loading…
Reference in New Issue