Compare commits

...

20 Commits

Author SHA1 Message Date
Michael Jeronimo
8fb32de707 Add a function to get all parameter values from a parameter event
Signed-off-by: Michael Jeronimo <michael.jeronimo@openrobotics.org>
2020-11-30 11:05:25 -08:00
Michael Jeronimo
b3b683f469 Remove a stray debug trace
Signed-off-by: Michael Jeronimo <michael.jeronimo@openrobotics.org>
2020-11-30 11:05:25 -08:00
Michael Jeronimo
e41f75c05f Remove some unneeded code
Signed-off-by: Michael Jeronimo <michael.jeronimo@openrobotics.org>
2020-11-30 11:05:25 -08:00
Michael Jeronimo
36e0190c27 Get rid of a few compiler warnings; add test to build
Signed-off-by: Michael Jeronimo <michael.jeronimo@openrobotics.org>
2020-11-30 11:05:25 -08:00
bpwilcox
f36035097f address feedback: replace ParameterEventsFilter dependency, fix copyright, add get_node_logging_interface, modify constructor
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
e4bc1d8968 address feedback: move HandleCompare, test exceptions, reference code source
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
0d5d75c9a2 update utility function description
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
255943465d use get_child for parameter event subscriber logger
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
9cc41c76c1 support multiple parameter event callbacks
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
7c19dbb408 use absolute parameter event topic for parameter event subscriber
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
921d2b4192 map parameters to list of callbacks
functions to remove parameter callbacks

add functions to remove event callbacks, remove subscriptions, allow subscribing event callback to many namespaces, additional test coverage

Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
12329bf79c address feedback part 1: static get_parameter method, remove register_parameter_update, mutex for thread-safety
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
f67d8298b2 add RCLCPP_PUBLIC macro
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
a010d2dd15 address feedback (wjwwood) part 1
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>

use const string & for node name

Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
f1893273f5 use unordered map with string pair, add test for different nodes same parameter, address feedback
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
884a2d8ad8 pass rclcpp::Parameter object to callback, rename functions, get param from event
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
72817ab1f2 add try-catch and warning around getting parameter value
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
cdf4156bbe fix lint and uncrustify issues
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
3246e349d3 test improvements, path name fixes, and more documentation
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
bpwilcox
6473041f1c add ParameterEventsSubscriber class and tests
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>
2020-11-30 11:05:25 -08:00
6 changed files with 815 additions and 0 deletions

View File

@@ -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

View 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_

View File

@@ -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"

View 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

View File

@@ -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})

View 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);
}