Compare commits
20 Commits
runtime_in
...
mjeronimo/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8fb32de707 | ||
|
|
b3b683f469 | ||
|
|
e41f75c05f | ||
|
|
36e0190c27 | ||
|
|
f36035097f | ||
|
|
e4bc1d8968 | ||
|
|
0d5d75c9a2 | ||
|
|
255943465d | ||
|
|
9cc41c76c1 | ||
|
|
7c19dbb408 | ||
|
|
921d2b4192 | ||
|
|
12329bf79c | ||
|
|
f67d8298b2 | ||
|
|
a010d2dd15 | ||
|
|
f1893273f5 | ||
|
|
884a2d8ad8 | ||
|
|
72817ab1f2 | ||
|
|
cdf4156bbe | ||
|
|
3246e349d3 | ||
|
|
6473041f1c |
@@ -80,6 +80,7 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/parameter_value.cpp
|
||||
src/rclcpp/parameter_client.cpp
|
||||
src/rclcpp/parameter_events_filter.cpp
|
||||
src/rclcpp/parameter_events_subscriber.cpp
|
||||
src/rclcpp/parameter_map.cpp
|
||||
src/rclcpp/parameter_service.cpp
|
||||
src/rclcpp/publisher_base.cpp
|
||||
|
||||
234
rclcpp/include/rclcpp/parameter_events_subscriber.hpp
Normal file
234
rclcpp/include/rclcpp/parameter_events_subscriber.hpp
Normal file
@@ -0,0 +1,234 @@
|
||||
// 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_EVENTS_SUBSCRIBER_HPP_
|
||||
#define RCLCPP__PARAMETER_EVENTS_SUBSCRIBER_HPP_
|
||||
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl_interfaces/msg/parameter_event.hpp"
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_logging_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_logging_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/subscription.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;
|
||||
};
|
||||
|
||||
class ParameterEventsSubscriber
|
||||
{
|
||||
public:
|
||||
/// Construct a subscriber to parameter events
|
||||
template<typename NodeT>
|
||||
ParameterEventsSubscriber(
|
||||
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);
|
||||
node_logging_ = rclcpp::node_interfaces::get_node_logging_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(&ParameterEventsSubscriber::event_callback, this, std::placeholders::_1));
|
||||
}
|
||||
|
||||
using ParameterEventCallbackType =
|
||||
ParameterEventCallbackHandle::ParameterEventCallbackType;
|
||||
|
||||
/// Set a custom callback for parameter events.
|
||||
/**
|
||||
* Multiple callbacks are allowed.
|
||||
*
|
||||
* \param[in] callback Function callback to be evaluated on event.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
ParameterEventCallbackHandle::SharedPtr
|
||||
add_parameter_event_callback(
|
||||
ParameterEventCallbackType callback);
|
||||
|
||||
/// Remove parameter event callback.
|
||||
/**
|
||||
* Calling this function will set the event callback to nullptr.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_parameter_event_callback(
|
||||
const ParameterEventCallbackHandle * const handle);
|
||||
|
||||
using ParameterCallbackType = ParameterCallbackHandle::ParameterCallbackType;
|
||||
|
||||
/// Add a custom callback for a specified parameter.
|
||||
/**
|
||||
* If a node_name is not provided, defaults to the current node.
|
||||
*
|
||||
* \param[in] parameter_name Name of parameter.
|
||||
* \param[in] callback Function callback to be evaluated upon parameter event.
|
||||
* \param[in] node_name Name of node which hosts the parameter.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
ParameterCallbackHandle::SharedPtr
|
||||
add_parameter_callback(
|
||||
const std::string & parameter_name,
|
||||
ParameterCallbackType callback,
|
||||
const std::string & node_name = "");
|
||||
|
||||
/// Remove a custom callback for a specified parameter given its callback handle.
|
||||
/**
|
||||
* 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] handle Pointer to callback handle to be removed.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_parameter_callback(
|
||||
const ParameterCallbackHandle * const handle);
|
||||
|
||||
/// Get a rclcpp::Parameter from parameter event, return true if parameter name & node in 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 true if requested parameter name & node is in event, false otherwise
|
||||
*/
|
||||
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 a 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.
|
||||
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 Interfaces used for base and logging.
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base_;
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface> node_logging_;
|
||||
|
||||
// *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_EVENTS_SUBSCRIBER_HPP_
|
||||
@@ -150,6 +150,7 @@
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/parameter_client.hpp"
|
||||
#include "rclcpp/parameter_service.hpp"
|
||||
#include "rclcpp/parameter_events_subscriber.hpp"
|
||||
#include "rclcpp/rate.hpp"
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
221
rclcpp/src/rclcpp/parameter_events_subscriber.cpp
Normal file
221
rclcpp/src/rclcpp/parameter_events_subscriber.cpp
Normal file
@@ -0,0 +1,221 @@
|
||||
// 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_events_subscriber.hpp"
|
||||
#include "rcpputils/join.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
ParameterEventCallbackHandle::SharedPtr
|
||||
ParameterEventsSubscriber::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
|
||||
ParameterEventsSubscriber::remove_parameter_event_callback(
|
||||
const ParameterEventCallbackHandle * const handle)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
auto it = std::find_if(
|
||||
event_callbacks_.begin(),
|
||||
event_callbacks_.end(),
|
||||
HandleCompare<ParameterEventCallbackHandle>(handle));
|
||||
if (it != event_callbacks_.end()) {
|
||||
event_callbacks_.erase(it);
|
||||
} else {
|
||||
throw std::runtime_error("Callback doesn't exist");
|
||||
}
|
||||
}
|
||||
|
||||
ParameterCallbackHandle::SharedPtr
|
||||
ParameterEventsSubscriber::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
|
||||
ParameterEventsSubscriber::remove_parameter_callback(
|
||||
const ParameterCallbackHandle * const handle)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
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
|
||||
ParameterEventsSubscriber::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
|
||||
ParameterEventsSubscriber::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>
|
||||
ParameterEventsSubscriber::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
|
||||
ParameterEventsSubscriber::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
|
||||
ParameterEventsSubscriber::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
|
||||
@@ -323,6 +323,16 @@ if(TARGET test_parameter)
|
||||
)
|
||||
target_link_libraries(test_parameter ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_parameter_events_subscriber test_parameter_events_subscriber.cpp)
|
||||
if(TARGET test_parameter_events_subscriber)
|
||||
ament_target_dependencies(test_parameter_events_subscriber
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_parameter_events_subscriber ${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})
|
||||
|
||||
348
rclcpp/test/rclcpp/test_parameter_events_subscriber.cpp
Normal file
348
rclcpp/test/rclcpp/test_parameter_events_subscriber.cpp
Normal file
@@ -0,0 +1,348 @@
|
||||
// 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 <memory>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
class TestParameterEventsSubscriber : public rclcpp::ParameterEventsSubscriber
|
||||
{
|
||||
public:
|
||||
explicit TestParameterEventsSubscriber(rclcpp::Node::SharedPtr node)
|
||||
: ParameterEventsSubscriber(node)
|
||||
{}
|
||||
|
||||
void test_event(const rcl_interfaces::msg::ParameterEvent::SharedPtr event)
|
||||
{
|
||||
event_callback(event);
|
||||
}
|
||||
};
|
||||
|
||||
class TestNode : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::NodeOptions options;
|
||||
options.allow_undeclared_parameters(true);
|
||||
|
||||
node = std::make_shared<rclcpp::Node>(
|
||||
"test_parameter_events_subscriber", options);
|
||||
|
||||
remote_node_name = "/remote_node";
|
||||
diff_ns_name = "/ns/remote_node";
|
||||
|
||||
ParamSubscriber = std::make_shared<TestParameterEventsSubscriber>(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();
|
||||
ParamSubscriber.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<TestParameterEventsSubscriber> ParamSubscriber;
|
||||
};
|
||||
|
||||
TEST_F(TestNode, RegisterParameterCallback)
|
||||
{
|
||||
bool received;
|
||||
auto cb = [&received](const rclcpp::Parameter &) {received = true;};
|
||||
|
||||
auto h1 = ParamSubscriber->add_parameter_callback("my_double", cb);
|
||||
auto h2 = ParamSubscriber->add_parameter_callback("my_int", cb);
|
||||
auto h3 = ParamSubscriber->add_parameter_callback("my_string", cb, remote_node_name);
|
||||
auto h4 = ParamSubscriber->add_parameter_callback("my_bool", cb, diff_ns_name);
|
||||
|
||||
received = false;
|
||||
ParamSubscriber->test_event(same_node_double);
|
||||
EXPECT_EQ(received, true);
|
||||
|
||||
received = false;
|
||||
ParamSubscriber->test_event(same_node_int);
|
||||
EXPECT_EQ(received, true);
|
||||
|
||||
received = false;
|
||||
ParamSubscriber->test_event(remote_node_string);
|
||||
EXPECT_EQ(received, true);
|
||||
|
||||
received = false;
|
||||
ParamSubscriber->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 = ParamSubscriber->add_parameter_callback("my_int", cb1);
|
||||
auto h2 = ParamSubscriber->add_parameter_callback("my_int", cb2, remote_node_name);
|
||||
|
||||
ParamSubscriber->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;
|
||||
|
||||
ParamSubscriber->test_event(diff_node_int);
|
||||
EXPECT_NE(int_param_node1, 1);
|
||||
EXPECT_EQ(int_param_node2, 1);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, GetParameterFromEvent)
|
||||
{
|
||||
using rclcpp::ParameterEventsSubscriber;
|
||||
std::string node_name = node->get_fully_qualified_name();
|
||||
std::string wrong_name = "/wrong_node_name";
|
||||
|
||||
rclcpp::Parameter p;
|
||||
EXPECT_TRUE(
|
||||
ParameterEventsSubscriber::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(
|
||||
ParameterEventsSubscriber::get_parameter_from_event(*multiple, p, "my_int", wrong_name));
|
||||
// False if parameter not part of event
|
||||
EXPECT_FALSE(
|
||||
ParameterEventsSubscriber::get_parameter_from_event(*diff_ns_bool, p, "my_int", node_name));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
ParameterEventsSubscriber::get_parameter_from_event(*multiple, "my_int", node_name));
|
||||
// Throws if parameter not with correct node name
|
||||
EXPECT_THROW(
|
||||
ParameterEventsSubscriber::get_parameter_from_event(*multiple, "my_int", wrong_name),
|
||||
std::runtime_error);
|
||||
// Throws if parameter not part of event
|
||||
EXPECT_THROW(
|
||||
ParameterEventsSubscriber::get_parameter_from_event(*diff_ns_bool, "my_int", node_name),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, GetParametersFromEvent)
|
||||
{
|
||||
using rclcpp::ParameterEventsSubscriber;
|
||||
std::string node_name = node->get_fully_qualified_name();
|
||||
|
||||
auto params = ParameterEventsSubscriber::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 = ParameterEventsSubscriber::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 = ParameterEventsSubscriber::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::ParameterEventsSubscriber;
|
||||
|
||||
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, &bool_param, &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 (ParameterEventsSubscriber::get_parameter_from_event(*event, p, "my_int", node_name)) {
|
||||
int_param = p.get_value<int64_t>();
|
||||
}
|
||||
try {
|
||||
p = ParameterEventsSubscriber::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 (ParameterEventsSubscriber::get_parameter_from_event(*event, p, "my_bool",
|
||||
diff_ns_name))
|
||||
{
|
||||
bool_param = p.get_value<bool>();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
auto event_handle = ParamSubscriber->add_parameter_event_callback(cb);
|
||||
auto event_handle2 = ParamSubscriber->add_parameter_event_callback(cb2);
|
||||
|
||||
bool_param = false;
|
||||
ParamSubscriber->test_event(multiple);
|
||||
EXPECT_EQ(received, true);
|
||||
EXPECT_EQ(product, 1.0);
|
||||
EXPECT_EQ(bool_param, false);
|
||||
|
||||
ParamSubscriber->test_event(diff_ns_bool);
|
||||
EXPECT_EQ(bool_param, true);
|
||||
|
||||
// Test removal of event callback
|
||||
received = false;
|
||||
bool_param = false;
|
||||
ParamSubscriber->remove_parameter_event_callback(event_handle.get());
|
||||
ParamSubscriber->test_event(multiple);
|
||||
ParamSubscriber->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(
|
||||
ParamSubscriber->remove_parameter_event_callback(event_handle.get()), 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 = ParamSubscriber->add_parameter_callback("my_int", cb1);
|
||||
auto h2 = ParamSubscriber->add_parameter_callback("my_int", cb2);
|
||||
auto h3 = ParamSubscriber->add_parameter_callback("my_double", cb3);
|
||||
|
||||
// Test multiple callbacks per parameter
|
||||
ParamSubscriber->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;
|
||||
ParamSubscriber->remove_parameter_callback(h1.get());
|
||||
ParamSubscriber->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;
|
||||
ParamSubscriber->remove_parameter_callback(h2.get());
|
||||
ParamSubscriber->test_event(same_node_int);
|
||||
EXPECT_EQ(received_2, false);
|
||||
|
||||
// Should throw if callback handle no longer exists or already removed
|
||||
EXPECT_THROW(ParamSubscriber->remove_parameter_callback(h1.get()), std::runtime_error);
|
||||
EXPECT_THROW(ParamSubscriber->remove_parameter_callback(h2.get()), std::runtime_error);
|
||||
}
|
||||
Reference in New Issue
Block a user