Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59ad83ab5a | ||
|
|
cae3836ca0 | ||
|
|
75051616f1 | ||
|
|
f5afe380d5 | ||
|
|
7bfda87d32 | ||
|
|
e33105057c | ||
|
|
736550cace | ||
|
|
35c89c8afc | ||
|
|
37ee1ff309 | ||
|
|
d01a577f87 | ||
|
|
6cc89caa63 | ||
|
|
c767f0b4c4 | ||
|
|
13312dc6ed | ||
|
|
e11986bbdb | ||
|
|
c8713edbe4 | ||
|
|
5a832e4db0 | ||
|
|
267786207b | ||
|
|
b9ffd72f42 | ||
|
|
dc7e95aaa5 | ||
|
|
ee7080d95d | ||
|
|
b9fb9bda3d | ||
|
|
24bb65305d | ||
|
|
ab743392e4 | ||
|
|
18b112fcce | ||
|
|
09950ba23f |
@@ -2,6 +2,33 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
7.0.1 (2021-03-22)
|
||||
------------------
|
||||
* get_parameters service should return empty if undeclared parameters are allowed (`#1514 <https://github.com/ros2/rclcpp/issues/1514>`_)
|
||||
* Made 'Context::shutdown_reason' function a const function (`#1578 <https://github.com/ros2/rclcpp/issues/1578>`_)
|
||||
* Contributors: Tomoya Fujita, suab321321
|
||||
|
||||
7.0.0 (2021-03-18)
|
||||
------------------
|
||||
* Document design decisions that were made for statically typed parameters (`#1568 <https://github.com/ros2/rclcpp/issues/1568>`_)
|
||||
* Fix doc typo in CallbackGroup constructor (`#1582 <https://github.com/ros2/rclcpp/issues/1582>`_)
|
||||
* Enable qos parameter overrides for the /parameter_events topic (`#1532 <https://github.com/ros2/rclcpp/issues/1532>`_)
|
||||
* Add support for rmw_connextdds (`#1574 <https://github.com/ros2/rclcpp/issues/1574>`_)
|
||||
* Remove 'struct' from the rcl_time_jump_t. (`#1577 <https://github.com/ros2/rclcpp/issues/1577>`_)
|
||||
* Add tests for declaring statically typed parameters when undeclared parameters are allowed (`#1575 <https://github.com/ros2/rclcpp/issues/1575>`_)
|
||||
* Quiet clang memory leak warning on "DoNotOptimize". (`#1571 <https://github.com/ros2/rclcpp/issues/1571>`_)
|
||||
* Add ParameterEventsSubscriber class (`#829 <https://github.com/ros2/rclcpp/issues/829>`_)
|
||||
* When a parameter change is rejected, the parameters map shouldn't be updated. (`#1567 <https://github.com/ros2/rclcpp/pull/1567>`_)
|
||||
* Fix when to throw the NoParameterOverrideProvided exception. (`#1567 <https://github.com/ros2/rclcpp/pull/1567>`_)
|
||||
* Fix SEGV caused by order of destruction of Node sub-interfaces (`#1469 <https://github.com/ros2/rclcpp/issues/1469>`_)
|
||||
* Fix benchmark test failure introduced in `#1522 <https://github.com/ros2/rclcpp/issues/1522>`_ (`#1564 <https://github.com/ros2/rclcpp/issues/1564>`_)
|
||||
* Fix documented example in create_publisher (`#1558 <https://github.com/ros2/rclcpp/issues/1558>`_)
|
||||
* Enforce static parameter types (`#1522 <https://github.com/ros2/rclcpp/issues/1522>`_)
|
||||
* Allow timers to keep up the intended rate in MultiThreadedExecutor (`#1516 <https://github.com/ros2/rclcpp/issues/1516>`_)
|
||||
* Fix UBSAN warnings in any_subscription_callback. (`#1551 <https://github.com/ros2/rclcpp/issues/1551>`_)
|
||||
* Fix runtime error: reference binding to null pointer of type (`#1547 <https://github.com/ros2/rclcpp/issues/1547>`_)
|
||||
* Contributors: Andrea Sorbini, Chris Lalancette, Colin MacKenzie, Ivan Santiago Paunovic, Jacob Perron, Steven! Ragnarök, bpwilcox, tomoya
|
||||
|
||||
6.3.1 (2021-02-08)
|
||||
------------------
|
||||
* Reference test resources directly from source tree (`#1543 <https://github.com/ros2/rclcpp/issues/1543>`_)
|
||||
|
||||
@@ -39,6 +39,8 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/clock.cpp
|
||||
src/rclcpp/context.cpp
|
||||
src/rclcpp/contexts/default_context.cpp
|
||||
src/rclcpp/detail/mutex_two_priorities.cpp
|
||||
src/rclcpp/detail/resolve_parameter_overrides.cpp
|
||||
src/rclcpp/detail/rmw_implementation_specific_payload.cpp
|
||||
src/rclcpp/detail/rmw_implementation_specific_publisher_payload.cpp
|
||||
src/rclcpp/detail/rmw_implementation_specific_subscription_payload.cpp
|
||||
@@ -79,6 +81,7 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/parameter.cpp
|
||||
src/rclcpp/parameter_value.cpp
|
||||
src/rclcpp/parameter_client.cpp
|
||||
src/rclcpp/parameter_event_handler.cpp
|
||||
src/rclcpp/parameter_events_filter.cpp
|
||||
src/rclcpp/parameter_map.cpp
|
||||
src/rclcpp/parameter_service.cpp
|
||||
|
||||
141
rclcpp/doc/notes_on_statically_typed_parameters.md
Normal file
141
rclcpp/doc/notes_on_statically_typed_parameters.md
Normal file
@@ -0,0 +1,141 @@
|
||||
# Notes on statically typed parameters
|
||||
|
||||
## Introduction
|
||||
|
||||
Until ROS 2 Foxy, all parameters could change type anytime, except if the user installed a parameter callback to reject a change.
|
||||
This could generate confusing errors, for example:
|
||||
|
||||
```
|
||||
$ ros2 run demo_nodes_py listener &
|
||||
$ ros2 param set /listener use_sim_time not_a_boolean
|
||||
[ERROR] [1614712713.233733147] [listener]: use_sim_time parameter set to something besides a bool
|
||||
Set parameter successful
|
||||
$ ros2 param get /listener use_sim_time
|
||||
String value is: not_a_boolean
|
||||
```
|
||||
|
||||
For most use cases, having static parameter types is enough.
|
||||
This article documents some of the decisions that were made when implementing static parameter types enforcement in:
|
||||
|
||||
* https://github.com/ros2/rclcpp/pull/1522
|
||||
* https://github.com/ros2/rclpy/pull/683
|
||||
|
||||
## Allowing dynamically typed parameters
|
||||
|
||||
There might be some scenarios where dynamic typing is desired, so a `dynamic_typing` field was added to the [parameter descriptor](https://github.com/ros2/rcl_interfaces/blob/09b5ed93a733adb9deddc47f9a4a8c6e9f584667/rcl_interfaces/msg/ParameterDescriptor.msg#L25).
|
||||
It defaults to `false`.
|
||||
|
||||
For example:
|
||||
|
||||
```cpp
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
|
||||
node->declare_parameter("dynamically_typed_parameter", rclcpp::ParameterValue{}, descriptor);
|
||||
```
|
||||
|
||||
```py
|
||||
rcl_interfaces.msg.ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = True;
|
||||
|
||||
node.declare_parameter("dynamically_typed_parameter", None, descriptor);
|
||||
```
|
||||
|
||||
## How is the parameter type specified?
|
||||
|
||||
The parameter type will be inferred from the default value provided when declaring it.
|
||||
|
||||
## Statically typed parameters when allowing undeclared parameters
|
||||
|
||||
When undeclared parameters are allowed and a parameter is set without a previous declaration, the parameter will be dynamically typed.
|
||||
This is consistent with other allowed behaviors when undeclared parameters are allowed, e.g. trying to get an undeclared parameter returns "NOT_SET".
|
||||
Parameter declarations will remain special and dynamic or static typing will be used based on the parameter descriptor (default to static).
|
||||
|
||||
## Declaring a parameter without a default value
|
||||
|
||||
There might be cases were a default value does not make sense and the user must always provide an override.
|
||||
In those cases, the parameter type can be specified explicitly:
|
||||
|
||||
```cpp
|
||||
// method signature
|
||||
template<typename T>
|
||||
Node::declare_parameter<T>(std::string name, rcl_interfaces::msg::ParameterDescriptor = rcl_interfaces::msg::ParameterDescriptor{});
|
||||
// or alternatively
|
||||
Node::declare_parameter(std::string name, rclcpp::ParameterType type, rcl_interfaces::msg::ParameterDescriptor = rcl_interfaces::msg::ParameterDescriptor{});
|
||||
|
||||
// examples
|
||||
node->declare_parameter<int64_t>("my_integer_parameter"); // declare an integer parameter
|
||||
node->declare_parameter("another_integer_parameter", rclcpp::ParameterType::PARAMETER_INTEGER); // another way to do the same
|
||||
```
|
||||
|
||||
```py
|
||||
# method signature
|
||||
Node.declare_parameter(name: str, param_type: rclpy.Parameter.Type, descriptor: rcl_interfaces.msg.ParameterDescriptor = rcl_interfaces.msg.ParameterDescriptor())
|
||||
|
||||
# example
|
||||
node.declare_parameter('my_integer_parameter', rclpy.Parameter.Type.INTEGER); # declare an integer parameter
|
||||
```
|
||||
|
||||
If the parameter may be unused, but when used requires a parameter override, then you could conditionally declare it:
|
||||
|
||||
```cpp
|
||||
auto mode = node->declare_parameter("mode", "modeA"); // "mode" parameter is an string
|
||||
if (mode == "modeB") {
|
||||
node->declare_parameter<int64_t>("param_needed_when_mode_b"); // when "modeB", the user must provide "param_needed_when_mode_b"
|
||||
}
|
||||
```
|
||||
|
||||
## Other migration notes
|
||||
|
||||
Declaring a parameter with only a name is deprecated:
|
||||
|
||||
```cpp
|
||||
node->declare_parameter("my_param"); // this generates a build warning
|
||||
```
|
||||
|
||||
```py
|
||||
node.declare_parameter("my_param"); # this generates a python user warning
|
||||
```
|
||||
|
||||
Before, you could initialize a parameter with the "NOT SET" value (in cpp `rclcpp::ParameterValue{}`, in python `None`).
|
||||
Now this will throw an exception in both cases:
|
||||
|
||||
```cpp
|
||||
node->declare_parameter("my_param", rclcpp::ParameterValue{}); // not valid, will throw exception
|
||||
```
|
||||
|
||||
```py
|
||||
node.declare_parameter("my_param", None); # not valid, will raise error
|
||||
```
|
||||
|
||||
## Possible improvements
|
||||
|
||||
### Easier way to declare dynamically typed parameters
|
||||
|
||||
Declaring a dynamically typed parameter in `rclcpp` could be considered to be a bit verbose:
|
||||
|
||||
```cpp
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
|
||||
node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
```
|
||||
|
||||
the following ways could be supported to make it simpler:
|
||||
|
||||
```cpp
|
||||
node->declare_parameter(name, rclcpp::PARAMETER_DYNAMIC);
|
||||
node->declare_parameter(name, default_value, rclcpp::PARAMETER_DYNAMIC);
|
||||
```
|
||||
|
||||
or alternatively:
|
||||
|
||||
```cpp
|
||||
node->declare_parameter(name, default_value, rclcpp::ParameterDescriptor{}.dynamic_typing());
|
||||
```
|
||||
|
||||
For `rclpy`, there's already a short way to do it:
|
||||
|
||||
```py
|
||||
node.declare_parameter(name, default_value, rclpy.ParameterDescriptor(dynamic_typing=true));
|
||||
```
|
||||
@@ -65,6 +65,9 @@ public:
|
||||
const_shared_ptr_callback_(nullptr), const_shared_ptr_with_info_callback_(nullptr),
|
||||
unique_ptr_callback_(nullptr), unique_ptr_with_info_callback_(nullptr)
|
||||
{
|
||||
if (allocator == nullptr) {
|
||||
throw std::runtime_error("invalid allocator");
|
||||
}
|
||||
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
|
||||
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
|
||||
}
|
||||
|
||||
@@ -85,8 +85,8 @@ public:
|
||||
* group, or after, is irrelevant; the callback group will be automatically
|
||||
* added to the executor in either case.
|
||||
*
|
||||
* \param[in] group_type They type of the callback group.
|
||||
* \param[in] automatically_add_to_executor_with_node a boolean which
|
||||
* \param[in] group_type The type of the callback group.
|
||||
* \param[in] automatically_add_to_executor_with_node A boolean that
|
||||
* determines whether a callback group is automatically added to an executor
|
||||
* with the node with which it is associated.
|
||||
*/
|
||||
|
||||
@@ -136,7 +136,7 @@ private:
|
||||
RCLCPP_PUBLIC
|
||||
static void
|
||||
on_time_jump(
|
||||
const struct rcl_time_jump_t * time_jump,
|
||||
const rcl_time_jump_t * time_jump,
|
||||
bool before_jump,
|
||||
void * user_data);
|
||||
|
||||
|
||||
@@ -150,7 +150,7 @@ public:
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
shutdown_reason();
|
||||
shutdown_reason() const;
|
||||
|
||||
/// Shutdown the context, making it uninitialized and therefore invalid for derived entities.
|
||||
/**
|
||||
@@ -286,7 +286,7 @@ private:
|
||||
|
||||
// This mutex is recursive so that the destructor can ensure atomicity
|
||||
// between is_initialized and shutdown.
|
||||
std::recursive_mutex init_mutex_;
|
||||
mutable std::recursive_mutex init_mutex_;
|
||||
std::shared_ptr<rcl_context_t> rcl_context_;
|
||||
rclcpp::InitOptions init_options_;
|
||||
std::string shutdown_reason_;
|
||||
|
||||
75
rclcpp/include/rclcpp/detail/mutex_two_priorities.hpp
Normal file
75
rclcpp/include/rclcpp/detail/mutex_two_priorities.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2021 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__DETAIL__MUTEX_TWO_PRIORITIES_HPP_
|
||||
#define RCLCPP__DETAIL__MUTEX_TWO_PRIORITIES_HPP_
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
/// \internal A mutex that has two locking mechanism, one with higher priority than the other.
|
||||
/**
|
||||
* After the current mutex owner release the lock, a thread that used the high
|
||||
* priority mechanism will have priority over threads that used the low priority mechanism.
|
||||
*/
|
||||
class MutexTwoPriorities
|
||||
{
|
||||
public:
|
||||
class HighPriorityLockable
|
||||
{
|
||||
public:
|
||||
explicit HighPriorityLockable(MutexTwoPriorities & parent);
|
||||
|
||||
void lock();
|
||||
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
MutexTwoPriorities & parent_;
|
||||
};
|
||||
|
||||
class LowPriorityLockable
|
||||
{
|
||||
public:
|
||||
explicit LowPriorityLockable(MutexTwoPriorities & parent);
|
||||
|
||||
void lock();
|
||||
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
MutexTwoPriorities & parent_;
|
||||
};
|
||||
|
||||
HighPriorityLockable
|
||||
get_high_priority_lockable();
|
||||
|
||||
LowPriorityLockable
|
||||
get_low_priority_lockable();
|
||||
|
||||
private:
|
||||
// Implementation detail: the idea here is that only one low priority thread can be
|
||||
// trying to take the data_ mutex while the others are excluded by the barrier_ mutex.
|
||||
// All high priority threads are already waiting for the data_ mutex.
|
||||
std::mutex barrier_;
|
||||
std::mutex data_;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DETAIL__MUTEX_TWO_PRIORITIES_HPP_
|
||||
@@ -282,6 +282,20 @@ class ParameterModifiedInCallbackException : public std::runtime_error
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/// Thrown when a parameter override wasn't provided and one was required.
|
||||
class NoParameterOverrideProvided : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
/// Construct an instance.
|
||||
/**
|
||||
* \param[in] name the name of the parameter.
|
||||
* \param[in] message custom exception message.
|
||||
*/
|
||||
explicit NoParameterOverrideProvided(const std::string & name)
|
||||
: std::runtime_error("parameter '" + name + "' requires an user provided parameter override")
|
||||
{}
|
||||
};
|
||||
|
||||
/// Thrown if the QoS overrides provided aren't valid.
|
||||
class InvalidQosOverridesException : public std::runtime_error
|
||||
{
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "rclcpp/detail/mutex_two_priorities.hpp"
|
||||
#include "rclcpp/executor.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/memory_strategies.hpp"
|
||||
@@ -81,7 +82,7 @@ protected:
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(MultiThreadedExecutor)
|
||||
|
||||
std::mutex wait_mutex_;
|
||||
detail::MutexTwoPriorities wait_mutex_;
|
||||
size_t number_of_threads_;
|
||||
bool yield_before_execute_;
|
||||
std::chrono::nanoseconds next_exec_timeout_;
|
||||
|
||||
@@ -163,7 +163,7 @@ public:
|
||||
* pub = node->create_publisher<MsgT>("chatter", QoS(10)); // implicitly KeepLast
|
||||
* pub = node->create_publisher<MsgT>("chatter", QoS(KeepLast(10)));
|
||||
* pub = node->create_publisher<MsgT>("chatter", QoS(KeepAll()));
|
||||
* pub = node->create_publisher<MsgT>("chatter", QoS(1).best_effort().volatile());
|
||||
* pub = node->create_publisher<MsgT>("chatter", QoS(1).best_effort().durability_volatile());
|
||||
* {
|
||||
* rclcpp::QoS custom_qos(KeepLast(10), rmw_qos_profile_sensor_data);
|
||||
* pub = node->create_publisher<MsgT>("chatter", custom_qos);
|
||||
@@ -305,16 +305,59 @@ public:
|
||||
* name is invalid.
|
||||
* \throws rclcpp::exceptions::InvalidParameterValueException if initial
|
||||
* value fails to be set.
|
||||
* \throws rclcpp::exceptions::InvalidParameterTypeException
|
||||
* if the type of the default value or override is wrong.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value = rclcpp::ParameterValue(),
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare and initialize a parameter, return the effective value.
|
||||
/**
|
||||
* Same as the previous one, but a default value is not provided and the user
|
||||
* must provide a parameter override of the correct type.
|
||||
*
|
||||
* \param[in] name The name of the parameter.
|
||||
* \param[in] type Desired type of the parameter, which will enforced at runtime.
|
||||
* \param[in] parameter_descriptor An optional, custom description for
|
||||
* the parameter.
|
||||
* \param[in] ignore_override When `true`, the parameter override is ignored.
|
||||
* Default to `false`.
|
||||
* \return A const reference to the value of the parameter.
|
||||
* \throws Same as the previous overload taking a default value.
|
||||
* \throws rclcpp::exceptions::InvalidParameterTypeException
|
||||
* if an override is not provided or the provided override is of the wrong type.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::ParameterType type,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor{},
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare a parameter
|
||||
[[deprecated(
|
||||
"declare_parameter() with only a name is deprecated and will be deleted in the future.\n" \
|
||||
"If you want to declare a parameter that won't change type without a default value use:\n" \
|
||||
"`node->declare_parameter<ParameterT>(name)`, where e.g. ParameterT=int64_t.\n\n" \
|
||||
"If you want to declare a parameter that can dynamically change type use:\n" \
|
||||
"```\n" \
|
||||
"rcl_interfaces::msg::ParameterDescriptor descriptor;\n" \
|
||||
"descriptor.dynamic_typing = true;\n" \
|
||||
"node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);\n" \
|
||||
"```"
|
||||
)]]
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(const std::string & name);
|
||||
|
||||
/// Declare and initialize a parameter with a type.
|
||||
/**
|
||||
* See the non-templated declare_parameter() on this class for details.
|
||||
@@ -345,6 +388,18 @@ public:
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare and initialize a parameter with a type.
|
||||
/**
|
||||
* See the non-templated declare_parameter() on this class for details.
|
||||
*/
|
||||
template<typename ParameterT>
|
||||
auto
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare and initialize several parameters with the same namespace and type.
|
||||
/**
|
||||
* For each key in the map, a parameter with a name of "namespace.key"
|
||||
|
||||
@@ -172,6 +172,24 @@ Node::declare_parameter(
|
||||
}
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
auto
|
||||
Node::declare_parameter(
|
||||
const std::string & name,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
|
||||
bool ignore_override)
|
||||
{
|
||||
// get advantage of parameter value template magic to get
|
||||
// the correct rclcpp::ParameterType from ParameterT
|
||||
rclcpp::ParameterValue value{ParameterT{}};
|
||||
return this->declare_parameter(
|
||||
name,
|
||||
value.get_type(),
|
||||
parameter_descriptor,
|
||||
ignore_override
|
||||
).get<ParameterT>();
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
std::vector<ParameterT>
|
||||
Node::declare_parameters(
|
||||
|
||||
@@ -103,13 +103,42 @@ public:
|
||||
virtual
|
||||
~NodeParameters();
|
||||
|
||||
// This is overriding a deprecated method, so we need to ignore the deprecation warning here.
|
||||
// Users of the method will still get a warning!
|
||||
#ifndef _WIN32
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
[[deprecated(RCLCPP_INTERNAL_NODE_PARAMETERS_INTERFACE_DEPRECATE_DECLARE)]]
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(const std::string & name) override;
|
||||
#ifndef _WIN32
|
||||
# pragma GCC diagnostic pop
|
||||
#else
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
|
||||
bool ignore_override) override;
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor{},
|
||||
bool ignore_override = false) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::ParameterType type,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
|
||||
@@ -45,6 +45,17 @@ struct OnSetParametersCallbackHandle
|
||||
OnParametersSetCallbackType callback;
|
||||
};
|
||||
|
||||
#define RCLCPP_INTERNAL_NODE_PARAMETERS_INTERFACE_DEPRECATE_DECLARE \
|
||||
"declare_parameter() with only a name is deprecated and will be deleted in the future.\n" \
|
||||
"If you want to declare a parameter that won't change type without a default value use:\n" \
|
||||
"`node_params->declare_parameter(name, type)`, with e.g. type=rclcpp::PARAMETER_INTEGER.\n\n" \
|
||||
"If you want to declare a parameter that can dynamically change type use:\n" \
|
||||
"```\n" \
|
||||
"rcl_interfaces::msg::ParameterDescriptor descriptor;\n" \
|
||||
"descriptor.dynamic_typing = true;\n" \
|
||||
"node_params->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);\n" \
|
||||
"```"
|
||||
|
||||
/// Pure virtual interface class for the NodeParameters part of the Node API.
|
||||
class NodeParametersInterface
|
||||
{
|
||||
@@ -55,6 +66,15 @@ public:
|
||||
virtual
|
||||
~NodeParametersInterface() = default;
|
||||
|
||||
/// Declare a parameter.
|
||||
/**
|
||||
* \sa rclcpp::Node::declare_parameter
|
||||
*/
|
||||
[[deprecated(RCLCPP_INTERNAL_NODE_PARAMETERS_INTERFACE_DEPRECATE_DECLARE)]]
|
||||
virtual
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(const std::string & name) = 0;
|
||||
|
||||
/// Declare and initialize a parameter.
|
||||
/**
|
||||
* \sa rclcpp::Node::declare_parameter
|
||||
@@ -64,7 +84,21 @@ public:
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value = rclcpp::ParameterValue(),
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false) = 0;
|
||||
|
||||
/// Declare a parameter.
|
||||
/**
|
||||
* \sa rclcpp::Node::declare_parameter
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::ParameterType type,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false) = 0;
|
||||
|
||||
338
rclcpp/include/rclcpp/parameter_event_handler.hpp
Normal file
338
rclcpp/include/rclcpp/parameter_event_handler.hpp
Normal file
@@ -0,0 +1,338 @@
|
||||
// Copyright 2019 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__PARAMETER_EVENT_HANDLER_HPP_
|
||||
#define RCLCPP__PARAMETER_EVENT_HANDLER_HPP_
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_event.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
struct ParameterCallbackHandle
|
||||
{
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ParameterCallbackHandle)
|
||||
|
||||
using ParameterCallbackType = std::function<void (const rclcpp::Parameter &)>;
|
||||
|
||||
std::string parameter_name;
|
||||
std::string node_name;
|
||||
ParameterCallbackType callback;
|
||||
};
|
||||
|
||||
struct ParameterEventCallbackHandle
|
||||
{
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ParameterEventCallbackHandle)
|
||||
|
||||
using ParameterEventCallbackType =
|
||||
std::function<void (const rcl_interfaces::msg::ParameterEvent::SharedPtr &)>;
|
||||
|
||||
ParameterEventCallbackType callback;
|
||||
};
|
||||
|
||||
/// A class used to "handle" (monitor and respond to) changes to parameters.
|
||||
/**
|
||||
* The ParameterEventHandler class allows for the monitoring of changes to node parameters,
|
||||
* either a node's own parameters or parameters owned by other nodes in the system.
|
||||
* Multiple parameter callbacks can be set and will be invoked when the specified parameter
|
||||
* changes.
|
||||
*
|
||||
* The first step is to instantiate a ParameterEventHandler, providing a ROS node to use
|
||||
* to create any required subscriptions:
|
||||
*
|
||||
* auto param_handler = std::make_shared<rclcpp::ParameterEventHandler>(node);
|
||||
*
|
||||
* Next, you can supply a callback to the add_parameter_callback method, as follows:
|
||||
*
|
||||
* auto cb1 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb1: Received an update to parameter \"%s\" of type %s: \"%ld\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_int());
|
||||
* };
|
||||
* auto handle1 = param_handler->add_parameter_callback("an_int_param", cb1);
|
||||
*
|
||||
* In this case, we didn't supply a node name (the third, optional, parameter) so the
|
||||
* default will be to monitor for changes to the "an_int_param" parameter associated with
|
||||
* the ROS node supplied in the ParameterEventHandler constructor.
|
||||
* The callback, a lambda function in this case, simply prints out the value of the parameter.
|
||||
*
|
||||
* You may also monitor for changes to parameters in other nodes by supplying the node
|
||||
* name to add_parameter_callback:
|
||||
*
|
||||
* auto cb2 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb2: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* };
|
||||
* auto handle2 = param_handler->add_parameter_callback(
|
||||
* "some_remote_param_name", cb2, "some_remote_node_name");
|
||||
*
|
||||
* In this case, the callback will be invoked whenever "some_remote_param_name" changes
|
||||
* on remote node "some_remote_node_name".
|
||||
*
|
||||
* To remove a parameter callback, call remove_parameter_callback, passing the handle returned
|
||||
* from add_parameter_callback:
|
||||
*
|
||||
* param_handler->remove_parameter_callback(handle2);
|
||||
*
|
||||
* You can also monitor for *all* parameter changes, using add_parameter_event_callback.
|
||||
* In this case, the callback will be invoked whenever any parameter changes in the system.
|
||||
* You are likely interested in a subset of these parameter changes, so in the callback it
|
||||
* is convenient to use a regular expression on the node names or namespaces of interest.
|
||||
* For example:
|
||||
*
|
||||
* auto cb3 =
|
||||
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent::SharedPtr & event) {
|
||||
* // Look for any updates to parameters in "/a_namespace" as well as any parameter changes
|
||||
* // to our own node ("this_node")
|
||||
* std::regex re("(/a_namespace/.*)|(/this_node)");
|
||||
* if (regex_match(event->node, re)) {
|
||||
* // Now that we know the event matches the regular expression we scanned for, we can
|
||||
* // use 'get_parameter_from_event' to get a specific parameter name that we're looking for
|
||||
* rclcpp::Parameter p;
|
||||
* if (rclcpp::ParameterEventsSubscriber::get_parameter_from_event(
|
||||
* *event, p, remote_param_name, fqn))
|
||||
* {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* }
|
||||
*
|
||||
* // You can also use 'get_parameter*s*_from_event' to enumerate all changes that came
|
||||
* // in on this event
|
||||
* auto params = rclcpp::ParameterEventsSubscriber::get_parameters_from_event(*event);
|
||||
* for (auto & p : params) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.value_to_string().c_str());
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
* auto handle3 = param_handler->add_parameter_event_callback(cb3);
|
||||
*
|
||||
* For both parameter callbacks and parameter event callbacks, when multiple callbacks are added,
|
||||
* the callbacks are invoked last-in, first-called order (LIFO).
|
||||
*
|
||||
* To remove a parameter event callback, use:
|
||||
*
|
||||
* param_handler->remove_event_parameter_callback(handle);
|
||||
*/
|
||||
class ParameterEventHandler
|
||||
{
|
||||
public:
|
||||
/// Construct a parameter events monitor.
|
||||
/**
|
||||
* \param[in] node The node to use to create any required subscribers.
|
||||
* \param[in] qos The QoS settings to use for any subscriptions.
|
||||
*/
|
||||
template<typename NodeT>
|
||||
ParameterEventHandler(
|
||||
NodeT node,
|
||||
const rclcpp::QoS & qos =
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)))
|
||||
{
|
||||
node_base_ = rclcpp::node_interfaces::get_node_base_interface(node);
|
||||
auto node_topics = rclcpp::node_interfaces::get_node_topics_interface(node);
|
||||
|
||||
event_subscription_ = rclcpp::create_subscription<rcl_interfaces::msg::ParameterEvent>(
|
||||
node_topics, "/parameter_events", qos,
|
||||
std::bind(&ParameterEventHandler::event_callback, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
using ParameterEventCallbackType =
|
||||
ParameterEventCallbackHandle::ParameterEventCallbackType;
|
||||
|
||||
/// Set a callback for all parameter events.
|
||||
/**
|
||||
* This function may be called multiple times to set multiple parameter event callbacks.
|
||||
*
|
||||
* \param[in] callback Function callback to be invoked on parameter updates.
|
||||
* \returns A handle used to refer to the callback.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
ParameterEventCallbackHandle::SharedPtr
|
||||
add_parameter_event_callback(
|
||||
ParameterEventCallbackType callback);
|
||||
|
||||
/// Remove parameter event callback registered with add_parameter_event_callback.
|
||||
/**
|
||||
* \param[in] callback_handle Handle of the callback to remove.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_parameter_event_callback(
|
||||
ParameterEventCallbackHandle::SharedPtr callback_handle);
|
||||
|
||||
using ParameterCallbackType = ParameterCallbackHandle::ParameterCallbackType;
|
||||
|
||||
/// Add a callback for a specified parameter.
|
||||
/**
|
||||
* If a node_name is not provided, defaults to the current node.
|
||||
*
|
||||
* \param[in] parameter_name Name of parameter to monitor.
|
||||
* \param[in] callback Function callback to be invoked upon parameter update.
|
||||
* \param[in] node_name Name of node which hosts the parameter.
|
||||
* \returns A handle used to refer to the callback.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
ParameterCallbackHandle::SharedPtr
|
||||
add_parameter_callback(
|
||||
const std::string & parameter_name,
|
||||
ParameterCallbackType callback,
|
||||
const std::string & node_name = "");
|
||||
|
||||
/// Remove a parameter callback registered with add_parameter_callback.
|
||||
/**
|
||||
* The parameter name and node name are inspected from the callback handle. The callback handle
|
||||
* is erased from the list of callback handles on the {parameter_name, node_name} in the map.
|
||||
* An error is thrown if the handle does not exist and/or was already removed.
|
||||
*
|
||||
* \param[in] callback_handle Handle of the callback to remove.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_parameter_callback(
|
||||
ParameterCallbackHandle::SharedPtr callback_handle);
|
||||
|
||||
/// Get an rclcpp::Parameter from a parameter event.
|
||||
/**
|
||||
* If a node_name is not provided, defaults to the current node.
|
||||
*
|
||||
* \param[in] event Event msg to be inspected.
|
||||
* \param[out] parameter Reference to rclcpp::Parameter to be assigned.
|
||||
* \param[in] parameter_name Name of parameter.
|
||||
* \param[in] node_name Name of node which hosts the parameter.
|
||||
* \returns Output parameter is set with requested parameter info and returns true if
|
||||
* requested parameter name and node is in event. Otherwise, returns false.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
static bool
|
||||
get_parameter_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event,
|
||||
rclcpp::Parameter & parameter,
|
||||
const std::string parameter_name,
|
||||
const std::string node_name = "");
|
||||
|
||||
/// Get an rclcpp::Parameter from parameter event
|
||||
/**
|
||||
* If a node_name is not provided, defaults to the current node.
|
||||
*
|
||||
* The user is responsible to check if the returned parameter has been properly assigned.
|
||||
* By default, if the requested parameter is not found in the event, the returned parameter
|
||||
* has parameter value of type rclcpp::PARAMETER_NOT_SET.
|
||||
*
|
||||
* \param[in] event Event msg to be inspected.
|
||||
* \param[in] parameter_name Name of parameter.
|
||||
* \param[in] node_name Name of node which hosts the parameter.
|
||||
* \returns The resultant rclcpp::Parameter from the event.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
static rclcpp::Parameter
|
||||
get_parameter_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event,
|
||||
const std::string parameter_name,
|
||||
const std::string node_name = "");
|
||||
|
||||
/// Get all rclcpp::Parameter values from a parameter event
|
||||
/**
|
||||
* \param[in] event Event msg to be inspected.
|
||||
* \returns A vector rclcpp::Parameter values from the event.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
static std::vector<rclcpp::Parameter>
|
||||
get_parameters_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event);
|
||||
|
||||
using CallbacksContainerType = std::list<ParameterCallbackHandle::WeakPtr>;
|
||||
|
||||
protected:
|
||||
/// Callback for parameter events subscriptions.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
event_callback(const rcl_interfaces::msg::ParameterEvent::SharedPtr event);
|
||||
|
||||
// Utility function for resolving node path.
|
||||
std::string resolve_path(const std::string & path);
|
||||
|
||||
// Node interface used for base functionality
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base_;
|
||||
|
||||
// *INDENT-OFF* Uncrustify doesn't handle indented public/private labels
|
||||
// Hash function for string pair required in std::unordered_map
|
||||
// See: https://stackoverflow.com/questions/35985960/c-why-is-boosthash-combine-the-best-way-to-combine-hash-values
|
||||
class StringPairHash
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
inline void hash_combine(std::size_t & seed, const T & v) const
|
||||
{
|
||||
std::hash<T> hasher;
|
||||
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
inline size_t operator()(const std::pair<std::string, std::string> & s) const
|
||||
{
|
||||
size_t seed = 0;
|
||||
hash_combine(seed, s.first);
|
||||
hash_combine(seed, s.second);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
// *INDENT-ON*
|
||||
|
||||
// Map container for registered parameters
|
||||
std::unordered_map<
|
||||
std::pair<std::string, std::string>,
|
||||
CallbacksContainerType,
|
||||
StringPairHash
|
||||
> parameter_callbacks_;
|
||||
|
||||
rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr event_subscription_;
|
||||
|
||||
std::list<ParameterEventCallbackHandle::WeakPtr> event_callbacks_;
|
||||
|
||||
std::recursive_mutex mutex_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__PARAMETER_EVENT_HANDLER_HPP_
|
||||
@@ -95,12 +95,7 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
get_allocator() const
|
||||
{
|
||||
if (!this->allocator) {
|
||||
// TODO(wjwwood): I would like to use the commented line instead, but
|
||||
// cppcheck 1.89 fails with:
|
||||
// Syntax Error: AST broken, binary operator '>' doesn't have two operands.
|
||||
// return std::make_shared<Allocator>();
|
||||
std::shared_ptr<Allocator> tmp(new Allocator());
|
||||
return tmp;
|
||||
return std::make_shared<Allocator>();
|
||||
}
|
||||
return this->allocator;
|
||||
}
|
||||
|
||||
@@ -147,14 +147,15 @@
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/parameter_client.hpp"
|
||||
#include "rclcpp/parameter_event_handler.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/parameter_service.hpp"
|
||||
#include "rclcpp/rate.hpp"
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/wait_set.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
#include "rclcpp/wait_set.hpp"
|
||||
|
||||
#endif // RCLCPP__RCLCPP_HPP_
|
||||
|
||||
@@ -180,7 +180,7 @@ public:
|
||||
std::weak_ptr<rcl_node_t> weak_node_handle(node_handle_);
|
||||
// rcl does the static memory allocation here
|
||||
service_handle_ = std::shared_ptr<rcl_service_t>(
|
||||
new rcl_service_t, [weak_node_handle](rcl_service_t * service)
|
||||
new rcl_service_t, [weak_node_handle, service_name](rcl_service_t * service)
|
||||
{
|
||||
auto handle = weak_node_handle.lock();
|
||||
if (handle) {
|
||||
@@ -192,10 +192,10 @@ public:
|
||||
rcl_reset_error();
|
||||
}
|
||||
} else {
|
||||
RCLCPP_ERROR(
|
||||
RCLCPP_ERROR_STREAM(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Error in destruction of rcl service handle: "
|
||||
"the Node Handle was destructed too early. You will leak memory");
|
||||
"Error in destruction of rcl service handle " << service_name <<
|
||||
": the Node Handle was destructed too early. You will leak memory");
|
||||
}
|
||||
delete service;
|
||||
});
|
||||
|
||||
@@ -104,7 +104,7 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
rcl_subscription_options_t result = rcl_subscription_get_default_options();
|
||||
using AllocatorTraits = std::allocator_traits<Allocator>;
|
||||
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
|
||||
auto message_alloc = std::make_shared<MessageAllocatorT>(*allocator.get());
|
||||
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
|
||||
result.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc);
|
||||
result.qos = qos.get_rmw_qos_profile();
|
||||
result.rmw_subscription_options.ignore_local_publications = this->ignore_local_publications;
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp</name>
|
||||
<version>6.3.1</version>
|
||||
<version>7.0.1</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
|
||||
|
||||
@@ -113,7 +113,7 @@ Clock::get_clock_mutex() noexcept
|
||||
|
||||
void
|
||||
Clock::on_time_jump(
|
||||
const struct rcl_time_jump_t * time_jump,
|
||||
const rcl_time_jump_t * time_jump,
|
||||
bool before_jump,
|
||||
void * user_data)
|
||||
{
|
||||
|
||||
@@ -291,7 +291,7 @@ Context::get_domain_id() const
|
||||
}
|
||||
|
||||
std::string
|
||||
Context::shutdown_reason()
|
||||
Context::shutdown_reason() const
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(init_mutex_);
|
||||
return shutdown_reason_;
|
||||
|
||||
75
rclcpp/src/rclcpp/detail/mutex_two_priorities.cpp
Normal file
75
rclcpp/src/rclcpp/detail/mutex_two_priorities.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// Copyright 2021 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/detail/mutex_two_priorities.hpp"
|
||||
|
||||
#include <mutex>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
using LowPriorityLockable = MutexTwoPriorities::LowPriorityLockable;
|
||||
using HighPriorityLockable = MutexTwoPriorities::HighPriorityLockable;
|
||||
|
||||
HighPriorityLockable::HighPriorityLockable(MutexTwoPriorities & parent)
|
||||
: parent_(parent)
|
||||
{}
|
||||
|
||||
void
|
||||
HighPriorityLockable::lock()
|
||||
{
|
||||
parent_.data_.lock();
|
||||
}
|
||||
|
||||
void
|
||||
HighPriorityLockable::unlock()
|
||||
{
|
||||
parent_.data_.unlock();
|
||||
}
|
||||
|
||||
LowPriorityLockable::LowPriorityLockable(MutexTwoPriorities & parent)
|
||||
: parent_(parent)
|
||||
{}
|
||||
|
||||
void
|
||||
LowPriorityLockable::lock()
|
||||
{
|
||||
std::unique_lock<std::mutex> barrier_guard{parent_.barrier_};
|
||||
parent_.data_.lock();
|
||||
barrier_guard.release();
|
||||
}
|
||||
|
||||
void
|
||||
LowPriorityLockable::unlock()
|
||||
{
|
||||
std::lock_guard<std::mutex> barrier_guard{parent_.barrier_, std::adopt_lock};
|
||||
parent_.data_.unlock();
|
||||
}
|
||||
|
||||
HighPriorityLockable
|
||||
MutexTwoPriorities::get_high_priority_lockable()
|
||||
{
|
||||
return HighPriorityLockable{*this};
|
||||
}
|
||||
|
||||
LowPriorityLockable
|
||||
MutexTwoPriorities::get_low_priority_lockable()
|
||||
{
|
||||
return LowPriorityLockable{*this};
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
77
rclcpp/src/rclcpp/detail/resolve_parameter_overrides.cpp
Normal file
77
rclcpp/src/rclcpp/detail/resolve_parameter_overrides.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2021 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "./resolve_parameter_overrides.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl_yaml_param_parser/parser.h"
|
||||
|
||||
#include "rclcpp/scope_exit.hpp"
|
||||
#include "rclcpp/parameter_map.hpp"
|
||||
|
||||
std::map<std::string, rclcpp::ParameterValue>
|
||||
rclcpp::detail::resolve_parameter_overrides(
|
||||
const std::string & node_fqn,
|
||||
const std::vector<rclcpp::Parameter> & parameter_overrides,
|
||||
const rcl_arguments_t * local_args,
|
||||
const rcl_arguments_t * global_args)
|
||||
{
|
||||
std::map<std::string, rclcpp::ParameterValue> result;
|
||||
|
||||
// global before local so that local overwrites global
|
||||
std::array<const rcl_arguments_t *, 2> argument_sources = {global_args, local_args};
|
||||
|
||||
// Get fully qualified node name post-remapping to use to find node's params in yaml files
|
||||
|
||||
for (const rcl_arguments_t * source : argument_sources) {
|
||||
if (!source) {
|
||||
continue;
|
||||
}
|
||||
rcl_params_t * params = NULL;
|
||||
rcl_ret_t ret = rcl_arguments_get_param_overrides(source, ¶ms);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
if (params) {
|
||||
auto cleanup_params = make_scope_exit(
|
||||
[params]() {
|
||||
rcl_yaml_node_struct_fini(params);
|
||||
});
|
||||
rclcpp::ParameterMap initial_map = rclcpp::parameter_map_from(params);
|
||||
|
||||
// Enforce wildcard matching precedence
|
||||
// TODO(cottsay) implement further wildcard matching
|
||||
const std::array<std::string, 2> node_matching_names{"/**", node_fqn};
|
||||
for (const auto & node_name : node_matching_names) {
|
||||
if (initial_map.count(node_name) > 0) {
|
||||
// Combine parameter yaml files, overwriting values in older ones
|
||||
for (const rclcpp::Parameter & param : initial_map.at(node_name)) {
|
||||
result[param.get_name()] =
|
||||
rclcpp::ParameterValue(param.get_value_message());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parameter overrides passed to constructor will overwrite overrides from yaml file sources
|
||||
for (auto & param : parameter_overrides) {
|
||||
result[param.get_name()] =
|
||||
rclcpp::ParameterValue(param.get_value_message());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
44
rclcpp/src/rclcpp/detail/resolve_parameter_overrides.hpp
Normal file
44
rclcpp/src/rclcpp/detail/resolve_parameter_overrides.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2021 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__DETAIL__RESOLVE_PARAMETER_OVERRIDES_HPP_
|
||||
#define RCLCPP__DETAIL__RESOLVE_PARAMETER_OVERRIDES_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/arguments.h"
|
||||
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/parameter_value.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
/// \internal Get the parameter overrides from the arguments.
|
||||
RCLCPP_LOCAL
|
||||
std::map<std::string, rclcpp::ParameterValue>
|
||||
resolve_parameter_overrides(
|
||||
const std::string & node_name,
|
||||
const std::vector<rclcpp::Parameter> & parameter_overrides,
|
||||
const rcl_arguments_t * local_args,
|
||||
const rcl_arguments_t * global_args);
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DETAIL__RESOLVE_PARAMETER_OVERRIDES_HPP_
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/scope_exit.hpp"
|
||||
|
||||
using rclcpp::detail::MutexTwoPriorities;
|
||||
using rclcpp::executors::MultiThreadedExecutor;
|
||||
|
||||
MultiThreadedExecutor::MultiThreadedExecutor(
|
||||
@@ -51,7 +52,8 @@ MultiThreadedExecutor::spin()
|
||||
std::vector<std::thread> threads;
|
||||
size_t thread_id = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
|
||||
auto low_priority_wait_mutex = wait_mutex_.get_low_priority_lockable();
|
||||
std::lock_guard<MutexTwoPriorities::LowPriorityLockable> wait_lock(low_priority_wait_mutex);
|
||||
for (; thread_id < number_of_threads_ - 1; ++thread_id) {
|
||||
auto func = std::bind(&MultiThreadedExecutor::run, this, thread_id);
|
||||
threads.emplace_back(func);
|
||||
@@ -76,7 +78,8 @@ MultiThreadedExecutor::run(size_t)
|
||||
while (rclcpp::ok(this->context_) && spinning.load()) {
|
||||
rclcpp::AnyExecutable any_exec;
|
||||
{
|
||||
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
|
||||
auto low_priority_wait_mutex = wait_mutex_.get_low_priority_lockable();
|
||||
std::lock_guard<MutexTwoPriorities::LowPriorityLockable> wait_lock(low_priority_wait_mutex);
|
||||
if (!rclcpp::ok(this->context_) || !spinning.load()) {
|
||||
return;
|
||||
}
|
||||
@@ -103,7 +106,8 @@ MultiThreadedExecutor::run(size_t)
|
||||
execute_any_executable(any_exec);
|
||||
|
||||
if (any_exec.timer) {
|
||||
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
|
||||
auto high_priority_wait_mutex = wait_mutex_.get_high_priority_lockable();
|
||||
std::lock_guard<MutexTwoPriorities::HighPriorityLockable> wait_lock(high_priority_wait_mutex);
|
||||
auto it = scheduled_timers_.find(any_exec.timer);
|
||||
if (it != scheduled_timers_.end()) {
|
||||
scheduled_timers_.erase(it);
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -20,6 +21,9 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/arguments.h"
|
||||
|
||||
#include "rclcpp/detail/qos_parameters.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/graph_listener.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
@@ -33,9 +37,12 @@
|
||||
#include "rclcpp/node_interfaces/node_timers.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics.hpp"
|
||||
#include "rclcpp/node_interfaces/node_waitables.hpp"
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
|
||||
#include "rmw/validate_namespace.h"
|
||||
|
||||
#include "./detail/resolve_parameter_overrides.hpp"
|
||||
|
||||
using rclcpp::Node;
|
||||
using rclcpp::NodeOptions;
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
@@ -94,6 +101,45 @@ Node::Node(
|
||||
{
|
||||
}
|
||||
|
||||
static
|
||||
rclcpp::QoS
|
||||
get_parameter_events_qos(
|
||||
rclcpp::node_interfaces::NodeBaseInterface & node_base,
|
||||
const rclcpp::NodeOptions & options)
|
||||
{
|
||||
auto final_qos = options.parameter_event_qos();
|
||||
const rcl_arguments_t * global_args = nullptr;
|
||||
auto * rcl_options = options.get_rcl_node_options();
|
||||
if (rcl_options->use_global_arguments) {
|
||||
auto context_ptr = node_base.get_context()->get_rcl_context();
|
||||
global_args = &(context_ptr->global_arguments);
|
||||
}
|
||||
|
||||
auto parameter_overrides = rclcpp::detail::resolve_parameter_overrides(
|
||||
node_base.get_fully_qualified_name(),
|
||||
options.parameter_overrides(),
|
||||
&rcl_options->arguments,
|
||||
global_args);
|
||||
|
||||
auto final_topic_name = node_base.resolve_topic_or_service_name("/parameter_events", false);
|
||||
auto prefix = "qos_overrides." + final_topic_name + ".";
|
||||
std::array<rclcpp::QosPolicyKind, 4> policies = {
|
||||
rclcpp::QosPolicyKind::Depth,
|
||||
rclcpp::QosPolicyKind::Durability,
|
||||
rclcpp::QosPolicyKind::History,
|
||||
rclcpp::QosPolicyKind::Reliability,
|
||||
};
|
||||
for (const auto & policy : policies) {
|
||||
auto param_name = prefix + rclcpp::qos_policy_kind_to_cstr(policy);
|
||||
auto it = parameter_overrides.find(param_name);
|
||||
auto value = it != parameter_overrides.end() ?
|
||||
it->second :
|
||||
rclcpp::detail::get_default_qos_param_value(policy, options.parameter_event_qos());
|
||||
rclcpp::detail::apply_qos_override(policy, value, final_qos);
|
||||
}
|
||||
return final_qos;
|
||||
}
|
||||
|
||||
Node::Node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
@@ -126,7 +172,9 @@ Node::Node(
|
||||
options.parameter_overrides(),
|
||||
options.start_parameter_services(),
|
||||
options.start_parameter_event_publisher(),
|
||||
options.parameter_event_qos(),
|
||||
// This is needed in order to apply parameter overrides to the qos profile provided in
|
||||
// options.
|
||||
get_parameter_events_qos(*node_base_, options),
|
||||
options.parameter_event_publisher_options(),
|
||||
options.allow_undeclared_parameters(),
|
||||
options.automatically_declare_parameters_from_overrides()
|
||||
@@ -146,6 +194,20 @@ Node::Node(
|
||||
sub_namespace_(""),
|
||||
effective_namespace_(create_effective_namespace(this->get_namespace(), sub_namespace_))
|
||||
{
|
||||
// we have got what we wanted directly from the overrides,
|
||||
// but declare the parameters anyway so they are visible.
|
||||
rclcpp::detail::declare_qos_parameters(
|
||||
rclcpp::QosOverridingOptions
|
||||
{
|
||||
QosPolicyKind::Depth,
|
||||
QosPolicyKind::Durability,
|
||||
QosPolicyKind::History,
|
||||
QosPolicyKind::Reliability,
|
||||
},
|
||||
node_parameters_,
|
||||
node_topics_->resolve_topic_name("/parameter_events"),
|
||||
options.parameter_event_qos(),
|
||||
rclcpp::detail::PublisherQosParametersTraits{});
|
||||
}
|
||||
|
||||
Node::Node(
|
||||
@@ -185,7 +247,18 @@ Node::Node(
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{}
|
||||
{
|
||||
// release sub-interfaces in an order that allows them to consult with node_base during tear-down
|
||||
node_waitables_.reset();
|
||||
node_time_source_.reset();
|
||||
node_parameters_.reset();
|
||||
node_clock_.reset();
|
||||
node_services_.reset();
|
||||
node_topics_.reset();
|
||||
node_timers_.reset();
|
||||
node_logging_.reset();
|
||||
node_graph_.reset();
|
||||
}
|
||||
|
||||
const char *
|
||||
Node::get_name() const
|
||||
@@ -219,6 +292,24 @@ Node::create_callback_group(
|
||||
return node_base_->create_callback_group(group_type, automatically_add_to_executor_with_node);
|
||||
}
|
||||
|
||||
const rclcpp::ParameterValue &
|
||||
Node::declare_parameter(const std::string & name)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
return this->node_parameters_->declare_parameter(name);
|
||||
#ifndef _WIN32
|
||||
# pragma GCC diagnostic pop
|
||||
#else
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
|
||||
const rclcpp::ParameterValue &
|
||||
Node::declare_parameter(
|
||||
const std::string & name,
|
||||
@@ -233,6 +324,20 @@ Node::declare_parameter(
|
||||
ignore_override);
|
||||
}
|
||||
|
||||
const rclcpp::ParameterValue &
|
||||
Node::declare_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::ParameterType type,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
|
||||
bool ignore_override)
|
||||
{
|
||||
return this->node_parameters_->declare_parameter(
|
||||
name,
|
||||
type,
|
||||
parameter_descriptor,
|
||||
ignore_override);
|
||||
}
|
||||
|
||||
void
|
||||
Node::undeclare_parameter(const std::string & name)
|
||||
{
|
||||
|
||||
@@ -16,8 +16,10 @@
|
||||
|
||||
#include <rcl_yaml_param_parser/parser.h>
|
||||
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
@@ -34,6 +36,8 @@
|
||||
#include "rcutils/logging_macros.h"
|
||||
#include "rmw/qos_profiles.h"
|
||||
|
||||
#include "../detail/resolve_parameter_overrides.hpp"
|
||||
|
||||
using rclcpp::node_interfaces::NodeParameters;
|
||||
|
||||
NodeParameters::NodeParameters(
|
||||
@@ -85,60 +89,27 @@ NodeParameters::NodeParameters(
|
||||
throw std::runtime_error("Need valid node options in NodeParameters");
|
||||
}
|
||||
|
||||
std::vector<const rcl_arguments_t *> argument_sources;
|
||||
// global before local so that local overwrites global
|
||||
const rcl_arguments_t * global_args = nullptr;
|
||||
if (options->use_global_arguments) {
|
||||
auto context_ptr = node_base->get_context()->get_rcl_context();
|
||||
argument_sources.push_back(&(context_ptr->global_arguments));
|
||||
global_args = &(context_ptr->global_arguments);
|
||||
}
|
||||
argument_sources.push_back(&options->arguments);
|
||||
|
||||
// Get fully qualified node name post-remapping to use to find node's params in yaml files
|
||||
combined_name_ = node_base->get_fully_qualified_name();
|
||||
|
||||
for (const rcl_arguments_t * source : argument_sources) {
|
||||
rcl_params_t * params = NULL;
|
||||
rcl_ret_t ret = rcl_arguments_get_param_overrides(source, ¶ms);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
if (params) {
|
||||
auto cleanup_params = make_scope_exit(
|
||||
[params]() {
|
||||
rcl_yaml_node_struct_fini(params);
|
||||
});
|
||||
rclcpp::ParameterMap initial_map = rclcpp::parameter_map_from(params);
|
||||
|
||||
// Enforce wildcard matching precedence
|
||||
// TODO(cottsay) implement further wildcard matching
|
||||
const std::vector<std::string> node_matching_names{"/**", combined_name_};
|
||||
for (const auto & node_name : node_matching_names) {
|
||||
if (initial_map.count(node_name) > 0) {
|
||||
// Combine parameter yaml files, overwriting values in older ones
|
||||
for (const rclcpp::Parameter & param : initial_map.at(node_name)) {
|
||||
parameter_overrides_[param.get_name()] =
|
||||
rclcpp::ParameterValue(param.get_value_message());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parameter overrides passed to constructor will overwrite overrides from yaml file sources
|
||||
for (auto & param : parameter_overrides) {
|
||||
parameter_overrides_[param.get_name()] =
|
||||
rclcpp::ParameterValue(param.get_value_message());
|
||||
}
|
||||
parameter_overrides_ = rclcpp::detail::resolve_parameter_overrides(
|
||||
combined_name_, parameter_overrides, &options->arguments, global_args);
|
||||
|
||||
// If asked, initialize any parameters that ended up in the initial parameter values,
|
||||
// but did not get declared explcitily by this point.
|
||||
if (automatically_declare_parameters_from_overrides) {
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
for (const auto & pair : this->get_parameter_overrides()) {
|
||||
if (!this->has_parameter(pair.first)) {
|
||||
this->declare_parameter(
|
||||
pair.first,
|
||||
pair.second,
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
descriptor,
|
||||
true);
|
||||
}
|
||||
}
|
||||
@@ -165,14 +136,13 @@ __are_doubles_equal(double x, double y, double ulp = 100.0)
|
||||
return std::abs(x - y) <= std::numeric_limits<double>::epsilon() * std::abs(x + y) * ulp;
|
||||
}
|
||||
|
||||
RCLCPP_LOCAL
|
||||
inline
|
||||
void
|
||||
format_reason(std::string & reason, const std::string & name, const char * range_type)
|
||||
static
|
||||
std::string
|
||||
format_range_reason(const std::string & name, const char * range_type)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
ss << "Parameter {" << name << "} doesn't comply with " << range_type << " range.";
|
||||
reason = ss.str();
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
RCLCPP_LOCAL
|
||||
@@ -191,7 +161,7 @@ __check_parameter_value_in_range(
|
||||
}
|
||||
if ((v < integer_range.from_value) || (v > integer_range.to_value)) {
|
||||
result.successful = false;
|
||||
format_reason(result.reason, descriptor.name, "integer");
|
||||
result.reason = format_range_reason(descriptor.name, "integer");
|
||||
return result;
|
||||
}
|
||||
if (integer_range.step == 0) {
|
||||
@@ -201,7 +171,7 @@ __check_parameter_value_in_range(
|
||||
return result;
|
||||
}
|
||||
result.successful = false;
|
||||
format_reason(result.reason, descriptor.name, "integer");
|
||||
result.reason = format_range_reason(descriptor.name, "integer");
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -213,7 +183,7 @@ __check_parameter_value_in_range(
|
||||
}
|
||||
if ((v < fp_range.from_value) || (v > fp_range.to_value)) {
|
||||
result.successful = false;
|
||||
format_reason(result.reason, descriptor.name, "floating point");
|
||||
result.reason = format_range_reason(descriptor.name, "floating point");
|
||||
return result;
|
||||
}
|
||||
if (fp_range.step == 0.0) {
|
||||
@@ -224,29 +194,62 @@ __check_parameter_value_in_range(
|
||||
return result;
|
||||
}
|
||||
result.successful = false;
|
||||
format_reason(result.reason, descriptor.name, "floating point");
|
||||
result.reason = format_range_reason(descriptor.name, "floating point");
|
||||
return result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
std::string
|
||||
format_type_reason(
|
||||
const std::string & name, const std::string & old_type, const std::string & new_type)
|
||||
{
|
||||
std::ostringstream ss;
|
||||
// WARN: A condition later depends on this message starting with "Wrong parameter type",
|
||||
// check `declare_parameter` if you modify this!
|
||||
ss << "Wrong parameter type, parameter {" << name << "} is of type {" << old_type <<
|
||||
"}, setting it to {" << new_type << "} is not allowed.";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
// Return true if parameter values comply with the descriptors in parameter_infos.
|
||||
RCLCPP_LOCAL
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
__check_parameters(
|
||||
std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameter_infos,
|
||||
const std::vector<rclcpp::Parameter> & parameters)
|
||||
const std::vector<rclcpp::Parameter> & parameters,
|
||||
bool allow_undeclared)
|
||||
{
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
result.successful = true;
|
||||
for (const rclcpp::Parameter & parameter : parameters) {
|
||||
const rcl_interfaces::msg::ParameterDescriptor & descriptor =
|
||||
parameter_infos[parameter.get_name()].descriptor;
|
||||
std::string name = parameter.get_name();
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
if (allow_undeclared) {
|
||||
auto it = parameter_infos.find(name);
|
||||
if (it != parameter_infos.cend()) {
|
||||
descriptor = it->second.descriptor;
|
||||
} else {
|
||||
// implicitly declared parameters are dinamically typed!
|
||||
descriptor.dynamic_typing = true;
|
||||
}
|
||||
} else {
|
||||
descriptor = parameter_infos[name].descriptor;
|
||||
}
|
||||
const auto new_type = parameter.get_type();
|
||||
const auto specified_type = static_cast<rclcpp::ParameterType>(descriptor.type);
|
||||
result.successful = descriptor.dynamic_typing || specified_type == new_type;
|
||||
if (!result.successful) {
|
||||
result.reason = format_type_reason(
|
||||
name, rclcpp::to_string(specified_type), rclcpp::to_string(new_type));
|
||||
return result;
|
||||
}
|
||||
result = __check_parameter_value_in_range(
|
||||
descriptor,
|
||||
parameter.get_parameter_value());
|
||||
if (!result.successful) {
|
||||
break;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -293,7 +296,8 @@ __set_parameters_atomically_common(
|
||||
const std::vector<rclcpp::Parameter> & parameters,
|
||||
std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameter_infos,
|
||||
CallbacksContainerType & callback_container,
|
||||
const OnParametersSetCallbackType & callback)
|
||||
const OnParametersSetCallbackType & callback,
|
||||
bool allow_undeclared = false)
|
||||
{
|
||||
// Call the user callback to see if the new value(s) are allowed.
|
||||
rcl_interfaces::msg::SetParametersResult result =
|
||||
@@ -302,7 +306,7 @@ __set_parameters_atomically_common(
|
||||
return result;
|
||||
}
|
||||
// Check if the value being set complies with the descriptor.
|
||||
result = __check_parameters(parameter_infos, parameters);
|
||||
result = __check_parameters(parameter_infos, parameters, allow_undeclared);
|
||||
if (!result.successful) {
|
||||
return result;
|
||||
}
|
||||
@@ -353,6 +357,10 @@ __declare_parameter_common(
|
||||
callback_container,
|
||||
callback);
|
||||
|
||||
if (!result.successful) {
|
||||
return result;
|
||||
}
|
||||
|
||||
// Add declared parameters to storage.
|
||||
parameters_out[name] = parameter_infos.at(name);
|
||||
|
||||
@@ -364,6 +372,99 @@ __declare_parameter_common(
|
||||
return result;
|
||||
}
|
||||
|
||||
static
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter_helper(
|
||||
const std::string & name,
|
||||
rclcpp::ParameterType type,
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
rcl_interfaces::msg::ParameterDescriptor parameter_descriptor,
|
||||
bool ignore_override,
|
||||
std::map<std::string, rclcpp::node_interfaces::ParameterInfo> & parameters,
|
||||
const std::map<std::string, rclcpp::ParameterValue> & overrides,
|
||||
CallbacksContainerType & callback_container,
|
||||
const OnParametersSetCallbackType & callback,
|
||||
rclcpp::Publisher<rcl_interfaces::msg::ParameterEvent> * events_publisher,
|
||||
const std::string & combined_name,
|
||||
rclcpp::node_interfaces::NodeClockInterface & node_clock)
|
||||
{
|
||||
// TODO(sloretz) parameter name validation
|
||||
if (name.empty()) {
|
||||
throw rclcpp::exceptions::InvalidParametersException("parameter name must not be empty");
|
||||
}
|
||||
|
||||
// Error if this parameter has already been declared and is different
|
||||
if (__lockless_has_parameter(parameters, name)) {
|
||||
throw rclcpp::exceptions::ParameterAlreadyDeclaredException(
|
||||
"parameter '" + name + "' has already been declared");
|
||||
}
|
||||
|
||||
if (!parameter_descriptor.dynamic_typing) {
|
||||
if (rclcpp::PARAMETER_NOT_SET == type) {
|
||||
type = default_value.get_type();
|
||||
}
|
||||
if (rclcpp::PARAMETER_NOT_SET == type) {
|
||||
throw rclcpp::exceptions::InvalidParameterTypeException{
|
||||
name,
|
||||
"cannot declare a statically typed parameter with an uninitialized value"
|
||||
};
|
||||
}
|
||||
parameter_descriptor.type = static_cast<uint8_t>(type);
|
||||
}
|
||||
|
||||
if (
|
||||
rclcpp::PARAMETER_NOT_SET == default_value.get_type() &&
|
||||
overrides.find(name) == overrides.end() &&
|
||||
parameter_descriptor.dynamic_typing == false)
|
||||
{
|
||||
throw rclcpp::exceptions::NoParameterOverrideProvided(name);
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ParameterEvent parameter_event;
|
||||
auto result = __declare_parameter_common(
|
||||
name,
|
||||
default_value,
|
||||
parameter_descriptor,
|
||||
parameters,
|
||||
overrides,
|
||||
callback_container,
|
||||
callback,
|
||||
¶meter_event,
|
||||
ignore_override);
|
||||
|
||||
// If it failed to be set, then throw an exception.
|
||||
if (!result.successful) {
|
||||
constexpr const char type_error_msg_start[] = "Wrong parameter type";
|
||||
if (
|
||||
0u == std::strncmp(
|
||||
result.reason.c_str(), type_error_msg_start, sizeof(type_error_msg_start) - 1))
|
||||
{
|
||||
// TODO(ivanpauno): Refactor the logic so we don't need the above `strncmp` and we can
|
||||
// detect between both exceptions more elegantly.
|
||||
throw rclcpp::exceptions::InvalidParameterTypeException(name, result.reason);
|
||||
}
|
||||
throw rclcpp::exceptions::InvalidParameterValueException(
|
||||
"parameter '" + name + "' could not be set: " + result.reason);
|
||||
}
|
||||
|
||||
// Publish if events_publisher_ is not nullptr, which may be if disabled in the constructor.
|
||||
if (nullptr != events_publisher) {
|
||||
parameter_event.node = combined_name;
|
||||
parameter_event.stamp = node_clock.get_clock()->now();
|
||||
events_publisher->publish(parameter_event);
|
||||
}
|
||||
|
||||
return parameters.at(name).value;
|
||||
}
|
||||
|
||||
const rclcpp::ParameterValue &
|
||||
NodeParameters::declare_parameter(const std::string & name)
|
||||
{
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
return this->declare_parameter(name, rclcpp::ParameterValue{}, descriptor, false);
|
||||
}
|
||||
|
||||
const rclcpp::ParameterValue &
|
||||
NodeParameters::declare_parameter(
|
||||
const std::string & name,
|
||||
@@ -372,46 +473,57 @@ NodeParameters::declare_parameter(
|
||||
bool ignore_override)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
|
||||
ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
|
||||
|
||||
// TODO(sloretz) parameter name validation
|
||||
if (name.empty()) {
|
||||
throw rclcpp::exceptions::InvalidParametersException("parameter name must not be empty");
|
||||
}
|
||||
|
||||
// Error if this parameter has already been declared and is different
|
||||
if (__lockless_has_parameter(parameters_, name)) {
|
||||
throw rclcpp::exceptions::ParameterAlreadyDeclaredException(
|
||||
"parameter '" + name + "' has already been declared");
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ParameterEvent parameter_event;
|
||||
auto result = __declare_parameter_common(
|
||||
return declare_parameter_helper(
|
||||
name,
|
||||
rclcpp::PARAMETER_NOT_SET,
|
||||
default_value,
|
||||
parameter_descriptor,
|
||||
ignore_override,
|
||||
parameters_,
|
||||
parameter_overrides_,
|
||||
on_parameters_set_callback_container_,
|
||||
on_parameters_set_callback_,
|
||||
¶meter_event,
|
||||
ignore_override);
|
||||
events_publisher_.get(),
|
||||
combined_name_,
|
||||
*node_clock_);
|
||||
}
|
||||
|
||||
// If it failed to be set, then throw an exception.
|
||||
if (!result.successful) {
|
||||
throw rclcpp::exceptions::InvalidParameterValueException(
|
||||
"parameter '" + name + "' could not be set: " + result.reason);
|
||||
const rclcpp::ParameterValue &
|
||||
NodeParameters::declare_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::ParameterType type,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
|
||||
bool ignore_override)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
ParameterMutationRecursionGuard guard(parameter_modification_enabled_);
|
||||
|
||||
if (rclcpp::PARAMETER_NOT_SET == type) {
|
||||
throw std::invalid_argument{
|
||||
"declare_parameter(): the provided parameter type cannot be rclcpp::PARAMETER_NOT_SET"};
|
||||
}
|
||||
|
||||
// Publish if events_publisher_ is not nullptr, which may be if disabled in the constructor.
|
||||
if (nullptr != events_publisher_) {
|
||||
parameter_event.node = combined_name_;
|
||||
parameter_event.stamp = node_clock_->get_clock()->now();
|
||||
events_publisher_->publish(parameter_event);
|
||||
if (parameter_descriptor.dynamic_typing == true) {
|
||||
throw std::invalid_argument{
|
||||
"declare_parameter(): cannot declare parameter of specific type and pass descriptor"
|
||||
"with `dynamic_typing=true`"};
|
||||
}
|
||||
|
||||
return parameters_.at(name).value;
|
||||
return declare_parameter_helper(
|
||||
name,
|
||||
type,
|
||||
rclcpp::ParameterValue{},
|
||||
parameter_descriptor,
|
||||
ignore_override,
|
||||
parameters_,
|
||||
parameter_overrides_,
|
||||
on_parameters_set_callback_container_,
|
||||
on_parameters_set_callback_,
|
||||
events_publisher_.get(),
|
||||
combined_name_,
|
||||
*node_clock_);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -431,6 +543,10 @@ NodeParameters::undeclare_parameter(const std::string & name)
|
||||
throw rclcpp::exceptions::ParameterImmutableException(
|
||||
"cannot undeclare parameter '" + name + "' because it is read-only");
|
||||
}
|
||||
if (!parameter_info->second.descriptor.dynamic_typing) {
|
||||
throw rclcpp::exceptions::InvalidParameterTypeException{
|
||||
name, "cannot undeclare an statically typed parameter"};
|
||||
}
|
||||
|
||||
parameters_.erase(parameter_info);
|
||||
}
|
||||
@@ -524,13 +640,17 @@ NodeParameters::set_parameters_atomically(const std::vector<rclcpp::Parameter> &
|
||||
rcl_interfaces::msg::ParameterEvent parameter_event_msg;
|
||||
parameter_event_msg.node = combined_name_;
|
||||
CallbacksContainerType empty_callback_container;
|
||||
|
||||
// Implicit declare uses dynamic type descriptor.
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
for (auto parameter_to_be_declared : parameters_to_be_declared) {
|
||||
// This should not throw, because we validated the name and checked that
|
||||
// the parameter was not already declared.
|
||||
result = __declare_parameter_common(
|
||||
parameter_to_be_declared->get_name(),
|
||||
parameter_to_be_declared->get_parameter_value(),
|
||||
rcl_interfaces::msg::ParameterDescriptor(), // Implicit declare uses default descriptor.
|
||||
descriptor,
|
||||
staged_parameter_changes,
|
||||
parameter_overrides_,
|
||||
// Only call callbacks once below
|
||||
@@ -579,6 +699,11 @@ NodeParameters::set_parameters_atomically(const std::vector<rclcpp::Parameter> &
|
||||
if (rclcpp::PARAMETER_NOT_SET == parameter.get_type()) {
|
||||
auto it = parameters_.find(parameter.get_name());
|
||||
if (it != parameters_.end() && rclcpp::PARAMETER_NOT_SET != it->second.value.get_type()) {
|
||||
if (!it->second.descriptor.dynamic_typing) {
|
||||
result.reason = "cannot undeclare an statically typed parameter";
|
||||
result.successful = false;
|
||||
return result;
|
||||
}
|
||||
parameters_to_be_undeclared.push_back(¶meter);
|
||||
}
|
||||
}
|
||||
@@ -594,7 +719,8 @@ NodeParameters::set_parameters_atomically(const std::vector<rclcpp::Parameter> &
|
||||
on_parameters_set_callback_container_,
|
||||
// These callbacks are called once. When a callback returns an unsuccessful result,
|
||||
// the remaining aren't called.
|
||||
on_parameters_set_callback_);
|
||||
on_parameters_set_callback_,
|
||||
allow_undeclared_); // allow undeclared
|
||||
|
||||
// If not successful, then stop here.
|
||||
if (!result.successful) {
|
||||
|
||||
222
rclcpp/src/rclcpp/parameter_event_handler.cpp
Normal file
222
rclcpp/src/rclcpp/parameter_event_handler.cpp
Normal file
@@ -0,0 +1,222 @@
|
||||
// Copyright 2019 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/parameter_event_handler.hpp"
|
||||
#include "rcpputils/join.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
ParameterEventCallbackHandle::SharedPtr
|
||||
ParameterEventHandler::add_parameter_event_callback(
|
||||
ParameterEventCallbackType callback)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
auto handle = std::make_shared<ParameterEventCallbackHandle>();
|
||||
handle->callback = callback;
|
||||
event_callbacks_.emplace_front(handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
template<typename CallbackHandleT>
|
||||
struct HandleCompare
|
||||
: public std::unary_function<typename CallbackHandleT::WeakPtr, bool>
|
||||
{
|
||||
explicit HandleCompare(const CallbackHandleT * const base)
|
||||
: base_(base) {}
|
||||
bool operator()(const typename CallbackHandleT::WeakPtr & handle)
|
||||
{
|
||||
auto shared_handle = handle.lock();
|
||||
if (base_ == shared_handle.get()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const CallbackHandleT * const base_;
|
||||
};
|
||||
|
||||
void
|
||||
ParameterEventHandler::remove_parameter_event_callback(
|
||||
ParameterEventCallbackHandle::SharedPtr callback_handle)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
auto it = std::find_if(
|
||||
event_callbacks_.begin(),
|
||||
event_callbacks_.end(),
|
||||
HandleCompare<ParameterEventCallbackHandle>(callback_handle.get()));
|
||||
if (it != event_callbacks_.end()) {
|
||||
event_callbacks_.erase(it);
|
||||
} else {
|
||||
throw std::runtime_error("Callback doesn't exist");
|
||||
}
|
||||
}
|
||||
|
||||
ParameterCallbackHandle::SharedPtr
|
||||
ParameterEventHandler::add_parameter_callback(
|
||||
const std::string & parameter_name,
|
||||
ParameterCallbackType callback,
|
||||
const std::string & node_name)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
auto full_node_name = resolve_path(node_name);
|
||||
|
||||
auto handle = std::make_shared<ParameterCallbackHandle>();
|
||||
handle->callback = callback;
|
||||
handle->parameter_name = parameter_name;
|
||||
handle->node_name = full_node_name;
|
||||
// the last callback registered is executed first.
|
||||
parameter_callbacks_[{parameter_name, full_node_name}].emplace_front(handle);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
void
|
||||
ParameterEventHandler::remove_parameter_callback(
|
||||
ParameterCallbackHandle::SharedPtr callback_handle)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
auto handle = callback_handle.get();
|
||||
auto & container = parameter_callbacks_[{handle->parameter_name, handle->node_name}];
|
||||
auto it = std::find_if(
|
||||
container.begin(),
|
||||
container.end(),
|
||||
HandleCompare<ParameterCallbackHandle>(handle));
|
||||
if (it != container.end()) {
|
||||
container.erase(it);
|
||||
if (container.empty()) {
|
||||
parameter_callbacks_.erase({handle->parameter_name, handle->node_name});
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Callback doesn't exist");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ParameterEventHandler::get_parameter_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event,
|
||||
rclcpp::Parameter & parameter,
|
||||
const std::string parameter_name,
|
||||
const std::string node_name)
|
||||
{
|
||||
if (event.node != node_name) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto & new_parameter : event.new_parameters) {
|
||||
if (new_parameter.name == parameter_name) {
|
||||
parameter = rclcpp::Parameter::from_parameter_msg(new_parameter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & changed_parameter : event.changed_parameters) {
|
||||
if (changed_parameter.name == parameter_name) {
|
||||
parameter = rclcpp::Parameter::from_parameter_msg(changed_parameter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
rclcpp::Parameter
|
||||
ParameterEventHandler::get_parameter_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event,
|
||||
const std::string parameter_name,
|
||||
const std::string node_name)
|
||||
{
|
||||
rclcpp::Parameter p;
|
||||
if (!get_parameter_from_event(event, p, parameter_name, node_name)) {
|
||||
throw std::runtime_error(
|
||||
"Parameter '" + parameter_name + "' of node '" + node_name +
|
||||
"' is not part of parameter event");
|
||||
}
|
||||
return p;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Parameter>
|
||||
ParameterEventHandler::get_parameters_from_event(
|
||||
const rcl_interfaces::msg::ParameterEvent & event)
|
||||
{
|
||||
std::vector<rclcpp::Parameter> params;
|
||||
|
||||
for (auto & new_parameter : event.new_parameters) {
|
||||
params.push_back(rclcpp::Parameter::from_parameter_msg(new_parameter));
|
||||
}
|
||||
|
||||
for (auto & changed_parameter : event.changed_parameters) {
|
||||
params.push_back(rclcpp::Parameter::from_parameter_msg(changed_parameter));
|
||||
}
|
||||
|
||||
return params;
|
||||
}
|
||||
|
||||
void
|
||||
ParameterEventHandler::event_callback(
|
||||
const rcl_interfaces::msg::ParameterEvent::SharedPtr event)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
|
||||
for (auto it = parameter_callbacks_.begin(); it != parameter_callbacks_.end(); ++it) {
|
||||
rclcpp::Parameter p;
|
||||
if (get_parameter_from_event(*event, p, it->first.first, it->first.second)) {
|
||||
for (auto cb = it->second.begin(); cb != it->second.end(); ++cb) {
|
||||
auto shared_handle = cb->lock();
|
||||
if (nullptr != shared_handle) {
|
||||
shared_handle->callback(p);
|
||||
} else {
|
||||
cb = it->second.erase(cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto event_cb = event_callbacks_.begin(); event_cb != event_callbacks_.end(); ++event_cb) {
|
||||
auto shared_event_handle = event_cb->lock();
|
||||
if (nullptr != shared_event_handle) {
|
||||
shared_event_handle->callback(event);
|
||||
} else {
|
||||
event_cb = event_callbacks_.erase(event_cb);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
ParameterEventHandler::resolve_path(const std::string & path)
|
||||
{
|
||||
std::string full_path;
|
||||
|
||||
if (path == "") {
|
||||
full_path = node_base_->get_fully_qualified_name();
|
||||
} else {
|
||||
full_path = path;
|
||||
if (*path.begin() != '/') {
|
||||
auto ns = node_base_->get_namespace();
|
||||
const std::vector<std::string> paths{ns, path};
|
||||
full_path = (ns == std::string("/")) ? ns + path : rcpputils::join(paths, "/");
|
||||
}
|
||||
}
|
||||
|
||||
return full_path;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -41,12 +41,13 @@ ParameterService::ParameterService(
|
||||
const std::shared_ptr<rcl_interfaces::srv::GetParameters::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::GetParameters::Response> response)
|
||||
{
|
||||
for (const auto & name : request->names) {
|
||||
// Default construct param to NOT_SET
|
||||
rclcpp::Parameter param;
|
||||
node_params->get_parameter(name, param);
|
||||
// push back NOT_SET when get_parameter() call fails
|
||||
response->values.push_back(param.get_value_message());
|
||||
try {
|
||||
auto parameters = node_params->get_parameters(request->names);
|
||||
for (const auto param : parameters) {
|
||||
response->values.push_back(param.get_value_message());
|
||||
}
|
||||
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to get parameters: %s", ex.what());
|
||||
}
|
||||
},
|
||||
qos_profile, nullptr);
|
||||
@@ -67,7 +68,7 @@ ParameterService::ParameterService(
|
||||
return static_cast<rclcpp::ParameterType>(type);
|
||||
});
|
||||
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
|
||||
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to get parameter types: %s", ex.what());
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to get parameter types: %s", ex.what());
|
||||
}
|
||||
},
|
||||
qos_profile, nullptr);
|
||||
@@ -88,7 +89,7 @@ ParameterService::ParameterService(
|
||||
result = node_params->set_parameters_atomically(
|
||||
{rclcpp::Parameter::from_parameter_msg(p)});
|
||||
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
|
||||
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to set parameter: %s", ex.what());
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to set parameter: %s", ex.what());
|
||||
result.successful = false;
|
||||
result.reason = ex.what();
|
||||
}
|
||||
@@ -116,7 +117,7 @@ ParameterService::ParameterService(
|
||||
auto result = node_params->set_parameters_atomically(pvariants);
|
||||
response->result = result;
|
||||
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
|
||||
RCLCPP_WARN(
|
||||
RCLCPP_DEBUG(
|
||||
rclcpp::get_logger("rclcpp"), "Failed to set parameters atomically: %s", ex.what());
|
||||
response->result.successful = false;
|
||||
response->result.reason = "One or more parameters were not declared before setting";
|
||||
@@ -136,7 +137,7 @@ ParameterService::ParameterService(
|
||||
auto descriptors = node_params->describe_parameters(request->names);
|
||||
response->descriptors = descriptors;
|
||||
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
|
||||
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to describe parameters: %s", ex.what());
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to describe parameters: %s", ex.what());
|
||||
}
|
||||
},
|
||||
qos_profile, nullptr);
|
||||
|
||||
@@ -46,7 +46,9 @@ BENCHMARK_F(NodePerformanceTest, create_node)(benchmark::State & state)
|
||||
for (auto _ : state) {
|
||||
// Using pointer to separate construction and destruction in timing
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
#ifndef __clang_analyzer__
|
||||
benchmark::DoNotOptimize(node);
|
||||
#endif
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
// Ensure destruction of node is not counted toward timing
|
||||
@@ -69,7 +71,9 @@ BENCHMARK_F(NodePerformanceTest, destroy_node)(benchmark::State & state)
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
state.ResumeTiming();
|
||||
|
||||
#ifndef __clang_analyzer__
|
||||
benchmark::DoNotOptimize(node);
|
||||
#endif
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
node.reset();
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
param2_name(param_prefix + ".my_param_2"),
|
||||
param3_name(param_prefix + ".my_param_3")
|
||||
{
|
||||
dynamically_typed_descriptor.dynamic_typing = true;
|
||||
}
|
||||
|
||||
void SetUp(benchmark::State & state)
|
||||
@@ -37,9 +38,12 @@ public:
|
||||
rclcpp::init(0, nullptr);
|
||||
node = std::make_shared<rclcpp::Node>(node_name);
|
||||
|
||||
node->declare_parameter(param1_name);
|
||||
node->declare_parameter(param2_name);
|
||||
node->declare_parameter(param3_name);
|
||||
node->declare_parameter(
|
||||
param1_name, rclcpp::ParameterValue{}, dynamically_typed_descriptor);
|
||||
node->declare_parameter(
|
||||
param2_name, rclcpp::ParameterValue{}, dynamically_typed_descriptor);
|
||||
node->declare_parameter(
|
||||
param3_name, rclcpp::ParameterValue{}, dynamically_typed_descriptor);
|
||||
node->undeclare_parameter(param3_name);
|
||||
|
||||
performance_test_fixture::PerformanceTest::SetUp(state);
|
||||
@@ -58,6 +62,7 @@ public:
|
||||
const std::string param1_name;
|
||||
const std::string param2_name;
|
||||
const std::string param3_name;
|
||||
rcl_interfaces::msg::ParameterDescriptor dynamically_typed_descriptor;
|
||||
|
||||
protected:
|
||||
rclcpp::Node::SharedPtr node;
|
||||
@@ -66,7 +71,7 @@ protected:
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, declare_undeclare)(benchmark::State & state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
node->declare_parameter(param3_name);
|
||||
node->declare_parameter(param3_name, rclcpp::ParameterValue{}, dynamically_typed_descriptor);
|
||||
node->undeclare_parameter(param3_name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -91,11 +91,14 @@ public:
|
||||
rclcpp::init(0, nullptr);
|
||||
node = std::make_shared<rclcpp::Node>(node_name);
|
||||
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
|
||||
remote_node->declare_parameter(
|
||||
param1_name, rclcpp::ParameterValue("param1_value"));
|
||||
param1_name, rclcpp::ParameterValue("param1_value"), descriptor);
|
||||
remote_node->declare_parameter(
|
||||
param2_name, rclcpp::ParameterValue(std::vector<int> {1, 2, 3}));
|
||||
remote_node->declare_parameter(param3_name);
|
||||
param2_name, rclcpp::ParameterValue(std::vector<int> {1, 2, 3}), descriptor);
|
||||
remote_node->declare_parameter(param3_name, rclcpp::ParameterValue{}, descriptor);
|
||||
remote_node->undeclare_parameter(param3_name);
|
||||
|
||||
params_client = std::make_shared<rclcpp::SyncParametersClient>(node, remote_node_name);
|
||||
|
||||
@@ -323,6 +323,16 @@ if(TARGET test_parameter)
|
||||
)
|
||||
target_link_libraries(test_parameter ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_parameter_event_handler test_parameter_event_handler.cpp)
|
||||
if(TARGET test_parameter_event_handler)
|
||||
ament_target_dependencies(test_parameter_event_handler
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_parameter_event_handler ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_parameter_map test_parameter_map.cpp)
|
||||
if(TARGET test_parameter_map)
|
||||
target_link_libraries(test_parameter_map ${PROJECT_NAME})
|
||||
|
||||
@@ -511,10 +511,11 @@ TEST_F(TestAllocatorMemoryStrategy, number_of_entities_with_subscription) {
|
||||
RclWaitSetSizes expected_sizes = {};
|
||||
expected_sizes.size_of_subscriptions = 1;
|
||||
const std::string implementation_identifier = rmw_get_implementation_identifier();
|
||||
// TODO(asorbini): Remove Connext exception once ros2/rmw_connext is deprecated.
|
||||
if (implementation_identifier == "rmw_connext_cpp" ||
|
||||
implementation_identifier == "rmw_cyclonedds_cpp")
|
||||
{
|
||||
// For connext, a subscription will also add an event and waitable
|
||||
// For cyclonedds, a subscription will also add an event and waitable
|
||||
expected_sizes.size_of_events += 1;
|
||||
expected_sizes.size_of_waitables += 1;
|
||||
}
|
||||
|
||||
@@ -30,10 +30,6 @@
|
||||
#include "rclcpp/executor.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
// Note: This is a long running test with rmw_connext_cpp, if you change this file, please check
|
||||
// that this test can complete fully, or adjust the timeout as necessary.
|
||||
// See https://github.com/ros2/rmw_connext/issues/325 for resolution]
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
template<typename T>
|
||||
|
||||
@@ -26,7 +26,8 @@ class TestAnySubscriptionCallback : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
TestAnySubscriptionCallback()
|
||||
: any_subscription_callback_(allocator_) {}
|
||||
: allocator_(std::make_shared<std::allocator<void>>()),
|
||||
any_subscription_callback_(allocator_) {}
|
||||
void SetUp()
|
||||
{
|
||||
msg_shared_ptr_ = std::make_shared<test_msgs::msg::Empty>();
|
||||
@@ -45,6 +46,20 @@ protected:
|
||||
rclcpp::MessageInfo message_info_;
|
||||
};
|
||||
|
||||
void construct_with_null_allocator()
|
||||
{
|
||||
// We need to wrap this in a function because `EXPECT_THROW` is a macro, and thinks
|
||||
// that the comma in here splits macro arguments, not the template arguments.
|
||||
rclcpp::AnySubscriptionCallback<
|
||||
test_msgs::msg::Empty, std::allocator<void>> any_subscription_callback_(nullptr);
|
||||
}
|
||||
|
||||
TEST(AnySubscription, null_allocator) {
|
||||
EXPECT_THROW(
|
||||
construct_with_null_allocator(),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, construct_destruct) {
|
||||
}
|
||||
|
||||
|
||||
@@ -330,8 +330,11 @@ TEST_F(TestNode, declare_parameter_with_no_initial_values) {
|
||||
// test cases without initial values
|
||||
auto node = std::make_shared<rclcpp::Node>("test_declare_parameter_node"_unq);
|
||||
{
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
// no default, no initial
|
||||
rclcpp::ParameterValue value = node->declare_parameter("parameter"_unq);
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter"_unq, rclcpp::ParameterValue{}, descriptor);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_NOT_SET);
|
||||
}
|
||||
{
|
||||
@@ -363,15 +366,21 @@ TEST_F(TestNode, declare_parameter_with_no_initial_values) {
|
||||
{
|
||||
// parameter already declared throws
|
||||
auto name = "parameter"_unq;
|
||||
node->declare_parameter(name);
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
EXPECT_THROW(
|
||||
{node->declare_parameter(name);},
|
||||
{
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
},
|
||||
rclcpp::exceptions::ParameterAlreadyDeclaredException);
|
||||
}
|
||||
{
|
||||
// parameter name invalid throws
|
||||
EXPECT_THROW(
|
||||
{node->declare_parameter("");},
|
||||
{node->declare_parameter("", 5);},
|
||||
rclcpp::exceptions::InvalidParametersException);
|
||||
}
|
||||
{
|
||||
@@ -400,6 +409,46 @@ TEST_F(TestNode, declare_parameter_with_no_initial_values) {
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestNode, declare_parameter_with_allow_undeclared_parameters) {
|
||||
// test cases without initial values
|
||||
auto node = std::make_shared<rclcpp::Node>(
|
||||
"test_declare_parameter_node"_unq, "/",
|
||||
rclcpp::NodeOptions{}.allow_undeclared_parameters(true));
|
||||
{
|
||||
// declared parameters static typing is still enforced
|
||||
auto param_name = "parameter"_unq;
|
||||
auto value = node->declare_parameter(param_name, 5);
|
||||
EXPECT_EQ(value, 5);
|
||||
EXPECT_FALSE(node->set_parameter({param_name, "asd"}).successful);
|
||||
auto param = node->get_parameter(param_name);
|
||||
EXPECT_EQ(param.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(param.get_value<int64_t>(), 5);
|
||||
}
|
||||
{
|
||||
// not for automatically declared parameters
|
||||
auto param_name = "parameter"_unq;
|
||||
EXPECT_TRUE(node->set_parameter({param_name, 5}).successful);
|
||||
auto param = node->get_parameter(param_name);
|
||||
EXPECT_EQ(param.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(param.get_value<int64_t>(), 5);
|
||||
EXPECT_TRUE(node->set_parameter({param_name, "asd"}).successful);
|
||||
param = node->get_parameter(param_name);
|
||||
EXPECT_EQ(param.get_type(), rclcpp::PARAMETER_STRING);
|
||||
EXPECT_EQ(param.get_value<std::string>(), "asd");
|
||||
}
|
||||
{
|
||||
// declare after set is invalid
|
||||
auto param_name = "parameter"_unq;
|
||||
EXPECT_TRUE(node->set_parameter({param_name, 5}).successful);
|
||||
auto param = node->get_parameter(param_name);
|
||||
EXPECT_EQ(param.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(param.get_value<int64_t>(), 5);
|
||||
EXPECT_THROW(
|
||||
node->declare_parameter(param_name, 5),
|
||||
rclcpp::exceptions::ParameterAlreadyDeclaredException);
|
||||
}
|
||||
}
|
||||
|
||||
auto get_fixed_on_parameter_set_callback(const std::string & name, bool successful)
|
||||
{
|
||||
return
|
||||
@@ -497,13 +546,15 @@ TEST_F(TestNode, declare_parameter_with_overrides) {
|
||||
auto node = std::make_shared<rclcpp::Node>("test_declare_parameter_node"_unq, no);
|
||||
{
|
||||
// no default, with override
|
||||
rclcpp::ParameterValue value = node->declare_parameter("parameter_no_default");
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter_no_default", rclcpp::ParameterType::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(value.get<int>(), 42);
|
||||
}
|
||||
{
|
||||
// no default, with override, and set after
|
||||
rclcpp::ParameterValue value = node->declare_parameter("parameter_no_default_set");
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter_no_default_set", rclcpp::ParameterType::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(value.get<int>(), 42);
|
||||
// check that the value is changed after a set
|
||||
@@ -512,8 +563,8 @@ TEST_F(TestNode, declare_parameter_with_overrides) {
|
||||
}
|
||||
{
|
||||
// no default, with override
|
||||
const rclcpp::ParameterValue & value =
|
||||
node->declare_parameter("parameter_no_default_set_cvref");
|
||||
const rclcpp::ParameterValue & value = node->declare_parameter(
|
||||
"parameter_no_default_set_cvref", rclcpp::ParameterType::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(value.get<int>(), 42);
|
||||
// check that the value is changed after a set
|
||||
@@ -560,15 +611,21 @@ TEST_F(TestNode, declare_parameter_with_overrides) {
|
||||
{
|
||||
// parameter already declared throws
|
||||
auto name = "parameter_already_declared";
|
||||
node->declare_parameter(name);
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
EXPECT_THROW(
|
||||
{node->declare_parameter(name);},
|
||||
{
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
},
|
||||
rclcpp::exceptions::ParameterAlreadyDeclaredException);
|
||||
}
|
||||
{
|
||||
// parameter name invalid throws
|
||||
EXPECT_THROW(
|
||||
{node->declare_parameter("");},
|
||||
{node->declare_parameter("", 5);},
|
||||
rclcpp::exceptions::InvalidParametersException);
|
||||
}
|
||||
{
|
||||
@@ -603,6 +660,22 @@ TEST_F(TestNode, declare_parameter_with_overrides) {
|
||||
{node->declare_parameter("parameter_type_mismatch", 42);},
|
||||
rclcpp::exceptions::InvalidParameterTypeException);
|
||||
}
|
||||
{
|
||||
// default type and expected type do not match
|
||||
EXPECT_THROW(
|
||||
{node->declare_parameter(
|
||||
"parameter_type_mismatch", rclcpp::ParameterType::PARAMETER_INTEGER);},
|
||||
rclcpp::exceptions::InvalidParameterTypeException);
|
||||
}
|
||||
{
|
||||
// cannot pass an expected type and a descriptor with dynamic_typing=True
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor{};
|
||||
descriptor.dynamic_typing = true;
|
||||
EXPECT_THROW(
|
||||
{node->declare_parameter(
|
||||
"invalid_argument", rclcpp::ParameterType::PARAMETER_INTEGER, descriptor);},
|
||||
std::invalid_argument);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestNode, declare_parameters_with_no_initial_values) {
|
||||
@@ -673,7 +746,7 @@ TEST_F(TestNode, declare_parameters_with_no_initial_values) {
|
||||
{
|
||||
// parameter already declared throws, even with not_set type
|
||||
auto name = "parameter"_unq;
|
||||
node->declare_parameter(name);
|
||||
node->declare_parameter(name, 42);
|
||||
EXPECT_THROW(
|
||||
{node->declare_parameters<int64_t>("", {{name, 42}});},
|
||||
rclcpp::exceptions::ParameterAlreadyDeclaredException);
|
||||
@@ -732,45 +805,53 @@ TEST_F(TestNode, declare_parameter_with_cli_overrides) {
|
||||
// To match parameters YAML file content, use a well-known node name for this test only.
|
||||
auto node = std::make_shared<rclcpp::Node>("test_declare_parameter_node", no);
|
||||
{
|
||||
rclcpp::ParameterValue value = node->declare_parameter("parameter_bool");
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter_bool", rclcpp::ParameterType::PARAMETER_BOOL);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_BOOL);
|
||||
EXPECT_EQ(value.get<bool>(), true);
|
||||
}
|
||||
{
|
||||
rclcpp::ParameterValue value = node->declare_parameter("parameter_int");
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter_int", rclcpp::ParameterType::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(value.get<int64_t>(), 21); // set to 42 in CLI, overriden by file
|
||||
}
|
||||
{
|
||||
rclcpp::ParameterValue value = node->declare_parameter("parameter_double");
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter_double", rclcpp::ParameterType::PARAMETER_DOUBLE);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE);
|
||||
EXPECT_EQ(value.get<double>(), 0.42);
|
||||
}
|
||||
{
|
||||
rclcpp::ParameterValue value = node->declare_parameter("parameter_string");
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter_string", rclcpp::ParameterType::PARAMETER_STRING);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_STRING);
|
||||
EXPECT_EQ(value.get<std::string>(), "foo");
|
||||
}
|
||||
{
|
||||
rclcpp::ParameterValue value = node->declare_parameter("parameter_bool_array");
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter_bool_array", rclcpp::ParameterType::PARAMETER_BOOL_ARRAY);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_BOOL_ARRAY);
|
||||
std::vector<bool> expected_value{false, true};
|
||||
EXPECT_EQ(value.get<std::vector<bool>>(), expected_value);
|
||||
}
|
||||
{
|
||||
rclcpp::ParameterValue value = node->declare_parameter("parameter_int_array");
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter_int_array", rclcpp::ParameterType::PARAMETER_INTEGER_ARRAY);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER_ARRAY);
|
||||
std::vector<int64_t> expected_value{-21, 42};
|
||||
EXPECT_EQ(value.get<std::vector<int64_t>>(), expected_value);
|
||||
}
|
||||
{
|
||||
rclcpp::ParameterValue value = node->declare_parameter("parameter_double_array");
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter_double_array", rclcpp::ParameterType::PARAMETER_DOUBLE_ARRAY);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE_ARRAY);
|
||||
std::vector<double> expected_value{-1.0, 0.42};
|
||||
EXPECT_EQ(value.get<std::vector<double>>(), expected_value);
|
||||
}
|
||||
{
|
||||
rclcpp::ParameterValue value = node->declare_parameter("parameter_string_array");
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter_string_array", rclcpp::ParameterType::PARAMETER_STRING_ARRAY);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_STRING_ARRAY);
|
||||
std::vector<std::string> expected_value{"foo", "bar"};
|
||||
// set to [baz, baz, baz] in file, overriden by CLI
|
||||
@@ -783,7 +864,9 @@ TEST_F(TestNode, undeclare_parameter) {
|
||||
{
|
||||
// normal use
|
||||
auto name = "parameter"_unq;
|
||||
node->declare_parameter(name);
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
node->undeclare_parameter(name);
|
||||
EXPECT_FALSE(node->has_parameter(name));
|
||||
@@ -796,6 +879,16 @@ TEST_F(TestNode, undeclare_parameter) {
|
||||
{node->undeclare_parameter(name);},
|
||||
rclcpp::exceptions::ParameterNotDeclaredException);
|
||||
}
|
||||
{
|
||||
// statically typed parameter throws
|
||||
auto name = "parameter"_unq;
|
||||
node->declare_parameter(name, 42);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
EXPECT_THROW(
|
||||
{node->undeclare_parameter(name);},
|
||||
rclcpp::exceptions::InvalidParameterTypeException);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
}
|
||||
{
|
||||
// read only parameter throws
|
||||
auto name = "parameter"_unq;
|
||||
@@ -815,7 +908,9 @@ TEST_F(TestNode, has_parameter) {
|
||||
// normal use
|
||||
auto name = "parameter"_unq;
|
||||
EXPECT_FALSE(node->has_parameter(name));
|
||||
node->declare_parameter(name);
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
node->undeclare_parameter(name);
|
||||
EXPECT_FALSE(node->has_parameter(name));
|
||||
@@ -826,7 +921,9 @@ TEST_F(TestNode, list_parameters) {
|
||||
// normal use
|
||||
auto name = "parameter"_unq;
|
||||
const size_t before_size = node->list_parameters({}, 1u).names.size();
|
||||
node->declare_parameter(name);
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
EXPECT_EQ(1u + before_size, node->list_parameters({}, 1u).names.size());
|
||||
node->undeclare_parameter(name);
|
||||
EXPECT_EQ(before_size, node->list_parameters({}, 1u).names.size());
|
||||
@@ -847,12 +944,23 @@ TEST_F(TestNode, set_parameter_undeclared_parameters_not_allowed) {
|
||||
EXPECT_EQ(node->get_parameter(name).get_value<int>(), 43);
|
||||
}
|
||||
{
|
||||
// normal use, change type
|
||||
// normal use, change type not allowed
|
||||
auto name = "parameter"_unq;
|
||||
EXPECT_FALSE(node->has_parameter(name));
|
||||
node->declare_parameter(name, 42);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
EXPECT_EQ(node->get_parameter(name).get_value<int>(), 42);
|
||||
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, "not an integer")).successful);
|
||||
}
|
||||
{
|
||||
// normal use, change type
|
||||
auto name = "parameter"_unq;
|
||||
EXPECT_FALSE(node->has_parameter(name));
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
node->declare_parameter(name, 42, descriptor);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
EXPECT_EQ(node->get_parameter(name).get_value<int>(), 42);
|
||||
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, "not an integer")).successful);
|
||||
EXPECT_EQ(node->get_parameter(name).get_value<std::string>(), std::string("not an integer"));
|
||||
}
|
||||
@@ -869,8 +977,6 @@ TEST_F(TestNode, set_parameter_undeclared_parameters_not_allowed) {
|
||||
EXPECT_TRUE(node->has_parameter(name3));
|
||||
|
||||
EXPECT_EQ(node->get_parameter(name1).get_value<int>(), 42);
|
||||
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name1, "not an integer")).successful);
|
||||
EXPECT_EQ(node->get_parameter(name1).get_value<std::string>(), std::string("not an integer"));
|
||||
|
||||
EXPECT_EQ(node->get_parameter(name2).get_value<bool>(), true);
|
||||
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name2, false)).successful);
|
||||
@@ -905,7 +1011,9 @@ TEST_F(TestNode, set_parameter_undeclared_parameters_not_allowed) {
|
||||
{
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET, when already not set, does not undeclare
|
||||
auto name = "parameter"_unq;
|
||||
auto value = node->declare_parameter(name);
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
auto value = node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_NOT_SET);
|
||||
|
||||
@@ -915,13 +1023,26 @@ TEST_F(TestNode, set_parameter_undeclared_parameters_not_allowed) {
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_NOT_SET);
|
||||
}
|
||||
{
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET, when already to another type, will undeclare
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET, when already to another type, will fail
|
||||
auto name = "parameter"_unq;
|
||||
node->declare_parameter(name, 42);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
auto value = node->get_parameter(name);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
|
||||
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name)).successful);
|
||||
}
|
||||
{
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET,
|
||||
// when dynamic typing is allowing and already set to another type, will undeclare
|
||||
auto name = "parameter"_unq;
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
node->declare_parameter(name, 42, descriptor);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
auto value = node->get_parameter(name);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
|
||||
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name)).successful);
|
||||
|
||||
EXPECT_FALSE(node->has_parameter(name));
|
||||
@@ -1261,15 +1382,9 @@ TEST_F(TestNode, set_parameter_undeclared_parameters_not_allowed) {
|
||||
auto name = "parameter"_unq;
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.type = rclcpp::PARAMETER_INTEGER;
|
||||
node->declare_parameter(name, 42, descriptor);
|
||||
node->declare_parameter(name, "asd", descriptor);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
auto value = node->get_parameter(name);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(value.get_value<int64_t>(), 42);
|
||||
|
||||
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name, "asd")).successful);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
value = node->get_parameter(name);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_STRING);
|
||||
EXPECT_EQ(value.get_value<std::string>(), "asd");
|
||||
}
|
||||
@@ -1417,7 +1532,9 @@ TEST_F(TestNode, set_parameters_undeclared_parameters_not_allowed) {
|
||||
{
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET, when already not set, does not undeclare
|
||||
auto name = "parameter"_unq;
|
||||
auto value = node->declare_parameter(name);
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
auto value = node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_NOT_SET);
|
||||
|
||||
@@ -1427,13 +1544,26 @@ TEST_F(TestNode, set_parameters_undeclared_parameters_not_allowed) {
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_NOT_SET);
|
||||
}
|
||||
{
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET, when already to another type, will undeclare
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET, when already to another type, will fail
|
||||
auto name = "parameter"_unq;
|
||||
node->declare_parameter(name, 42);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
auto value = node->get_parameter(name);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
|
||||
EXPECT_FALSE(node->set_parameters({rclcpp::Parameter(name)})[0].successful);
|
||||
}
|
||||
{
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET,
|
||||
// when already to another type and dynamic typic allowed, will undeclare
|
||||
auto name = "parameter"_unq;
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
node->declare_parameter(name, 42, descriptor);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
auto value = node->get_parameter(name);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
|
||||
EXPECT_TRUE(node->set_parameters({rclcpp::Parameter(name)})[0].successful);
|
||||
|
||||
EXPECT_FALSE(node->has_parameter(name));
|
||||
@@ -1591,7 +1721,9 @@ TEST_F(TestNode, set_parameters_atomically_undeclared_parameters_not_allowed) {
|
||||
{
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET, when already not set, does not undeclare
|
||||
auto name = "parameter"_unq;
|
||||
auto value = node->declare_parameter(name);
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
auto value = node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_NOT_SET);
|
||||
|
||||
@@ -1601,13 +1733,26 @@ TEST_F(TestNode, set_parameters_atomically_undeclared_parameters_not_allowed) {
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_NOT_SET);
|
||||
}
|
||||
{
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET, when already to another type, will undeclare
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET, when already to another type, will fail
|
||||
auto name = "parameter"_unq;
|
||||
node->declare_parameter(name, 42);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
auto value = node->get_parameter(name);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
|
||||
EXPECT_FALSE(node->set_parameters_atomically({rclcpp::Parameter(name)}).successful);
|
||||
}
|
||||
{
|
||||
// setting type of rclcpp::PARAMETER_NOT_SET,
|
||||
// when dynamic typing is allowed and already declared to another type, will undeclare
|
||||
auto name = "parameter"_unq;
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
node->declare_parameter(name, 42, descriptor);
|
||||
EXPECT_TRUE(node->has_parameter(name));
|
||||
auto value = node->get_parameter(name);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER);
|
||||
|
||||
EXPECT_TRUE(node->set_parameters_atomically({rclcpp::Parameter(name)}).successful);
|
||||
|
||||
EXPECT_FALSE(node->has_parameter(name));
|
||||
@@ -1963,30 +2108,28 @@ TEST_F(TestNode, get_parameters_undeclared_parameters_not_allowed) {
|
||||
{
|
||||
// templated version with empty prefix will get all parameters
|
||||
auto node_local = std::make_shared<rclcpp::Node>("test_get_parameters_node"_unq);
|
||||
auto name1 = "prefix1.parameter"_unq;
|
||||
auto name2 = "prefix2.parameter"_unq;
|
||||
auto name1 = "parameter"_unq;
|
||||
auto name2 = "parameter"_unq;
|
||||
|
||||
node_local->declare_parameter(name1, 42);
|
||||
node_local->declare_parameter(name2, 100);
|
||||
// undeclare so that it doesn't interfere with the test
|
||||
node_local->undeclare_parameter("use_sim_time");
|
||||
node_local->declare_parameter("prefix." + name1, 42);
|
||||
node_local->declare_parameter("prefix." + name2, 100);
|
||||
|
||||
{
|
||||
std::map<std::string, int64_t> actual;
|
||||
EXPECT_TRUE(node_local->get_parameters("", actual));
|
||||
EXPECT_TRUE(node_local->get_parameters("prefix", actual));
|
||||
EXPECT_NE(actual.find(name1), actual.end());
|
||||
EXPECT_NE(actual.find(name2), actual.end());
|
||||
}
|
||||
|
||||
// will throw if set of parameters is non-homogeneous
|
||||
auto name3 = "prefix2.parameter"_unq;
|
||||
node_local->declare_parameter<std::string>(name3, "not an int");
|
||||
auto name3 = "prefix1.parameter"_unq;
|
||||
node_local->declare_parameter<std::string>("prefix." + name3, "not an int");
|
||||
|
||||
{
|
||||
std::map<std::string, int64_t> actual;
|
||||
EXPECT_THROW(
|
||||
{
|
||||
node_local->get_parameters("", actual);
|
||||
node_local->get_parameters("prefix", actual);
|
||||
},
|
||||
rclcpp::exceptions::InvalidParameterTypeException);
|
||||
}
|
||||
@@ -2577,3 +2720,76 @@ TEST_F(TestNode, create_sub_node_rmw_validate_namespace_error) {
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestNode, static_and_dynamic_typing) {
|
||||
rclcpp::NodeOptions no;
|
||||
no.parameter_overrides(
|
||||
{
|
||||
{"integer_parameter_override_ok", 43},
|
||||
{"string_parameter_override_should_throw", 43},
|
||||
{"integer_must_provide_override", 43},
|
||||
{"cool_way_of_declaring_a_string_without_a_default", "hello!"}
|
||||
});
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns", no);
|
||||
{
|
||||
auto param = node->declare_parameter("an_int", 42);
|
||||
EXPECT_EQ(42, param);
|
||||
auto result = node->set_parameter({"an_int", "string value"});
|
||||
EXPECT_FALSE(result.successful);
|
||||
result = node->set_parameter({"an_int", 43});
|
||||
EXPECT_TRUE(result.successful);
|
||||
EXPECT_TRUE(node->get_parameter("an_int", param));
|
||||
EXPECT_EQ(43, param);
|
||||
}
|
||||
{
|
||||
auto param = node->declare_parameter("integer_parameter_override_ok", 42);
|
||||
EXPECT_EQ(43, param);
|
||||
}
|
||||
{
|
||||
EXPECT_THROW(
|
||||
node->declare_parameter("string_parameter_override_should_throw", "a string"),
|
||||
rclcpp::exceptions::InvalidParameterTypeException);
|
||||
}
|
||||
{
|
||||
auto param = node->declare_parameter(
|
||||
"integer_must_provide_override", rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(43, param.get<int64_t>());
|
||||
}
|
||||
{
|
||||
auto param = node->declare_parameter<std::string>(
|
||||
"cool_way_of_declaring_a_string_without_a_default");
|
||||
EXPECT_EQ("hello!", param);
|
||||
}
|
||||
{
|
||||
EXPECT_THROW(
|
||||
node->declare_parameter("integer_override_not_given", rclcpp::PARAMETER_INTEGER),
|
||||
rclcpp::exceptions::NoParameterOverrideProvided);
|
||||
}
|
||||
{
|
||||
EXPECT_THROW(
|
||||
node->declare_parameter("parameter_not_set_is_not_a_valid_type", rclcpp::PARAMETER_NOT_SET),
|
||||
std::invalid_argument);
|
||||
}
|
||||
{
|
||||
EXPECT_THROW(
|
||||
node->declare_parameter(
|
||||
"uninitialized_not_valid_except_dynamic_typing", rclcpp::ParameterValue{}),
|
||||
rclcpp::exceptions::InvalidParameterTypeException);
|
||||
}
|
||||
{
|
||||
#ifndef _WIN32
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
auto param = node->declare_parameter("deprecated_way_dynamic_typing");
|
||||
EXPECT_EQ(param, rclcpp::ParameterValue{});
|
||||
#ifndef _WIN32
|
||||
# pragma GCC diagnostic pop
|
||||
#else
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
@@ -218,12 +218,59 @@ TEST_F(TestParameterClient, async_parameter_get_parameter_types) {
|
||||
ASSERT_TRUE(callback_called);
|
||||
}
|
||||
|
||||
/*
|
||||
Coverage for async get_parameter_types with allow_undeclared_ enabled
|
||||
*/
|
||||
TEST_F(TestParameterClient, async_parameter_get_parameter_types_allow_undeclared) {
|
||||
auto asynchronous_client =
|
||||
std::make_shared<rclcpp::AsyncParametersClient>(node_with_option);
|
||||
bool callback_called = false;
|
||||
auto callback = [&callback_called](std::shared_future<std::vector<rclcpp::ParameterType>> result)
|
||||
{
|
||||
if (result.valid() && result.get().size() == 1 &&
|
||||
result.get()[0] == rclcpp::PARAMETER_NOT_SET)
|
||||
{
|
||||
callback_called = true;
|
||||
}
|
||||
};
|
||||
std::vector<std::string> names{"foo"};
|
||||
std::shared_future<std::vector<rclcpp::ParameterType>> future =
|
||||
asynchronous_client->get_parameter_types(names, callback);
|
||||
auto return_code = rclcpp::spin_until_future_complete(
|
||||
node_with_option, future, std::chrono::milliseconds(100));
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
|
||||
ASSERT_TRUE(callback_called);
|
||||
}
|
||||
|
||||
/*
|
||||
Coverage for async get_parameters
|
||||
*/
|
||||
TEST_F(TestParameterClient, async_parameter_get_parameters) {
|
||||
auto asynchronous_client = std::make_shared<rclcpp::AsyncParametersClient>(node);
|
||||
bool callback_called = false;
|
||||
auto callback = [&callback_called](std::shared_future<std::vector<rclcpp::Parameter>> result)
|
||||
{
|
||||
// We expect the result to be empty since we tried to get a parameter that didn't exist.
|
||||
if (result.valid() && result.get().size() == 0) {
|
||||
callback_called = true;
|
||||
}
|
||||
};
|
||||
std::vector<std::string> names{"foo"};
|
||||
std::shared_future<std::vector<rclcpp::Parameter>> future = asynchronous_client->get_parameters(
|
||||
names, callback);
|
||||
auto return_code = rclcpp::spin_until_future_complete(
|
||||
node, future, std::chrono::milliseconds(100));
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
|
||||
ASSERT_TRUE(callback_called);
|
||||
}
|
||||
|
||||
/*
|
||||
Coverage for async get_parameters with allow_undeclared_ enabled
|
||||
*/
|
||||
TEST_F(TestParameterClient, async_parameter_get_parameters_allow_undeclared) {
|
||||
auto asynchronous_client =
|
||||
std::make_shared<rclcpp::AsyncParametersClient>(node_with_option);
|
||||
bool callback_called = false;
|
||||
auto callback = [&callback_called](std::shared_future<std::vector<rclcpp::Parameter>> result)
|
||||
{
|
||||
if (result.valid() && result.get().size() == 1 && result.get()[0].get_name() == "foo") {
|
||||
@@ -234,7 +281,7 @@ TEST_F(TestParameterClient, async_parameter_get_parameters) {
|
||||
std::shared_future<std::vector<rclcpp::Parameter>> future = asynchronous_client->get_parameters(
|
||||
names, callback);
|
||||
auto return_code = rclcpp::spin_until_future_complete(
|
||||
node, future, std::chrono::milliseconds(100));
|
||||
node_with_option, future, std::chrono::milliseconds(100));
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
|
||||
ASSERT_TRUE(callback_called);
|
||||
}
|
||||
@@ -292,12 +339,197 @@ TEST_F(TestParameterClient, async_parameter_list_parameters) {
|
||||
*/
|
||||
TEST_F(TestParameterClient, sync_parameter_get_parameter_types) {
|
||||
node->declare_parameter("foo", 4);
|
||||
node->declare_parameter("bar", "this is bar");
|
||||
auto synchronous_client = std::make_shared<rclcpp::SyncParametersClient>(node);
|
||||
std::vector<std::string> names{"foo"};
|
||||
std::vector<rclcpp::ParameterType> parameter_types =
|
||||
synchronous_client->get_parameter_types(names, 10s);
|
||||
ASSERT_EQ(1u, parameter_types.size());
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_types[0]);
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"none"};
|
||||
std::vector<rclcpp::ParameterType> parameter_types =
|
||||
synchronous_client->get_parameter_types(names, 10s);
|
||||
ASSERT_EQ(0u, parameter_types.size());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"none", "foo", "bar"};
|
||||
std::vector<rclcpp::ParameterType> parameter_types =
|
||||
synchronous_client->get_parameter_types(names, 10s);
|
||||
ASSERT_EQ(0u, parameter_types.size());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"foo"};
|
||||
std::vector<rclcpp::ParameterType> parameter_types =
|
||||
synchronous_client->get_parameter_types(names, 10s);
|
||||
ASSERT_EQ(1u, parameter_types.size());
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_types[0]);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"bar"};
|
||||
std::vector<rclcpp::ParameterType> parameter_types =
|
||||
synchronous_client->get_parameter_types(names, 10s);
|
||||
ASSERT_EQ(1u, parameter_types.size());
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_STRING, parameter_types[0]);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"foo", "bar"};
|
||||
std::vector<rclcpp::ParameterType> parameter_types =
|
||||
synchronous_client->get_parameter_types(names, 10s);
|
||||
ASSERT_EQ(2u, parameter_types.size());
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_types[0]);
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_STRING, parameter_types[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Coverage for sync get_parameter_types with allow_undeclared_ enabled
|
||||
*/
|
||||
TEST_F(TestParameterClient, sync_parameter_get_parameter_types_allow_undeclared) {
|
||||
node_with_option->declare_parameter("foo", 4);
|
||||
node_with_option->declare_parameter("bar", "this is bar");
|
||||
auto synchronous_client = std::make_shared<rclcpp::SyncParametersClient>(node_with_option);
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"none"};
|
||||
std::vector<rclcpp::ParameterType> parameter_types =
|
||||
synchronous_client->get_parameter_types(names, 10s);
|
||||
ASSERT_EQ(1u, parameter_types.size());
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_NOT_SET, parameter_types[0]);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"none", "foo", "bar"};
|
||||
std::vector<rclcpp::ParameterType> parameter_types =
|
||||
synchronous_client->get_parameter_types(names, 10s);
|
||||
ASSERT_EQ(3u, parameter_types.size());
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_NOT_SET, parameter_types[0]);
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_types[1]);
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_STRING, parameter_types[2]);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"foo"};
|
||||
std::vector<rclcpp::ParameterType> parameter_types =
|
||||
synchronous_client->get_parameter_types(names, 10s);
|
||||
ASSERT_EQ(1u, parameter_types.size());
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_types[0]);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"bar"};
|
||||
std::vector<rclcpp::ParameterType> parameter_types =
|
||||
synchronous_client->get_parameter_types(names, 10s);
|
||||
ASSERT_EQ(1u, parameter_types.size());
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_STRING, parameter_types[0]);
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"foo", "bar"};
|
||||
std::vector<rclcpp::ParameterType> parameter_types =
|
||||
synchronous_client->get_parameter_types(names, 10s);
|
||||
ASSERT_EQ(2u, parameter_types.size());
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_types[0]);
|
||||
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_STRING, parameter_types[1]);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Coverage for sync get_parameters
|
||||
*/
|
||||
TEST_F(TestParameterClient, sync_parameter_get_parameters) {
|
||||
node->declare_parameter("foo", 4);
|
||||
node->declare_parameter("bar", "this is bar");
|
||||
auto synchronous_client = std::make_shared<rclcpp::SyncParametersClient>(node);
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"none"};
|
||||
std::vector<rclcpp::Parameter> parameters = synchronous_client->get_parameters(names, 10s);
|
||||
ASSERT_EQ(0u, parameters.size());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"none", "foo", "bar"};
|
||||
std::vector<rclcpp::Parameter> parameters = synchronous_client->get_parameters(names, 10s);
|
||||
ASSERT_EQ(0u, parameters.size());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"foo"};
|
||||
std::vector<rclcpp::Parameter> parameters = synchronous_client->get_parameters(names, 10s);
|
||||
ASSERT_EQ(1u, parameters.size());
|
||||
ASSERT_EQ("foo", parameters[0].get_name());
|
||||
ASSERT_EQ(4u, parameters[0].as_int());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"bar"};
|
||||
std::vector<rclcpp::Parameter> parameters = synchronous_client->get_parameters(names, 10s);
|
||||
ASSERT_EQ(1u, parameters.size());
|
||||
ASSERT_EQ("bar", parameters[0].get_name());
|
||||
ASSERT_EQ("this is bar", parameters[0].as_string());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"foo", "bar"};
|
||||
std::vector<rclcpp::Parameter> parameters = synchronous_client->get_parameters(names, 10s);
|
||||
ASSERT_EQ(2u, parameters.size());
|
||||
ASSERT_EQ("foo", parameters[0].get_name());
|
||||
ASSERT_EQ(4u, parameters[0].as_int());
|
||||
ASSERT_EQ("bar", parameters[1].get_name());
|
||||
ASSERT_EQ("this is bar", parameters[1].as_string());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Coverage for sync get_parameters with allow_undeclared_ enabled
|
||||
*/
|
||||
TEST_F(TestParameterClient, sync_parameter_get_parameters_allow_undeclared) {
|
||||
node_with_option->declare_parameter("foo", 4);
|
||||
node_with_option->declare_parameter("bar", "this is bar");
|
||||
auto synchronous_client = std::make_shared<rclcpp::SyncParametersClient>(node_with_option);
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"none"};
|
||||
std::vector<rclcpp::Parameter> parameters = synchronous_client->get_parameters(names, 10s);
|
||||
ASSERT_EQ(1u, parameters.size());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"none", "foo", "bar"};
|
||||
std::vector<rclcpp::Parameter> parameters = synchronous_client->get_parameters(names, 10s);
|
||||
ASSERT_EQ(3u, parameters.size());
|
||||
ASSERT_EQ("foo", parameters[1].get_name());
|
||||
ASSERT_EQ(4u, parameters[1].as_int());
|
||||
ASSERT_EQ("bar", parameters[2].get_name());
|
||||
ASSERT_EQ("this is bar", parameters[2].as_string());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"foo"};
|
||||
std::vector<rclcpp::Parameter> parameters = synchronous_client->get_parameters(names, 10s);
|
||||
ASSERT_EQ(1u, parameters.size());
|
||||
ASSERT_EQ("foo", parameters[0].get_name());
|
||||
ASSERT_EQ(4u, parameters[0].as_int());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"bar"};
|
||||
std::vector<rclcpp::Parameter> parameters = synchronous_client->get_parameters(names, 10s);
|
||||
ASSERT_EQ(1u, parameters.size());
|
||||
ASSERT_EQ("bar", parameters[0].get_name());
|
||||
ASSERT_EQ("this is bar", parameters[0].as_string());
|
||||
}
|
||||
|
||||
{
|
||||
std::vector<std::string> names{"foo", "bar"};
|
||||
std::vector<rclcpp::Parameter> parameters = synchronous_client->get_parameters(names, 10s);
|
||||
ASSERT_EQ(2u, parameters.size());
|
||||
ASSERT_EQ("foo", parameters[0].get_name());
|
||||
ASSERT_EQ(4u, parameters[0].as_int());
|
||||
ASSERT_EQ("bar", parameters[1].get_name());
|
||||
ASSERT_EQ("this is bar", parameters[1].as_string());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
432
rclcpp/test/rclcpp/test_parameter_event_handler.cpp
Normal file
432
rclcpp/test/rclcpp/test_parameter_event_handler.cpp
Normal file
@@ -0,0 +1,432 @@
|
||||
// Copyright 2019 Intel Corporation
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
class TestParameterEventHandler : public rclcpp::ParameterEventHandler
|
||||
{
|
||||
public:
|
||||
explicit TestParameterEventHandler(rclcpp::Node::SharedPtr node)
|
||||
: ParameterEventHandler(node)
|
||||
{}
|
||||
|
||||
void test_event(const rcl_interfaces::msg::ParameterEvent::SharedPtr event)
|
||||
{
|
||||
event_callback(event);
|
||||
}
|
||||
|
||||
size_t num_event_callbacks()
|
||||
{
|
||||
return event_callbacks_.size();
|
||||
}
|
||||
|
||||
size_t num_parameter_callbacks()
|
||||
{
|
||||
return parameter_callbacks_.size();
|
||||
}
|
||||
};
|
||||
|
||||
class TestNode : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::NodeOptions options;
|
||||
node = std::make_shared<rclcpp::Node>(
|
||||
"test_parameter_events_subscriber", options);
|
||||
|
||||
remote_node_name = "/remote_node";
|
||||
diff_ns_name = "/ns/remote_node";
|
||||
|
||||
param_handler = std::make_shared<TestParameterEventHandler>(node);
|
||||
|
||||
same_node_int = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
|
||||
same_node_double = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
|
||||
multiple = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
|
||||
remote_node_string = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
|
||||
diff_ns_bool = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
|
||||
diff_node_int = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
|
||||
|
||||
same_node_int->node = node->get_fully_qualified_name();
|
||||
same_node_double->node = node->get_fully_qualified_name();
|
||||
multiple->node = node->get_fully_qualified_name();
|
||||
remote_node_string->node = remote_node_name;
|
||||
diff_ns_bool->node = diff_ns_name;
|
||||
diff_node_int->node = remote_node_name;
|
||||
|
||||
rcl_interfaces::msg::Parameter p;
|
||||
p.name = "my_int";
|
||||
p.value.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
|
||||
p.value.integer_value = 1;
|
||||
same_node_int->changed_parameters.push_back(p);
|
||||
diff_node_int->changed_parameters.push_back(p);
|
||||
multiple->changed_parameters.push_back(p);
|
||||
|
||||
p.name = "my_double";
|
||||
p.value.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
|
||||
p.value.double_value = 1.0;
|
||||
same_node_double->changed_parameters.push_back(p);
|
||||
multiple->changed_parameters.push_back(p);
|
||||
|
||||
p.name = "my_string";
|
||||
p.value.type = rcl_interfaces::msg::ParameterType::PARAMETER_STRING;
|
||||
p.value.string_value = "test";
|
||||
remote_node_string->changed_parameters.push_back(p);
|
||||
|
||||
p.name = "my_bool";
|
||||
p.value.type = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL;
|
||||
p.value.bool_value = true;
|
||||
diff_ns_bool->changed_parameters.push_back(p);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
param_handler.reset();
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr same_node_int;
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr same_node_double;
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr diff_node_int;
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr remote_node_string;
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr multiple;
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr diff_ns_bool;
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
std::string remote_node_name;
|
||||
std::string diff_ns_name;
|
||||
std::shared_ptr<TestParameterEventHandler> param_handler;
|
||||
};
|
||||
|
||||
TEST_F(TestNode, RegisterParameterCallback)
|
||||
{
|
||||
bool received;
|
||||
auto cb = [&received](const rclcpp::Parameter &) {received = true;};
|
||||
|
||||
auto h1 = param_handler->add_parameter_callback("my_double", cb);
|
||||
auto h2 = param_handler->add_parameter_callback("my_int", cb);
|
||||
auto h3 = param_handler->add_parameter_callback("my_string", cb, remote_node_name);
|
||||
auto h4 = param_handler->add_parameter_callback("my_bool", cb, diff_ns_name);
|
||||
|
||||
received = false;
|
||||
param_handler->test_event(same_node_double);
|
||||
EXPECT_EQ(received, true);
|
||||
|
||||
received = false;
|
||||
param_handler->test_event(same_node_int);
|
||||
EXPECT_EQ(received, true);
|
||||
|
||||
received = false;
|
||||
param_handler->test_event(remote_node_string);
|
||||
EXPECT_EQ(received, true);
|
||||
|
||||
received = false;
|
||||
param_handler->test_event(diff_ns_bool);
|
||||
EXPECT_EQ(received, true);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, SameParameterDifferentNode)
|
||||
{
|
||||
int64_t int_param_node1;
|
||||
int64_t int_param_node2;
|
||||
|
||||
auto cb1 = [&int_param_node1](const rclcpp::Parameter & p) {
|
||||
int_param_node1 = p.get_value<int64_t>();
|
||||
};
|
||||
auto cb2 = [&int_param_node2](const rclcpp::Parameter & p) {
|
||||
int_param_node2 = p.get_value<int64_t>();
|
||||
};
|
||||
|
||||
// Set individual parameters
|
||||
auto h1 = param_handler->add_parameter_callback("my_int", cb1);
|
||||
auto h2 = param_handler->add_parameter_callback("my_int", cb2, remote_node_name);
|
||||
|
||||
param_handler->test_event(same_node_int);
|
||||
EXPECT_EQ(int_param_node1, 1);
|
||||
EXPECT_NE(int_param_node2, 1);
|
||||
|
||||
int_param_node1 = 0;
|
||||
int_param_node2 = 0;
|
||||
|
||||
param_handler->test_event(diff_node_int);
|
||||
EXPECT_NE(int_param_node1, 1);
|
||||
EXPECT_EQ(int_param_node2, 1);
|
||||
|
||||
param_handler->remove_parameter_callback(h1);
|
||||
param_handler->remove_parameter_callback(h2);
|
||||
EXPECT_EQ(param_handler->num_parameter_callbacks(), 0UL);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, GetParameterFromEvent)
|
||||
{
|
||||
using rclcpp::ParameterEventHandler;
|
||||
std::string node_name = node->get_fully_qualified_name();
|
||||
std::string wrong_name = "/wrong_node_name";
|
||||
|
||||
rclcpp::Parameter p;
|
||||
EXPECT_TRUE(
|
||||
ParameterEventHandler::get_parameter_from_event(*multiple, p, "my_int", node_name));
|
||||
EXPECT_EQ(p.get_value<int>(), 1);
|
||||
// False if parameter not with correct node name
|
||||
EXPECT_FALSE(
|
||||
ParameterEventHandler::get_parameter_from_event(*multiple, p, "my_int", wrong_name));
|
||||
// False if parameter not part of event
|
||||
EXPECT_FALSE(
|
||||
ParameterEventHandler::get_parameter_from_event(*diff_ns_bool, p, "my_int", node_name));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
ParameterEventHandler::get_parameter_from_event(*multiple, "my_int", node_name));
|
||||
// Throws if parameter not with correct node name
|
||||
EXPECT_THROW(
|
||||
ParameterEventHandler::get_parameter_from_event(*multiple, "my_int", wrong_name),
|
||||
std::runtime_error);
|
||||
// Throws if parameter not part of event
|
||||
EXPECT_THROW(
|
||||
ParameterEventHandler::get_parameter_from_event(*diff_ns_bool, "my_int", node_name),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, GetParametersFromEvent)
|
||||
{
|
||||
using rclcpp::ParameterEventHandler;
|
||||
std::string node_name = node->get_fully_qualified_name();
|
||||
|
||||
auto params = ParameterEventHandler::get_parameters_from_event(*multiple);
|
||||
EXPECT_EQ(params.size(), 2u);
|
||||
bool found_int = false;
|
||||
bool found_double = false;
|
||||
for (auto & p : params) {
|
||||
if (p.get_name() == std::string("my_int")) {
|
||||
found_int = true;
|
||||
EXPECT_EQ(p.get_value<int>(), 1);
|
||||
} else if (p.get_name() == std::string("my_double")) {
|
||||
found_double = true;
|
||||
EXPECT_EQ(p.get_value<double>(), 1.0);
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(found_int, true);
|
||||
EXPECT_EQ(found_double, true);
|
||||
|
||||
params = ParameterEventHandler::get_parameters_from_event(*remote_node_string);
|
||||
EXPECT_EQ(params.size(), 1u);
|
||||
bool found_string = false;
|
||||
for (auto & p : params) {
|
||||
if (p.get_name() == std::string("my_string")) {
|
||||
found_string = true;
|
||||
EXPECT_EQ(p.get_value<std::string>(), std::string("test"));
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(found_string, true);
|
||||
|
||||
params = ParameterEventHandler::get_parameters_from_event(*diff_ns_bool);
|
||||
EXPECT_EQ(params.size(), 1u);
|
||||
bool found_bool = false;
|
||||
for (auto & p : params) {
|
||||
if (p.get_name() == std::string("my_bool")) {
|
||||
found_bool = true;
|
||||
EXPECT_EQ(p.get_value<bool>(), true);
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(found_bool, true);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, EventCallback)
|
||||
{
|
||||
using rclcpp::ParameterEventHandler;
|
||||
|
||||
double double_param = 0.0;
|
||||
int64_t int_param = 0;
|
||||
bool bool_param{false};
|
||||
bool received{false};
|
||||
|
||||
double product;
|
||||
auto cb =
|
||||
[&int_param, &double_param, &product, &received,
|
||||
this](const rcl_interfaces::msg::ParameterEvent::SharedPtr & event)
|
||||
{
|
||||
auto node_name = node->get_fully_qualified_name();
|
||||
|
||||
if (event->node == node_name) {
|
||||
received = true;
|
||||
}
|
||||
|
||||
rclcpp::Parameter p;
|
||||
if (ParameterEventHandler::get_parameter_from_event(*event, p, "my_int", node_name)) {
|
||||
int_param = p.get_value<int64_t>();
|
||||
}
|
||||
try {
|
||||
p = ParameterEventHandler::get_parameter_from_event(*event, "my_double", node_name);
|
||||
double_param = p.get_value<double>();
|
||||
} catch (...) {
|
||||
}
|
||||
|
||||
product = static_cast<double>(int_param) * double_param;
|
||||
};
|
||||
|
||||
auto cb2 =
|
||||
[&bool_param, this](const rcl_interfaces::msg::ParameterEvent::SharedPtr & event)
|
||||
{
|
||||
rclcpp::Parameter p;
|
||||
if (event->node == diff_ns_name) {
|
||||
if (ParameterEventHandler::get_parameter_from_event(
|
||||
*event, p, "my_bool", diff_ns_name))
|
||||
{
|
||||
bool_param = p.get_value<bool>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto event_handle1 = param_handler->add_parameter_event_callback(cb);
|
||||
auto event_handle2 = param_handler->add_parameter_event_callback(cb2);
|
||||
|
||||
bool_param = false;
|
||||
param_handler->test_event(multiple);
|
||||
EXPECT_EQ(received, true);
|
||||
EXPECT_EQ(product, 1.0);
|
||||
EXPECT_EQ(bool_param, false);
|
||||
|
||||
param_handler->test_event(diff_ns_bool);
|
||||
EXPECT_EQ(bool_param, true);
|
||||
|
||||
// Test removal of event callback
|
||||
received = false;
|
||||
bool_param = false;
|
||||
param_handler->remove_parameter_event_callback(event_handle1);
|
||||
param_handler->test_event(multiple);
|
||||
param_handler->test_event(diff_ns_bool);
|
||||
EXPECT_EQ(received, false);
|
||||
EXPECT_EQ(bool_param, true);
|
||||
|
||||
// Should throw if callback handle no longer exists or already removed
|
||||
EXPECT_THROW(
|
||||
param_handler->remove_parameter_event_callback(event_handle1), std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, MultipleParameterCallbacks)
|
||||
{
|
||||
bool received_1{false};
|
||||
bool received_2{false};
|
||||
|
||||
auto cb1 = [&received_1](const rclcpp::Parameter &) {received_1 = true;};
|
||||
auto cb2 = [&received_2](const rclcpp::Parameter &) {received_2 = true;};
|
||||
auto cb3 = [](const rclcpp::Parameter &) { /*do nothing*/};
|
||||
|
||||
auto h1 = param_handler->add_parameter_callback("my_int", cb1);
|
||||
auto h2 = param_handler->add_parameter_callback("my_int", cb2);
|
||||
auto h3 = param_handler->add_parameter_callback("my_double", cb3);
|
||||
|
||||
// Test multiple callbacks per parameter
|
||||
param_handler->test_event(same_node_int);
|
||||
EXPECT_EQ(received_1, true);
|
||||
EXPECT_EQ(received_2, true);
|
||||
|
||||
// Test removal of parameter callback by callback handle
|
||||
received_1 = false;
|
||||
received_2 = false;
|
||||
param_handler->remove_parameter_callback(h1);
|
||||
param_handler->test_event(same_node_int);
|
||||
EXPECT_EQ(received_1, false);
|
||||
EXPECT_EQ(received_2, true);
|
||||
|
||||
// Test removal of parameter callback by name
|
||||
received_2 = false;
|
||||
param_handler->remove_parameter_callback(h2);
|
||||
param_handler->test_event(same_node_int);
|
||||
EXPECT_EQ(received_2, false);
|
||||
|
||||
// Should throw if callback handle no longer exists or already removed
|
||||
EXPECT_THROW(param_handler->remove_parameter_callback(h1), std::runtime_error);
|
||||
EXPECT_THROW(param_handler->remove_parameter_callback(h2), std::runtime_error);
|
||||
|
||||
param_handler->remove_parameter_callback(h3);
|
||||
|
||||
// All callbacks should have been removed
|
||||
EXPECT_EQ(received_2, 0);
|
||||
EXPECT_EQ(param_handler->num_event_callbacks(), 0UL);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, LastInFirstCallForParameterCallbacks)
|
||||
{
|
||||
rclcpp::Time time_1;
|
||||
rclcpp::Time time_2;
|
||||
|
||||
// The callbacks will log the current time for comparison purposes. Add a bit of a stall
|
||||
// to ensure that the time noted in the back-to-back calls isn't the same
|
||||
auto cb1 = [this, &time_1](const rclcpp::Parameter &) {
|
||||
time_1 = node->now();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
};
|
||||
auto cb2 = [this, &time_2](const rclcpp::Parameter &) {
|
||||
time_2 = node->now();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
};
|
||||
|
||||
auto h1 = param_handler->add_parameter_callback("my_int", cb1);
|
||||
auto h2 = param_handler->add_parameter_callback("my_int", cb2);
|
||||
|
||||
// Test multiple callbacks per parameter
|
||||
param_handler->test_event(same_node_int);
|
||||
|
||||
// The most-recently install handler should be called first
|
||||
EXPECT_EQ(time_2 < time_1, true);
|
||||
|
||||
param_handler->remove_parameter_callback(h1);
|
||||
param_handler->remove_parameter_callback(h2);
|
||||
EXPECT_EQ(param_handler->num_parameter_callbacks(), 0UL);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, LastInFirstCallForParameterEventCallbacks)
|
||||
{
|
||||
rclcpp::Time time_1;
|
||||
rclcpp::Time time_2;
|
||||
|
||||
// The callbacks will log the current time for comparison purposes. Add a bit of a stall
|
||||
// to ensure that the time noted in the back-to-back calls isn't the same
|
||||
auto cb1 =
|
||||
[this, &time_1](const rcl_interfaces::msg::ParameterEvent::SharedPtr &)
|
||||
{
|
||||
time_1 = node->now();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
};
|
||||
auto cb2 =
|
||||
[this, &time_2](const rcl_interfaces::msg::ParameterEvent::SharedPtr &)
|
||||
{
|
||||
time_2 = node->now();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
};
|
||||
|
||||
auto h1 = param_handler->add_parameter_event_callback(cb1);
|
||||
auto h2 = param_handler->add_parameter_event_callback(cb2);
|
||||
|
||||
// Test multiple callbacks per parameter
|
||||
param_handler->test_event(same_node_int);
|
||||
|
||||
// The most-recently install handler should be called first
|
||||
EXPECT_EQ(time_2 < time_1, true);
|
||||
|
||||
param_handler->remove_parameter_event_callback(h1);
|
||||
param_handler->remove_parameter_event_callback(h2);
|
||||
EXPECT_EQ(param_handler->num_event_callbacks(), 0UL);
|
||||
}
|
||||
@@ -29,10 +29,6 @@
|
||||
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
|
||||
// Note: This is a long running test with rmw_connext_cpp, if you change this file, please check
|
||||
// that this test can complete fully, or adjust the timeout as necessary.
|
||||
// See https://github.com/ros2/rmw_connext/issues/325 for resolution
|
||||
|
||||
class TestPublisher : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -234,7 +234,8 @@ TEST(TestQosParameters, declare_no_parameters_interface) {
|
||||
|
||||
std::map<std::string, rclcpp::Parameter> qos_params;
|
||||
EXPECT_FALSE(
|
||||
node->get_node_parameters_interface()->get_parameters_by_prefix("qos_overrides", qos_params));
|
||||
node->get_node_parameters_interface()->get_parameters_by_prefix(
|
||||
"qos_overrides./my/fully/qualified/topic_name", qos_params));
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
@@ -29,10 +29,6 @@
|
||||
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
|
||||
// Note: This is a long running test with rmw_connext_cpp, if you change this file, please check
|
||||
// that this test can complete fully, or adjust the timeout as necessary.
|
||||
// See https://github.com/ros2/rmw_connext/issues/325 for resolution
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class TestSubscription : public ::testing::Test
|
||||
|
||||
@@ -3,6 +3,15 @@ Changelog for package rclcpp_action
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
7.0.1 (2021-03-22)
|
||||
------------------
|
||||
|
||||
7.0.0 (2021-03-18)
|
||||
------------------
|
||||
* Add support for rmw_connextdds (`#1574 <https://github.com/ros2/rclcpp/issues/1574>`_)
|
||||
* node_handle must be destroyed after client_handle to prevent memory leak (`#1562 <https://github.com/ros2/rclcpp/issues/1562>`_)
|
||||
* Contributors: Andrea Sorbini, Tomoya Fujita
|
||||
|
||||
6.3.1 (2021-02-08)
|
||||
------------------
|
||||
* Finalize rcl_handle to prevent leak (`#1528 <https://github.com/ros2/rclcpp/issues/1528>`_) (`#1529 <https://github.com/ros2/rclcpp/issues/1529>`_)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp_action</name>
|
||||
<version>6.3.1</version>
|
||||
<version>7.0.1</version>
|
||||
<description>Adds action APIs for C++.</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
|
||||
|
||||
@@ -102,8 +102,9 @@ public:
|
||||
|
||||
rclcpp::Context::SharedPtr context_;
|
||||
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
|
||||
std::shared_ptr<rcl_action_client_t> client_handle{nullptr};
|
||||
// node_handle must be destroyed after client_handle to prevent memory leak
|
||||
std::shared_ptr<rcl_node_t> node_handle{nullptr};
|
||||
std::shared_ptr<rcl_action_client_t> client_handle{nullptr};
|
||||
rclcpp::Logger logger;
|
||||
|
||||
using ResponseCallback = std::function<void (std::shared_ptr<void> response)>;
|
||||
|
||||
@@ -29,10 +29,6 @@
|
||||
#include "rclcpp_action/server.hpp"
|
||||
#include "./mocking_utils/patch.hpp"
|
||||
|
||||
// Note: This is a long running test with rmw_connext_cpp, if you change this file, please check
|
||||
// that this test can complete fully, or adjust the timeout as necessary.
|
||||
// See https://github.com/ros2/rmw_connext/issues/325 for resolution
|
||||
|
||||
using Fibonacci = test_msgs::action::Fibonacci;
|
||||
using CancelResponse = typename Fibonacci::Impl::CancelGoalService::Response;
|
||||
using GoalUUID = rclcpp_action::GoalUUID;
|
||||
|
||||
@@ -2,6 +2,12 @@
|
||||
Changelog for package rclcpp_components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
7.0.1 (2021-03-22)
|
||||
------------------
|
||||
|
||||
7.0.0 (2021-03-18)
|
||||
------------------
|
||||
|
||||
6.3.1 (2021-02-08)
|
||||
------------------
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp_components</name>
|
||||
<version>6.3.1</version>
|
||||
<version>7.0.1</version>
|
||||
<description>Package containing tools for dynamically loadable components</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
|
||||
|
||||
@@ -3,6 +3,16 @@ Changelog for package rclcpp_lifecycle
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
7.0.1 (2021-03-22)
|
||||
------------------
|
||||
|
||||
7.0.0 (2021-03-18)
|
||||
------------------
|
||||
* Add support for rmw_connextdds (`#1574 <https://github.com/ros2/rclcpp/issues/1574>`_)
|
||||
* Fix SEGV caused by order of destruction of Node sub-interfaces (`#1469 <https://github.com/ros2/rclcpp/issues/1469>`_)
|
||||
* Enforce static parameter types (`#1522 <https://github.com/ros2/rclcpp/issues/1522>`_)
|
||||
* Contributors: Andrea Sorbini, Colin MacKenzie, Ivan Santiago Paunovic
|
||||
|
||||
6.3.1 (2021-02-08)
|
||||
------------------
|
||||
|
||||
|
||||
@@ -282,9 +282,39 @@ public:
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value = rclcpp::ParameterValue(),
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor());
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare and initialize a parameter, return the effective value.
|
||||
/**
|
||||
* \sa rclcpp::Node::declare_parameter
|
||||
*/
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::ParameterType type,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor{},
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare a parameter
|
||||
[[deprecated(
|
||||
"declare_parameter() with only a name is deprecated and will be deleted in the future.\n" \
|
||||
"If you want to declare a parameter that won't change type without a default value use:\n" \
|
||||
"`node->declare_parameter<ParameterT>(name)`, where e.g. ParameterT=int64_t.\n\n" \
|
||||
"If you want to declare a parameter that can dynamically change type use:\n" \
|
||||
"```\n" \
|
||||
"rcl_interfaces::msg::ParameterDescriptor descriptor;\n" \
|
||||
"descriptor.dynamic_typing = true;\n" \
|
||||
"node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);\n" \
|
||||
"```"
|
||||
)]]
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
const rclcpp::ParameterValue &
|
||||
declare_parameter(const std::string & name);
|
||||
|
||||
/// Declare and initialize a parameter with a type.
|
||||
/**
|
||||
@@ -296,7 +326,20 @@ public:
|
||||
const std::string & name,
|
||||
const ParameterT & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor());
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare and initialize a parameter with a type.
|
||||
/**
|
||||
* See the non-templated declare_parameter() on this class for details.
|
||||
*/
|
||||
template<typename ParameterT>
|
||||
auto
|
||||
declare_parameter(
|
||||
const std::string & name,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
|
||||
rcl_interfaces::msg::ParameterDescriptor(),
|
||||
bool ignore_override = false);
|
||||
|
||||
/// Declare and initialize several parameters with the same namespace and type.
|
||||
/**
|
||||
|
||||
@@ -135,12 +135,32 @@ auto
|
||||
LifecycleNode::declare_parameter(
|
||||
const std::string & name,
|
||||
const ParameterT & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor)
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
|
||||
bool ignore_override)
|
||||
{
|
||||
return this->declare_parameter(
|
||||
name,
|
||||
rclcpp::ParameterValue(default_value),
|
||||
parameter_descriptor
|
||||
parameter_descriptor,
|
||||
ignore_override
|
||||
).get<ParameterT>();
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
auto
|
||||
LifecycleNode::declare_parameter(
|
||||
const std::string & name,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
|
||||
bool ignore_override)
|
||||
{
|
||||
// get advantage of parameter value template magic to get
|
||||
// the correct rclcpp::ParameterType from ParameterT
|
||||
rclcpp::ParameterValue value{ParameterT{}};
|
||||
return this->declare_parameter(
|
||||
name,
|
||||
value.get_type(),
|
||||
parameter_descriptor,
|
||||
ignore_override
|
||||
).get<ParameterT>();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp_lifecycle</name>
|
||||
<version>6.3.1</version>
|
||||
<version>7.0.1</version>
|
||||
<description>Package containing a prototype for lifecycle implementation</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
|
||||
|
||||
@@ -128,7 +128,18 @@ LifecycleNode::LifecycleNode(
|
||||
}
|
||||
|
||||
LifecycleNode::~LifecycleNode()
|
||||
{}
|
||||
{
|
||||
// release sub-interfaces in an order that allows them to consult with node_base during tear-down
|
||||
node_waitables_.reset();
|
||||
node_time_source_.reset();
|
||||
node_parameters_.reset();
|
||||
node_clock_.reset();
|
||||
node_services_.reset();
|
||||
node_topics_.reset();
|
||||
node_timers_.reset();
|
||||
node_logging_.reset();
|
||||
node_graph_.reset();
|
||||
}
|
||||
|
||||
const char *
|
||||
LifecycleNode::get_name() const
|
||||
@@ -159,9 +170,43 @@ const rclcpp::ParameterValue &
|
||||
LifecycleNode::declare_parameter(
|
||||
const std::string & name,
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor)
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
|
||||
bool ignore_override)
|
||||
{
|
||||
return this->node_parameters_->declare_parameter(name, default_value, parameter_descriptor);
|
||||
return this->node_parameters_->declare_parameter(
|
||||
name, default_value, parameter_descriptor, ignore_override);
|
||||
}
|
||||
|
||||
const rclcpp::ParameterValue &
|
||||
LifecycleNode::declare_parameter(const std::string & name)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
return this->node_parameters_->declare_parameter(name);
|
||||
#ifndef _WIN32
|
||||
# pragma GCC diagnostic pop
|
||||
#else
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
|
||||
const rclcpp::ParameterValue &
|
||||
LifecycleNode::declare_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::ParameterType type,
|
||||
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
|
||||
bool ignore_override)
|
||||
{
|
||||
return this->node_parameters_->declare_parameter(
|
||||
name,
|
||||
type,
|
||||
parameter_descriptor,
|
||||
ignore_override);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -31,10 +31,6 @@
|
||||
|
||||
#include "./mocking_utils/patch.hpp"
|
||||
|
||||
// Note: This is a long running test with rmw_connext_cpp, if you change this file, please check
|
||||
// that this test can complete fully, or adjust the timeout as necessary.
|
||||
// See https://github.com/ros2/rmw_connext/issues/325 for resolution
|
||||
|
||||
using lifecycle_msgs::msg::State;
|
||||
using lifecycle_msgs::msg::Transition;
|
||||
|
||||
@@ -425,7 +421,9 @@ TEST_F(TestDefaultStateMachine, check_parameters) {
|
||||
rclcpp::exceptions::ParameterNotDeclaredException);
|
||||
|
||||
// Default descriptor overload
|
||||
test_node->declare_parameter(bool_name, rclcpp::ParameterValue(true));
|
||||
rcl_interfaces::msg::ParameterDescriptor bool_descriptor;
|
||||
bool_descriptor.dynamic_typing = true;
|
||||
test_node->declare_parameter(bool_name, rclcpp::ParameterValue(true), bool_descriptor);
|
||||
|
||||
// Explicit descriptor overload
|
||||
rcl_interfaces::msg::ParameterDescriptor int_descriptor;
|
||||
|
||||
@@ -52,10 +52,6 @@ constexpr char const * node_get_transition_graph_topic =
|
||||
"/lc_talker/get_transition_graph";
|
||||
const lifecycle_msgs::msg::State unknown_state = lifecycle_msgs::msg::State();
|
||||
|
||||
// Note: This is a long running test with rmw_connext_cpp, if you change this file, please check
|
||||
// that this test can complete fully, or adjust the timeout as necessary.
|
||||
// See https://github.com/ros2/rmw_connext/issues/325 for resolution
|
||||
|
||||
class EmptyLifecycleNode : public rclcpp_lifecycle::LifecycleNode
|
||||
{
|
||||
public:
|
||||
|
||||
Reference in New Issue
Block a user