Compare commits
31 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
866c56d153 | ||
|
|
6a244e3305 | ||
|
|
bddd6105c2 | ||
|
|
38cfc4c215 | ||
|
|
b009212508 | ||
|
|
c7065f7f34 | ||
|
|
db7469798f | ||
|
|
5ac7f1f024 | ||
|
|
f8a7ace7a8 | ||
|
|
fcc505f453 | ||
|
|
841a4632a4 | ||
|
|
cd86362f75 | ||
|
|
6397047d47 | ||
|
|
7f783cbf58 | ||
|
|
c85ff926d2 | ||
|
|
a4a72ac544 | ||
|
|
4e166cbed3 | ||
|
|
6cbb994aca | ||
|
|
825b4e4650 | ||
|
|
41c7f68013 | ||
|
|
02caec12c3 | ||
|
|
87a4cefb83 | ||
|
|
d3c95a6a46 | ||
|
|
1500449c11 | ||
|
|
95cb964a3b | ||
|
|
f160024ba4 | ||
|
|
e6ea37ed5a | ||
|
|
354413c060 | ||
|
|
63bdf2add4 | ||
|
|
ad019b9827 | ||
|
|
eb49444c32 |
@@ -2,6 +2,39 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
30.1.5 (2026-02-09)
|
||||
-------------------
|
||||
* remove default: so that compiler can detect the missing case. (`#3048 <https://github.com/ros2/rclcpp/issues/3048>`_)
|
||||
* use weak_ptr for rcl entities in the memory strategy. (`#2988 <https://github.com/ros2/rclcpp/issues/2988>`_)
|
||||
* remove test_static_executor_entities_collector.cpp (`#3041 <https://github.com/ros2/rclcpp/issues/3041>`_)
|
||||
* include the 1st spin that might throw the exception. (`#3042 <https://github.com/ros2/rclcpp/issues/3042>`_)
|
||||
* print warning message on owner node if the parameter operation fails. (`#3037 <https://github.com/ros2/rclcpp/issues/3037>`_)
|
||||
* fix context in wait for message wait set (`#3030 <https://github.com/ros2/rclcpp/issues/3030>`_)
|
||||
* Revert "construct wait set with passed in context (`#3021 <https://github.com/ros2/rclcpp/issues/3021>`_)" (`#3028 <https://github.com/ros2/rclcpp/issues/3028>`_)
|
||||
* construct wait set with passed in context (`#3021 <https://github.com/ros2/rclcpp/issues/3021>`_)
|
||||
* Improve the robustness of the TopicEndpointInfo constructor (`#3013 <https://github.com/ros2/rclcpp/issues/3013>`_)
|
||||
* Deprecate the shared_ptr<MessageT> subscription callback signatures (`#2975 <https://github.com/ros2/rclcpp/issues/2975>`_)
|
||||
* Contributors: Barry Xu, Maurice Alexander Purnawan, Michael Carroll, Rahat Dhande, Tomoya Fujita
|
||||
|
||||
30.1.4 (2025-12-23)
|
||||
-------------------
|
||||
* Updated deprecated ament_index_cpp API (`#3011 <https://github.com/ros2/rclcpp/issues/3011>`_)
|
||||
* Unified Node Interfaces: Add const version of get_node_x_interface() (`#3006 <https://github.com/ros2/rclcpp/issues/3006>`_)
|
||||
* Parameter Descriptor Simplification (`#2179 <https://github.com/ros2/rclcpp/issues/2179>`_)
|
||||
* ParameterEventHandler support ContentFiltering (`#2971 <https://github.com/ros2/rclcpp/issues/2971>`_)
|
||||
* update policy_name_from_kind && test_qos (`#2156 <https://github.com/ros2/rclcpp/issues/2156>`_)
|
||||
* Add ability to disable and enable subscription's callbacks (`#2985 <https://github.com/ros2/rclcpp/issues/2985>`_)
|
||||
* Switch to isolated testing via rmw_test_fixture (`#2929 <https://github.com/ros2/rclcpp/issues/2929>`_)
|
||||
* remove I/O from signal handler. (`#2986 <https://github.com/ros2/rclcpp/issues/2986>`_)
|
||||
* Contributors: Alejandro Hernández Cordero, Andrianov Roman, Barry Xu, Lucas Wendland, Michael Orlov, Tomoya Fujita, fabianhirmann, yadunund
|
||||
|
||||
30.1.3 (2025-11-18)
|
||||
-------------------
|
||||
* correct test function descriptions (`#2970 <https://github.com/ros2/rclcpp/issues/2970>`_)
|
||||
* add : get clients, servers info (`#2569 <https://github.com/ros2/rclcpp/issues/2569>`_)
|
||||
* Fix REP url locations (`#2987 <https://github.com/ros2/rclcpp/issues/2987>`_)
|
||||
* Contributors: Minju, Lee, Tim Clephas, Yuchen966
|
||||
|
||||
30.1.2 (2025-10-21)
|
||||
-------------------
|
||||
* clear handles before node destruction in test_memory_strategy. (`#2969 <https://github.com/ros2/rclcpp/issues/2969>`_)
|
||||
|
||||
@@ -103,6 +103,7 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/node_options.cpp
|
||||
src/rclcpp/parameter.cpp
|
||||
src/rclcpp/parameter_client.cpp
|
||||
src/rclcpp/parameter_descriptor_wrapper.cpp
|
||||
src/rclcpp/parameter_event_handler.cpp
|
||||
src/rclcpp/parameter_events_filter.cpp
|
||||
src/rclcpp/parameter_map.cpp
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
This document is a declaration of software quality for the `rclcpp` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
|
||||
This document is a declaration of software quality for the `rclcpp` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/).
|
||||
|
||||
# rclcpp Quality Declaration
|
||||
|
||||
The package `rclcpp` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware.
|
||||
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html).
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://reps.openrobotics.org/rep-2004/).
|
||||
|
||||
## Version Policy [1]
|
||||
|
||||
@@ -55,7 +55,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d
|
||||
|
||||
### Continuous Integration [2.iv]
|
||||
|
||||
All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers)
|
||||
All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers)
|
||||
|
||||
Currently nightly results can be seen here:
|
||||
|
||||
@@ -213,7 +213,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
|
||||
|
||||
## Platform Support [6]
|
||||
|
||||
`rclcpp` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them.
|
||||
`rclcpp` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them.
|
||||
|
||||
Currently nightly build status can be seen here:
|
||||
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp/)
|
||||
@@ -225,4 +225,4 @@ Currently nightly build status can be seen here:
|
||||
|
||||
### Vulnerability Disclosure Policy [7.i]
|
||||
|
||||
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html).
|
||||
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/).
|
||||
|
||||
@@ -15,8 +15,10 @@
|
||||
#ifndef RCLCPP__ANY_SUBSCRIPTION_CALLBACK_HPP_
|
||||
#define RCLCPP__ANY_SUBSCRIPTION_CALLBACK_HPP_
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
@@ -375,7 +377,19 @@ public:
|
||||
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
|
||||
}
|
||||
|
||||
AnySubscriptionCallback(const AnySubscriptionCallback &) = default;
|
||||
AnySubscriptionCallback(const AnySubscriptionCallback & other)
|
||||
: callback_variant_(other.callback_variant_),
|
||||
callback_disabled_(other.callback_disabled_.load()),
|
||||
subscribed_type_allocator_(other.subscribed_type_allocator_),
|
||||
subscribed_type_deleter_(other.subscribed_type_deleter_),
|
||||
ros_message_type_allocator_(other.ros_message_type_allocator_),
|
||||
ros_message_type_deleter_(other.ros_message_type_deleter_),
|
||||
serialized_message_allocator_(other.serialized_message_allocator_),
|
||||
serialized_message_deleter_(other.serialized_message_deleter_)
|
||||
{
|
||||
allocator::set_allocator_for_deleter(&subscribed_type_deleter_, &subscribed_type_allocator_);
|
||||
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
|
||||
}
|
||||
|
||||
/// Generic function for setting the callback.
|
||||
/**
|
||||
@@ -393,12 +407,91 @@ public:
|
||||
// converted to one another, e.g. shared_ptr and unique_ptr.
|
||||
using scbth = detail::SubscriptionCallbackTypeHelper<MessageT, CallbackT>;
|
||||
|
||||
callback_variant_ = static_cast<typename scbth::callback_type>(callback);
|
||||
// Determine if the given CallbackT is a deprecated signature or not.
|
||||
constexpr auto is_deprecated =
|
||||
rclcpp::function_traits::same_arguments<
|
||||
typename scbth::callback_type,
|
||||
std::function<void(std::shared_ptr<SubscribedType>)>
|
||||
>::value ||
|
||||
rclcpp::function_traits::same_arguments<
|
||||
typename scbth::callback_type,
|
||||
std::function<void(std::shared_ptr<SubscribedType>, const rclcpp::MessageInfo &)>
|
||||
>::value ||
|
||||
rclcpp::function_traits::same_arguments<
|
||||
typename scbth::callback_type,
|
||||
std::function<void(std::shared_ptr<ROSMessageType>)>
|
||||
>::value ||
|
||||
rclcpp::function_traits::same_arguments<
|
||||
typename scbth::callback_type,
|
||||
std::function<void(std::shared_ptr<ROSMessageType>, const rclcpp::MessageInfo &)>
|
||||
>::value ||
|
||||
rclcpp::function_traits::same_arguments<
|
||||
typename scbth::callback_type,
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)>
|
||||
>::value ||
|
||||
rclcpp::function_traits::same_arguments<
|
||||
typename scbth::callback_type,
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>, const rclcpp::MessageInfo &)>
|
||||
>::value;
|
||||
|
||||
// Use the discovered type to force the type of callback when assigning
|
||||
// into the variant.
|
||||
if constexpr (is_deprecated) {
|
||||
// If deprecated, call sub-routine that is deprecated.
|
||||
set_deprecated(static_cast<typename scbth::callback_type>(callback));
|
||||
} else {
|
||||
// Otherwise just assign it.
|
||||
callback_variant_ = static_cast<typename scbth::callback_type>(callback);
|
||||
}
|
||||
|
||||
// Return copy of self for easier testing, normally will be compiled out.
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Function for shared_ptr to non-const MessageT, which is deprecated.
|
||||
template<typename SetT>
|
||||
// *INDENT-OFF*
|
||||
#if !defined(RCLCPP_AVOID_DEPRECATIONS_FOR_UNIT_TESTS)
|
||||
// suppress deprecation warnings in `test_any_subscription_callback.cpp`
|
||||
[[deprecated("use 'void(std::shared_ptr<const MessageT>)' instead")]]
|
||||
#endif
|
||||
// *INDENT-ON*
|
||||
void
|
||||
set_deprecated(std::function<void(std::shared_ptr<SetT>)> callback)
|
||||
{
|
||||
callback_variant_ = callback;
|
||||
}
|
||||
|
||||
/// Function for shared_ptr to non-const MessageT with MessageInfo, which is deprecated.
|
||||
template<typename SetT>
|
||||
// *INDENT-OFF*
|
||||
#if !defined(RCLCPP_AVOID_DEPRECATIONS_FOR_UNIT_TESTS)
|
||||
// suppress deprecation warnings in `test_any_subscription_callback.cpp`
|
||||
[[deprecated(
|
||||
"use 'void(std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)' instead"
|
||||
)]]
|
||||
#endif
|
||||
// *INDENT-ON*
|
||||
void
|
||||
set_deprecated(std::function<void(std::shared_ptr<SetT>, const rclcpp::MessageInfo &)> callback)
|
||||
{
|
||||
callback_variant_ = callback;
|
||||
}
|
||||
|
||||
/// Disable the callback from being called during dispatch.
|
||||
void disable()
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
|
||||
callback_disabled_.store(true);
|
||||
}
|
||||
|
||||
/// Enable the callback to be called during dispatch.
|
||||
void enable()
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
|
||||
callback_disabled_.store(false);
|
||||
}
|
||||
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
|
||||
create_ros_unique_ptr_from_ros_shared_ptr_message(
|
||||
const std::shared_ptr<const ROSMessageType> & message)
|
||||
@@ -469,6 +562,10 @@ public:
|
||||
std::shared_ptr<ROSMessageType> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
|
||||
if (callback_disabled_.load()) {
|
||||
return;
|
||||
}
|
||||
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
// Check if the variant is "unset", throw if it is.
|
||||
if (callback_variant_.index() == 0) {
|
||||
@@ -569,6 +666,10 @@ public:
|
||||
std::shared_ptr<const rclcpp::SerializedMessage> serialized_message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
|
||||
if (callback_disabled_.load()) {
|
||||
return;
|
||||
}
|
||||
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
// Check if the variant is "unset", throw if it is.
|
||||
if (callback_variant_.index() == 0) {
|
||||
@@ -648,6 +749,10 @@ public:
|
||||
std::shared_ptr<const SubscribedType> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
|
||||
if (callback_disabled_.load()) {
|
||||
return;
|
||||
}
|
||||
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), true);
|
||||
// Check if the variant is "unset", throw if it is.
|
||||
if (callback_variant_.index() == 0) {
|
||||
@@ -778,6 +883,10 @@ public:
|
||||
std::unique_ptr<SubscribedType, SubscribedTypeDeleter> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
|
||||
if (callback_disabled_.load()) {
|
||||
return;
|
||||
}
|
||||
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), true);
|
||||
// Check if the variant is "unset", throw if it is.
|
||||
if (callback_variant_.index() == 0) {
|
||||
@@ -972,6 +1081,8 @@ private:
|
||||
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2162r0.html
|
||||
// For now, compose the variant into this class as a private attribute.
|
||||
typename HelperT::variant_type callback_variant_;
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::atomic_bool callback_disabled_{false};
|
||||
|
||||
SubscribedTypeAllocator subscribed_type_allocator_;
|
||||
SubscribedTypeDeleter subscribed_type_deleter_;
|
||||
|
||||
@@ -270,8 +270,8 @@ apply_qos_override(
|
||||
RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING(
|
||||
reliability, RELIABILITY, value, qos);
|
||||
break;
|
||||
default:
|
||||
throw std::invalid_argument{"unknown QosPolicyKind"};
|
||||
case QosPolicyKind::Invalid:
|
||||
throw std::invalid_argument{"invalid QosPolicyKind"};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -332,9 +332,11 @@ get_default_qos_param_value(rclcpp::QosPolicyKind kind, const rclcpp::QoS & qos)
|
||||
return ParameterValue(
|
||||
check_if_stringified_policy_is_null(
|
||||
rmw_qos_reliability_policy_to_str(rmw_qos.reliability), kind));
|
||||
default:
|
||||
throw std::invalid_argument{"unknown QoS policy kind"};
|
||||
case QosPolicyKind::Invalid:
|
||||
throw std::invalid_argument{"invalid QoS policy kind"};
|
||||
}
|
||||
|
||||
return ParameterValue();
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
@@ -29,7 +29,7 @@ template<typename OptionsT, typename NodeBaseT>
|
||||
bool
|
||||
resolve_enable_topic_statistics(const OptionsT & options, const NodeBaseT & node_base)
|
||||
{
|
||||
bool topic_stats_enabled;
|
||||
bool topic_stats_enabled = false;
|
||||
switch (options.topic_stats_options.state) {
|
||||
case TopicStatisticsState::Enable:
|
||||
topic_stats_enabled = true;
|
||||
|
||||
@@ -30,7 +30,7 @@ template<typename OptionsT, typename NodeBaseT>
|
||||
bool
|
||||
resolve_use_intra_process(const OptionsT & options, const NodeBaseT & node_base)
|
||||
{
|
||||
bool use_intra_process;
|
||||
bool use_intra_process = false;
|
||||
switch (options.use_intra_process_comm) {
|
||||
case IntraProcessSetting::Enable:
|
||||
use_intra_process = true;
|
||||
@@ -43,7 +43,6 @@ resolve_use_intra_process(const OptionsT & options, const NodeBaseT & node_base)
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unrecognized IntraProcessSetting value");
|
||||
break;
|
||||
}
|
||||
|
||||
return use_intra_process;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP__EVENT_HANDLER_HPP_
|
||||
#define RCLCPP__EVENT_HANDLER_HPP_
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
@@ -110,6 +111,14 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~EventHandlerBase();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void enable() = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void disable() = 0;
|
||||
|
||||
/// Get the number of ready events
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
@@ -192,7 +201,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(on_new_event_callback_mutex_);
|
||||
|
||||
// Set it temporarily to the new callback, while we replace the old one.
|
||||
// This two-step setting, prevents a gap where the old std::function has
|
||||
@@ -215,7 +224,7 @@ public:
|
||||
void
|
||||
clear_on_ready_callback() override
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(on_new_event_callback_mutex_);
|
||||
if (on_new_event_callback_) {
|
||||
set_on_new_event_callback(nullptr, nullptr);
|
||||
on_new_event_callback_ = nullptr;
|
||||
@@ -234,7 +243,7 @@ protected:
|
||||
void
|
||||
set_on_new_event_callback(rcl_event_callback_t callback, const void * user_data);
|
||||
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::recursive_mutex on_new_event_callback_mutex_;
|
||||
std::function<void(size_t)> on_new_event_callback_{nullptr};
|
||||
|
||||
rcl_event_t event_handle_;
|
||||
@@ -302,6 +311,10 @@ public:
|
||||
void
|
||||
execute(const std::shared_ptr<void> & data) override
|
||||
{
|
||||
std::unique_lock<std::mutex> event_callback_lock(event_callback_mutex_);
|
||||
if (disabled_.load()) {
|
||||
return;
|
||||
}
|
||||
if (!data) {
|
||||
throw std::runtime_error("'data' is empty");
|
||||
}
|
||||
@@ -310,12 +323,53 @@ public:
|
||||
callback_ptr.reset();
|
||||
}
|
||||
|
||||
/// Disable the event callback from being called when execute(..) invoked
|
||||
/**
|
||||
* This will also temporarily remove the on_new_event_callback from the underlying rmw layer,
|
||||
* so that it is not called from the middleware while disabled.
|
||||
*/
|
||||
void disable() override
|
||||
{
|
||||
{
|
||||
// Temporary remove the on_new_event_callback_ to prevent it from being called
|
||||
std::lock_guard<std::recursive_mutex> on_new_event_lock(on_new_event_callback_mutex_);
|
||||
if (on_new_event_callback_) {
|
||||
set_on_new_event_callback(nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
std::lock_guard<std::mutex> event_callback_lock(event_callback_mutex_);
|
||||
disabled_.store(true);
|
||||
}
|
||||
|
||||
/// Enable the event callback to be called when execute(..) invoked
|
||||
/**
|
||||
* This will also set back the on_new_event_callback to the underlying rmw layer, if it was
|
||||
* previously removed with disable().
|
||||
*/
|
||||
void enable() override
|
||||
{
|
||||
{
|
||||
// Set callback again if it was previously removed in disable()
|
||||
std::lock_guard<std::recursive_mutex> on_new_event_lock(on_new_event_callback_mutex_);
|
||||
if (on_new_event_callback_) {
|
||||
set_on_new_event_callback(
|
||||
rclcpp::detail::cpp_callback_trampoline<
|
||||
decltype(on_new_event_callback_), const void *, size_t>,
|
||||
static_cast<const void *>(&on_new_event_callback_));
|
||||
}
|
||||
}
|
||||
std::lock_guard<std::mutex> event_callback_lock(event_callback_mutex_);
|
||||
disabled_.store(false);
|
||||
}
|
||||
|
||||
private:
|
||||
using EventCallbackInfoT = typename std::remove_reference<typename
|
||||
rclcpp::function_traits::function_traits<EventCallbackT>::template argument_type<0>>::type;
|
||||
|
||||
ParentHandleT parent_handle_;
|
||||
EventCallbackT event_callback_;
|
||||
std::mutex event_callback_mutex_;
|
||||
std::atomic_bool disabled_{false};
|
||||
};
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -82,10 +82,9 @@ create_intra_process_buffer(
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
case IntraProcessBufferType::CallbackDefault:
|
||||
{
|
||||
throw std::runtime_error("Unrecognized IntraProcessBufferType value");
|
||||
break;
|
||||
throw std::runtime_error("IntraProcessBufferType::CallbackDefault is not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -154,6 +154,29 @@ public:
|
||||
execute_impl<SubscribedType>(data);
|
||||
}
|
||||
|
||||
/// Disable callbacks from being called
|
||||
/**
|
||||
* This method will block, until any subscription's callbacks currently being executed are
|
||||
* finished.
|
||||
* This method is thread safe, and provides a safe way to atomically disable the callbacks.
|
||||
*/
|
||||
void disable_callbacks() override
|
||||
{
|
||||
SubscriptionIntraProcessBase::disable_callbacks();
|
||||
any_callback_.disable();
|
||||
}
|
||||
|
||||
/// Enable the callbacks to be called
|
||||
/**
|
||||
* This method is thread safe, and provides a safe way to atomically enable the callbacks
|
||||
* in a multithreaded environment.
|
||||
*/
|
||||
void enable_callbacks() override
|
||||
{
|
||||
SubscriptionIntraProcessBase::enable_callbacks();
|
||||
any_callback_.enable();
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_same<T, rcl_serialized_message_t>::value, void>::type
|
||||
|
||||
@@ -87,6 +87,22 @@ public:
|
||||
void
|
||||
execute(const std::shared_ptr<void> & data) override = 0;
|
||||
|
||||
/// Disable callbacks from being called
|
||||
/**
|
||||
* This function temporary disable on_new_message_callback to prevent it from being called.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void disable_callbacks();
|
||||
|
||||
/// Enable the callbacks to be called
|
||||
/**
|
||||
* This function enable the on_new_message_callback if it was previously set.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void enable_callbacks();
|
||||
|
||||
virtual
|
||||
bool
|
||||
use_take_shared_method() const = 0;
|
||||
@@ -158,7 +174,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
|
||||
on_new_message_callback_ = new_callback;
|
||||
|
||||
if (unread_count_ > 0) {
|
||||
@@ -176,7 +192,7 @@ public:
|
||||
void
|
||||
clear_on_ready_callback() override
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
|
||||
on_new_message_callback_ = nullptr;
|
||||
}
|
||||
|
||||
@@ -188,8 +204,9 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::recursive_mutex on_new_message_callback_mutex_;
|
||||
std::function<void(size_t)> on_new_message_callback_ {nullptr};
|
||||
bool on_new_message_callback_disabled_{false};
|
||||
size_t unread_count_{0};
|
||||
rclcpp::GuardCondition gc_;
|
||||
|
||||
@@ -199,11 +216,13 @@ protected:
|
||||
void
|
||||
invoke_on_new_message()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(this->callback_mutex_);
|
||||
if (this->on_new_message_callback_) {
|
||||
this->on_new_message_callback_(1);
|
||||
} else {
|
||||
this->unread_count_++;
|
||||
std::lock_guard<std::recursive_mutex> lock(this->on_new_message_callback_mutex_);
|
||||
if (!on_new_message_callback_disabled_) {
|
||||
if (this->on_new_message_callback_) {
|
||||
this->on_new_message_callback_(1);
|
||||
} else {
|
||||
this->unread_count_++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,26 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rclcpp::SerializedMessage> create_serialized_message() override;
|
||||
|
||||
/// Disable callbacks from being called
|
||||
/**
|
||||
* This method will block, until any subscription's callbacks provided during construction
|
||||
* currently being executed are finished.
|
||||
* \note This method also temporary removes the on new message callback and all
|
||||
* on new event callbacks from the rmw layer to prevent them from being called. However, this
|
||||
* method will not block and wait until the currently executing on_new_[message]event callbacks
|
||||
* are finished.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void disable_callbacks() override;
|
||||
|
||||
/// Enable the callbacks to be called
|
||||
/**
|
||||
* This method is thread safe, and provides a safe way to atomically enable the callbacks
|
||||
* in a multithreaded environment.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void enable_callbacks() override;
|
||||
|
||||
/// Cast the message to a rclcpp::SerializedMessage and call the callback.
|
||||
RCLCPP_PUBLIC
|
||||
void handle_message(
|
||||
|
||||
@@ -1388,6 +1388,66 @@ public:
|
||||
std::vector<rclcpp::TopicEndpointInfo>
|
||||
get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle = false) const;
|
||||
|
||||
/// Return the service endpoint information about clients on a given service.
|
||||
/**
|
||||
* The returned parameter is a list of service endpoint information, where each item will contain
|
||||
* the node name, node namespace, service type, endpoint type, endpoint count,
|
||||
* service endpoint's GIDs, and its QoS profiles.
|
||||
*
|
||||
* When the `no_mangle` parameter is `true`, the provided `service_name` should be a valid service
|
||||
* name for the middleware (useful when combining ROS with native middleware apps).
|
||||
* When the `no_mangle` parameter is `false`, the provided `service_name` should follow
|
||||
* ROS service name conventions. In DDS-based RMWs, services are implemented as topics with mangled
|
||||
* names (e.g., `rq/my_serviceRequest` and `rp/my_serviceReply`), so `no_mangle = true` is not
|
||||
* supported and will result in an error. Use `get_publishers_info_by_topic` or
|
||||
* `get_subscriptions_info_by_topic` for unmangled topic queries in such cases. Other RMWs
|
||||
* (e.g., Zenoh) may support `no_mangle = true` if they natively handle services without topic-based
|
||||
*
|
||||
* 'service_name` may be a relative, private, or fully qualified service name.
|
||||
* A relative or private service will be expanded using this node's namespace and name.
|
||||
* The queried `service_name` is not remapped.
|
||||
*
|
||||
* \param[in] service_name the actual service name used; it will not be automatically remapped.
|
||||
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
|
||||
* otherwise it should be a valid ROS service name. Defaults to `false`.
|
||||
* \return a list of SeviceEndpointInfo representing all the clients on this service.
|
||||
* \throws InvalidServiceNameError if the given service_name is invalid.
|
||||
* \throws std::runtime_error if internal error happens.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
get_clients_info_by_service(const std::string & service_name, bool no_mangle = false) const;
|
||||
|
||||
/// Return the service endpoint information about servers on a given service.
|
||||
/**
|
||||
* The returned parameter is a list of service endpoint information, where each item will contain
|
||||
* the node name, node namespace, service type, endpoint type, endpoint count,
|
||||
* service endpoint's GIDs, and its QoS profiles.
|
||||
*
|
||||
* When the `no_mangle` parameter is `true`, the provided `service_name` should be a valid service
|
||||
* name for the middleware (useful when combining ROS with native middleware apps).
|
||||
* When the `no_mangle` parameter is `false`, the provided `service_name` should follow
|
||||
* ROS service name conventions. In DDS-based RMWs, services are implemented as topics with mangled
|
||||
* names (e.g., `rq/my_serviceRequest` and `rp/my_serviceReply`), so `no_mangle = true` is not
|
||||
* supported and will result in an error. Use `rcl_get_publishers_info_by_topic` or
|
||||
* `rcl_get_subscriptions_info_by_topic` for unmangled topic queries in such cases. Other RMWs
|
||||
* (e.g., Zenoh) may support `no_mangle = true` if they natively handle services without topic-based
|
||||
*
|
||||
* 'service_name` may be a relative, private, or fully qualified service name.
|
||||
* A relative or private service will be expanded using this node's namespace and name.
|
||||
* The queried `service_name` is not remapped.
|
||||
*
|
||||
* \param[in] service_name the actual service name used; it will not be automatically remapped.
|
||||
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
|
||||
* otherwise it should be a valid ROS service name. Defaults to `false`.
|
||||
* \return a list of SeviceEndpointInfo representing all the servers on this service.
|
||||
* \throws InvalidServiceNameError if the given service_name is invalid.
|
||||
* \throws std::runtime_error if internal error happens.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
get_servers_info_by_service(const std::string & service_name, bool no_mangle = false) const;
|
||||
|
||||
/// Return a graph event, which will be set anytime a graph change occurs.
|
||||
/* The graph Event object is a loan which must be returned.
|
||||
* The Event object is scoped and therefore to return the loan just let it go
|
||||
|
||||
@@ -199,6 +199,12 @@ init_tuple(NodeT & n)
|
||||
{ \
|
||||
return StorageClassT::template get<NodeInterfaceType>(); \
|
||||
} \
|
||||
\
|
||||
std::shared_ptr<const NodeInterfaceType> \
|
||||
get_node_ ## NodeInterfaceName ## _interface() const \
|
||||
{ \
|
||||
return StorageClassT::template get<NodeInterfaceType>(); \
|
||||
} \
|
||||
}; \
|
||||
} // namespace rclcpp::node_interfaces::detail
|
||||
// *INDENT-ON*
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/service_endpoint_info_array.h"
|
||||
#include "rmw/topic_endpoint_info_array.h"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -159,6 +160,18 @@ public:
|
||||
const std::string & topic_name,
|
||||
bool no_mangle = false) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
get_clients_info_by_service(
|
||||
const std::string & service_name,
|
||||
bool no_mangle = false) const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
get_servers_info_by_service(
|
||||
const std::string & service_name,
|
||||
bool no_mangle = false) const override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeGraph)
|
||||
|
||||
|
||||
@@ -18,6 +18,8 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
@@ -40,7 +42,9 @@ enum class EndpointType
|
||||
{
|
||||
Invalid = RMW_ENDPOINT_INVALID,
|
||||
Publisher = RMW_ENDPOINT_PUBLISHER,
|
||||
Subscription = RMW_ENDPOINT_SUBSCRIPTION
|
||||
Subscription = RMW_ENDPOINT_SUBSCRIPTION,
|
||||
Client = RMW_ENDPOINT_CLIENT,
|
||||
Server = RMW_ENDPOINT_SERVER
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -53,13 +57,17 @@ public:
|
||||
/// Construct a TopicEndpointInfo from a rcl_topic_endpoint_info_t.
|
||||
RCLCPP_PUBLIC
|
||||
explicit TopicEndpointInfo(const rcl_topic_endpoint_info_t & info)
|
||||
: node_name_(info.node_name),
|
||||
node_namespace_(info.node_namespace),
|
||||
topic_type_(info.topic_type),
|
||||
endpoint_type_(static_cast<rclcpp::EndpointType>(info.endpoint_type)),
|
||||
: endpoint_type_(static_cast<rclcpp::EndpointType>(info.endpoint_type)),
|
||||
qos_profile_({info.qos_profile.history, info.qos_profile.depth}, info.qos_profile),
|
||||
topic_type_hash_(info.topic_type_hash)
|
||||
{
|
||||
if (!info.node_name || !info.node_namespace || !info.topic_type) {
|
||||
throw std::invalid_argument("Constructor TopicEndpointInfo with invalid topic endpoint info");
|
||||
}
|
||||
node_name_ = info.node_name;
|
||||
node_namespace_ = info.node_namespace;
|
||||
topic_type_ = info.topic_type;
|
||||
|
||||
std::copy(info.endpoint_gid, info.endpoint_gid + RMW_GID_STORAGE_SIZE, endpoint_gid_.begin());
|
||||
}
|
||||
|
||||
@@ -143,6 +151,125 @@ private:
|
||||
rosidl_type_hash_t topic_type_hash_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Struct that contains service endpoint information like the associated node name, node namespace,
|
||||
* service type, endpoint type, endpoint count, endpoint GIDs, and its QoS profiles.
|
||||
*/
|
||||
class ServiceEndpointInfo
|
||||
{
|
||||
public:
|
||||
/// Construct a ServiceEndpointInfo from a rcl_service_endpoint_info_t.
|
||||
RCLCPP_PUBLIC
|
||||
explicit ServiceEndpointInfo(const rcl_service_endpoint_info_t & info)
|
||||
: node_name_(info.node_name),
|
||||
node_namespace_(info.node_namespace),
|
||||
service_type_(info.service_type),
|
||||
endpoint_type_(static_cast<rclcpp::EndpointType>(info.endpoint_type)),
|
||||
service_type_hash_(info.service_type_hash),
|
||||
endpoint_count_(info.endpoint_count)
|
||||
{
|
||||
for(size_t i = 0; i < endpoint_count_; i++) {
|
||||
std::array<uint8_t, RMW_GID_STORAGE_SIZE> gid;
|
||||
std::copy(info.endpoint_gids[i], info.endpoint_gids[i] + RMW_GID_STORAGE_SIZE, gid.begin());
|
||||
endpoint_gids_.push_back(gid);
|
||||
|
||||
rclcpp::QoS qos(
|
||||
{info.qos_profiles[i].history, info.qos_profiles[i].depth}, info.qos_profiles[i]);
|
||||
qos_profiles_.push_back(qos);
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a mutable reference to the node name.
|
||||
RCLCPP_PUBLIC
|
||||
std::string &
|
||||
node_name();
|
||||
|
||||
/// Get a const reference to the node name.
|
||||
RCLCPP_PUBLIC
|
||||
const std::string &
|
||||
node_name() const;
|
||||
|
||||
/// Get a mutable reference to the node namespace.
|
||||
RCLCPP_PUBLIC
|
||||
std::string &
|
||||
node_namespace();
|
||||
|
||||
/// Get a const reference to the node namespace.
|
||||
RCLCPP_PUBLIC
|
||||
const std::string &
|
||||
node_namespace() const;
|
||||
|
||||
/// Get a mutable reference to the service type string.
|
||||
RCLCPP_PUBLIC
|
||||
std::string &
|
||||
service_type();
|
||||
|
||||
/// Get a const reference to the service type string.
|
||||
RCLCPP_PUBLIC
|
||||
const std::string &
|
||||
service_type() const;
|
||||
|
||||
/// Get a mutable reference to the service endpoint type.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::EndpointType &
|
||||
endpoint_type();
|
||||
|
||||
/// Get a const reference to the service endpoint type.
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::EndpointType &
|
||||
endpoint_type() const;
|
||||
|
||||
/// Get a mutable reference to the endpoint count.
|
||||
RCLCPP_PUBLIC
|
||||
size_t &
|
||||
endpoint_count();
|
||||
|
||||
/// Get a const reference to the endpoint count.
|
||||
RCLCPP_PUBLIC
|
||||
const size_t &
|
||||
endpoint_count() const;
|
||||
|
||||
/// Get a mutable reference to the GID of the service endpoint.
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
|
||||
endpoint_gids();
|
||||
|
||||
/// Get a const reference to the GID of the service endpoint.
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
|
||||
endpoint_gids() const;
|
||||
|
||||
/// Get a mutable reference to the QoS profile of the service endpoint.
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::QoS> &
|
||||
qos_profiles();
|
||||
|
||||
/// Get a const reference to the QoS profile of the service endpoint.
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<rclcpp::QoS> &
|
||||
qos_profiles() const;
|
||||
|
||||
/// Get a mutable reference to the type hash of the service endpoint.
|
||||
RCLCPP_PUBLIC
|
||||
rosidl_type_hash_t &
|
||||
service_type_hash();
|
||||
|
||||
/// Get a const reference to the type hash of the service endpoint.
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_type_hash_t &
|
||||
service_type_hash() const;
|
||||
|
||||
private:
|
||||
std::string node_name_;
|
||||
std::string node_namespace_;
|
||||
std::string service_type_;
|
||||
rclcpp::EndpointType endpoint_type_;
|
||||
std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> endpoint_gids_;
|
||||
std::vector<rclcpp::QoS> qos_profiles_;
|
||||
rosidl_type_hash_t service_type_hash_;
|
||||
size_t endpoint_count_;
|
||||
};
|
||||
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
@@ -408,6 +535,30 @@ public:
|
||||
virtual
|
||||
std::vector<rclcpp::TopicEndpointInfo>
|
||||
get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle = false) const = 0;
|
||||
|
||||
/// Return the service endpoint information about clients on a given service.
|
||||
/**
|
||||
* \param[in] service_name the actual service name used; it will not be automatically remapped.
|
||||
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
|
||||
* otherwise it should be a valid ROS service name.
|
||||
* \sa rclcpp::Node::get_clients_info_by_service
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
get_clients_info_by_service(const std::string & service_name, bool no_mangle = false) const = 0;
|
||||
|
||||
/// Return the service endpoint information about servers on a given service.
|
||||
/**
|
||||
* \param[in] service_name the actual service name used; it will not be automatically remapped.
|
||||
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
|
||||
* otherwise it should be a valid ROS service name.
|
||||
* \sa rclcpp::Node::get_servers_info_by_service
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
get_servers_info_by_service(const std::string & service_name, bool no_mangle = false) const = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
82
rclcpp/include/rclcpp/parameter_descriptor_wrapper.hpp
Normal file
82
rclcpp/include/rclcpp/parameter_descriptor_wrapper.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2023 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__PARAMETER_DESCRIPTOR_WRAPPER_HPP_
|
||||
#define RCLCPP__PARAMETER_DESCRIPTOR_WRAPPER_HPP_
|
||||
|
||||
// C++ Standard library includes
|
||||
#include <functional>
|
||||
#include <utility>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// Additional ROS libraries needed
|
||||
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
|
||||
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/parameter_value.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_parameters_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "node_interfaces/node_parameters_interface.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
// Implements ParameterDesription class with builder design pattern
|
||||
class ParameterDescription
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ParameterDescription)
|
||||
|
||||
// List of classes the builder manages
|
||||
RCLCPP_PUBLIC
|
||||
ParameterDescription();
|
||||
|
||||
// Our Main build methods which will construct the base class
|
||||
RCLCPP_PUBLIC rcl_interfaces::msg::ParameterDescriptor build() const;
|
||||
|
||||
// Builder Methods:
|
||||
// Describes the instances in a parameter_description object
|
||||
RCLCPP_PUBLIC ParameterDescription & set_name(const std::string & name);
|
||||
RCLCPP_PUBLIC ParameterDescription & set_type(std::uint8_t type);
|
||||
RCLCPP_PUBLIC ParameterDescription & set_description_text(const std::string & description);
|
||||
RCLCPP_PUBLIC ParameterDescription & set_additional_constraints(const std::string & constraints);
|
||||
RCLCPP_PUBLIC ParameterDescription & set_read_only(bool read_only);
|
||||
RCLCPP_PUBLIC ParameterDescription & set_dynamic_typing(bool dynamic_typing);
|
||||
RCLCPP_PUBLIC ParameterDescription & set_floating_point_description_range(
|
||||
float min, float max, float step);
|
||||
RCLCPP_PUBLIC ParameterDescription & set_integer_description_range(int min, int max, int step);
|
||||
|
||||
// Need the current node in order to begin the configuration state
|
||||
// for it via the declare_parameter function
|
||||
template<typename NodeT>
|
||||
ParameterDescription & declare_parameter(
|
||||
const rclcpp::ParameterValue & default_value,
|
||||
NodeT && node)
|
||||
{
|
||||
auto node_param = rclcpp::node_interfaces::get_node_parameters_interface(node);
|
||||
node_param->declare_parameter(
|
||||
parameter_descriptor.name, default_value,
|
||||
parameter_descriptor);
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
// The main descriptor object we're meant to initialize and adjust
|
||||
rcl_interfaces::msg::ParameterDescriptor parameter_descriptor;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__PARAMETER_DESCRIPTOR_WRAPPER_HPP_
|
||||
@@ -234,6 +234,10 @@ public:
|
||||
/**
|
||||
* If a node_name is not provided, defaults to the current node.
|
||||
*
|
||||
* The configure_nodes_filter() function will affect the behavior of this function.
|
||||
* If the node specified in this function isn't included in the nodes specified in
|
||||
* configure_nodes_filter(), the callback will never be called.
|
||||
*
|
||||
* Note: if the returned callback handle smart pointer is not captured, the callback
|
||||
* is immediately unregistered. A compiler warning should be generated to warn
|
||||
* of this.
|
||||
@@ -251,6 +255,31 @@ public:
|
||||
ParameterCallbackType callback,
|
||||
const std::string & node_name = "");
|
||||
|
||||
/// Configure which node parameter events will be received.
|
||||
/**
|
||||
* This function depends on rmw implementation support for content filtering.
|
||||
* If middleware doesn't support contentfilter, return false.
|
||||
*
|
||||
* If node_names is empty, the configured node filter will be cleared.
|
||||
*
|
||||
* If this function return true, only parameter events from the specified node will be received.
|
||||
* It affects the behavior of the following two functions.
|
||||
* - add_parameter_event_callback()
|
||||
* The callback will only be called for parameter events from the specified nodes which are
|
||||
* configured in this function.
|
||||
* - add_parameter_callback()
|
||||
* The callback will only be called for parameter events from the specified nodes which are
|
||||
* configured in this function and add_parameter_callback().
|
||||
* If the nodes specified in this function is different from the nodes specified in
|
||||
* add_parameter_callback(), the callback will never be called.
|
||||
*
|
||||
* \param[in] node_names Node names to filter parameter events from.
|
||||
* \returns true if configuring was successfully applied, false otherwise.
|
||||
* \throws rclcpp::exceptions::RCLError if internal error occurred when calling the rcl function.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool configure_nodes_filter(const std::vector<std::string> & node_names);
|
||||
|
||||
/// Remove a parameter callback registered with add_parameter_callback.
|
||||
/**
|
||||
* The parameter name and node name are inspected from the callback handle. The callback handle
|
||||
|
||||
@@ -175,6 +175,7 @@
|
||||
#include "rclcpp/parameter_event_handler.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/parameter_service.hpp"
|
||||
#include "rclcpp/parameter_descriptor_wrapper.hpp"
|
||||
#include "rclcpp/rate.hpp"
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
@@ -99,49 +99,92 @@ public:
|
||||
// Important to use subscription_handles_.size() instead of wait set's size since
|
||||
// there may be more subscriptions in the wait set due to Waitables added to the end.
|
||||
// The same logic applies for other entities.
|
||||
|
||||
// Mark corresponding weak_ptr as expired for entities that are null in the wait set
|
||||
size_t valid_subscription_count = 0;
|
||||
for (size_t i = 0; i < subscription_handles_.size(); ++i) {
|
||||
if (!wait_set->subscriptions[i]) {
|
||||
subscription_handles_[i].reset();
|
||||
if (valid_subscription_count < wait_set->size_of_subscriptions &&
|
||||
!wait_set->subscriptions[valid_subscription_count])
|
||||
{
|
||||
subscription_handles_[i] = std::weak_ptr<const rcl_subscription_t>{};
|
||||
}
|
||||
if (subscription_handles_[i].lock()) {
|
||||
++valid_subscription_count;
|
||||
}
|
||||
}
|
||||
|
||||
size_t valid_service_count = 0;
|
||||
for (size_t i = 0; i < service_handles_.size(); ++i) {
|
||||
if (!wait_set->services[i]) {
|
||||
service_handles_[i].reset();
|
||||
if (valid_service_count < wait_set->size_of_services &&
|
||||
!wait_set->services[valid_service_count])
|
||||
{
|
||||
service_handles_[i] = std::weak_ptr<const rcl_service_t>{};
|
||||
}
|
||||
if (service_handles_[i].lock()) {
|
||||
++valid_service_count;
|
||||
}
|
||||
}
|
||||
|
||||
size_t valid_client_count = 0;
|
||||
for (size_t i = 0; i < client_handles_.size(); ++i) {
|
||||
if (!wait_set->clients[i]) {
|
||||
client_handles_[i].reset();
|
||||
if (valid_client_count < wait_set->size_of_clients &&
|
||||
!wait_set->clients[valid_client_count])
|
||||
{
|
||||
client_handles_[i] = std::weak_ptr<const rcl_client_t>{};
|
||||
}
|
||||
if (client_handles_[i].lock()) {
|
||||
++valid_client_count;
|
||||
}
|
||||
}
|
||||
|
||||
size_t valid_timer_count = 0;
|
||||
for (size_t i = 0; i < timer_handles_.size(); ++i) {
|
||||
if (!wait_set->timers[i]) {
|
||||
timer_handles_[i].reset();
|
||||
if (valid_timer_count < wait_set->size_of_timers &&
|
||||
!wait_set->timers[valid_timer_count])
|
||||
{
|
||||
timer_handles_[i] = std::weak_ptr<const rcl_timer_t>{};
|
||||
}
|
||||
if (timer_handles_[i].lock()) {
|
||||
++valid_timer_count;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < waitable_handles_.size(); ++i) {
|
||||
if (!waitable_handles_[i]->is_ready(*wait_set)) {
|
||||
waitable_handles_[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
// Remove expired weak_ptr instances
|
||||
subscription_handles_.erase(
|
||||
std::remove(subscription_handles_.begin(), subscription_handles_.end(), nullptr),
|
||||
std::remove_if(subscription_handles_.begin(), subscription_handles_.end(),
|
||||
[](const std::weak_ptr<const rcl_subscription_t> & weak_ptr) {
|
||||
return weak_ptr.expired();
|
||||
}),
|
||||
subscription_handles_.end()
|
||||
);
|
||||
|
||||
service_handles_.erase(
|
||||
std::remove(service_handles_.begin(), service_handles_.end(), nullptr),
|
||||
std::remove_if(service_handles_.begin(), service_handles_.end(),
|
||||
[](const std::weak_ptr<const rcl_service_t> & weak_ptr) {
|
||||
return weak_ptr.expired();
|
||||
}),
|
||||
service_handles_.end()
|
||||
);
|
||||
|
||||
client_handles_.erase(
|
||||
std::remove(client_handles_.begin(), client_handles_.end(), nullptr),
|
||||
std::remove_if(client_handles_.begin(), client_handles_.end(),
|
||||
[](const std::weak_ptr<const rcl_client_t> & weak_ptr) {
|
||||
return weak_ptr.expired();
|
||||
}),
|
||||
client_handles_.end()
|
||||
);
|
||||
|
||||
timer_handles_.erase(
|
||||
std::remove(timer_handles_.begin(), timer_handles_.end(), nullptr),
|
||||
std::remove_if(timer_handles_.begin(), timer_handles_.end(),
|
||||
[](const std::weak_ptr<const rcl_timer_t> & weak_ptr) {
|
||||
return weak_ptr.expired();
|
||||
}),
|
||||
timer_handles_.end()
|
||||
);
|
||||
|
||||
@@ -196,7 +239,13 @@ public:
|
||||
|
||||
bool add_handles_to_wait_set(rcl_wait_set_t * wait_set) override
|
||||
{
|
||||
for (const std::shared_ptr<const rcl_subscription_t> & subscription : subscription_handles_) {
|
||||
for (const std::weak_ptr<const rcl_subscription_t> & weak_subscription :
|
||||
subscription_handles_)
|
||||
{
|
||||
auto subscription = weak_subscription.lock();
|
||||
if (!subscription) {
|
||||
continue; // Skip expired handles
|
||||
}
|
||||
if (rcl_wait_set_add_subscription(wait_set, subscription.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
@@ -206,7 +255,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::shared_ptr<const rcl_client_t> & client : client_handles_) {
|
||||
for (const std::weak_ptr<const rcl_client_t> & weak_client : client_handles_) {
|
||||
auto client = weak_client.lock();
|
||||
if (!client) {
|
||||
continue; // Skip expired handles
|
||||
}
|
||||
if (rcl_wait_set_add_client(wait_set, client.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
@@ -216,7 +269,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::shared_ptr<const rcl_service_t> & service : service_handles_) {
|
||||
for (const std::weak_ptr<const rcl_service_t> & weak_service : service_handles_) {
|
||||
auto service = weak_service.lock();
|
||||
if (!service) {
|
||||
continue; // Skip expired handles
|
||||
}
|
||||
if (rcl_wait_set_add_service(wait_set, service.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
@@ -226,7 +283,11 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
for (const std::shared_ptr<const rcl_timer_t> & timer : timer_handles_) {
|
||||
for (const std::weak_ptr<const rcl_timer_t> & weak_timer : timer_handles_) {
|
||||
auto timer = weak_timer.lock();
|
||||
if (!timer) {
|
||||
continue; // Skip expired handles
|
||||
}
|
||||
if (rcl_wait_set_add_timer(wait_set, timer.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
@@ -253,7 +314,13 @@ public:
|
||||
{
|
||||
auto it = subscription_handles_.begin();
|
||||
while (it != subscription_handles_.end()) {
|
||||
auto subscription = get_subscription_by_handle(*it, weak_groups_to_nodes);
|
||||
auto subscription_handle = it->lock();
|
||||
if (!subscription_handle) {
|
||||
// Handle expired, remove it and continue
|
||||
it = subscription_handles_.erase(it);
|
||||
continue;
|
||||
}
|
||||
auto subscription = get_subscription_by_handle(subscription_handle, weak_groups_to_nodes);
|
||||
if (subscription) {
|
||||
// Find the group for this handle and see if it can be serviced
|
||||
auto group = get_group_by_subscription(subscription, weak_groups_to_nodes);
|
||||
@@ -288,7 +355,13 @@ public:
|
||||
{
|
||||
auto it = service_handles_.begin();
|
||||
while (it != service_handles_.end()) {
|
||||
auto service = get_service_by_handle(*it, weak_groups_to_nodes);
|
||||
auto service_handle = it->lock();
|
||||
if (!service_handle) {
|
||||
// Handle expired, remove it and continue
|
||||
it = service_handles_.erase(it);
|
||||
continue;
|
||||
}
|
||||
auto service = get_service_by_handle(service_handle, weak_groups_to_nodes);
|
||||
if (service) {
|
||||
// Find the group for this handle and see if it can be serviced
|
||||
auto group = get_group_by_service(service, weak_groups_to_nodes);
|
||||
@@ -323,7 +396,13 @@ public:
|
||||
{
|
||||
auto it = client_handles_.begin();
|
||||
while (it != client_handles_.end()) {
|
||||
auto client = get_client_by_handle(*it, weak_groups_to_nodes);
|
||||
auto client_handle = it->lock();
|
||||
if (!client_handle) {
|
||||
// Handle expired, remove it and continue
|
||||
it = client_handles_.erase(it);
|
||||
continue;
|
||||
}
|
||||
auto client = get_client_by_handle(client_handle, weak_groups_to_nodes);
|
||||
if (client) {
|
||||
// Find the group for this handle and see if it can be serviced
|
||||
auto group = get_group_by_client(client, weak_groups_to_nodes);
|
||||
@@ -358,7 +437,13 @@ public:
|
||||
{
|
||||
auto it = timer_handles_.begin();
|
||||
while (it != timer_handles_.end()) {
|
||||
auto timer = get_timer_by_handle(*it, weak_groups_to_nodes);
|
||||
auto timer_handle = it->lock();
|
||||
if (!timer_handle) {
|
||||
// Handle expired, remove it and continue
|
||||
it = timer_handles_.erase(it);
|
||||
continue;
|
||||
}
|
||||
auto timer = get_timer_by_handle(timer_handle, weak_groups_to_nodes);
|
||||
if (timer) {
|
||||
// Find the group for this handle and see if it can be serviced
|
||||
auto group = get_group_by_timer(timer, weak_groups_to_nodes);
|
||||
@@ -435,7 +520,13 @@ public:
|
||||
|
||||
size_t number_of_ready_subscriptions() const override
|
||||
{
|
||||
size_t number_of_subscriptions = subscription_handles_.size();
|
||||
size_t number_of_subscriptions = 0;
|
||||
// Count only non-expired weak_ptr references
|
||||
for (const auto & weak_subscription : subscription_handles_) {
|
||||
if (!weak_subscription.expired()) {
|
||||
++number_of_subscriptions;
|
||||
}
|
||||
}
|
||||
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
|
||||
number_of_subscriptions += waitable->get_number_of_ready_subscriptions();
|
||||
}
|
||||
@@ -444,7 +535,13 @@ public:
|
||||
|
||||
size_t number_of_ready_services() const override
|
||||
{
|
||||
size_t number_of_services = service_handles_.size();
|
||||
size_t number_of_services = 0;
|
||||
// Count only non-expired weak_ptr references
|
||||
for (const auto & weak_service : service_handles_) {
|
||||
if (!weak_service.expired()) {
|
||||
++number_of_services;
|
||||
}
|
||||
}
|
||||
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
|
||||
number_of_services += waitable->get_number_of_ready_services();
|
||||
}
|
||||
@@ -462,7 +559,13 @@ public:
|
||||
|
||||
size_t number_of_ready_clients() const override
|
||||
{
|
||||
size_t number_of_clients = client_handles_.size();
|
||||
size_t number_of_clients = 0;
|
||||
// Count only non-expired weak_ptr references
|
||||
for (const auto & weak_client : client_handles_) {
|
||||
if (!weak_client.expired()) {
|
||||
++number_of_clients;
|
||||
}
|
||||
}
|
||||
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
|
||||
number_of_clients += waitable->get_number_of_ready_clients();
|
||||
}
|
||||
@@ -480,7 +583,13 @@ public:
|
||||
|
||||
size_t number_of_ready_timers() const override
|
||||
{
|
||||
size_t number_of_timers = timer_handles_.size();
|
||||
size_t number_of_timers = 0;
|
||||
// Count only non-expired weak_ptr references
|
||||
for (const auto & weak_timer : timer_handles_) {
|
||||
if (!weak_timer.expired()) {
|
||||
++number_of_timers;
|
||||
}
|
||||
}
|
||||
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
|
||||
number_of_timers += waitable->get_number_of_ready_timers();
|
||||
}
|
||||
@@ -499,10 +608,10 @@ private:
|
||||
|
||||
VectorRebind<const rclcpp::GuardCondition *> guard_conditions_;
|
||||
|
||||
VectorRebind<std::shared_ptr<const rcl_subscription_t>> subscription_handles_;
|
||||
VectorRebind<std::shared_ptr<const rcl_service_t>> service_handles_;
|
||||
VectorRebind<std::shared_ptr<const rcl_client_t>> client_handles_;
|
||||
VectorRebind<std::shared_ptr<const rcl_timer_t>> timer_handles_;
|
||||
VectorRebind<std::weak_ptr<const rcl_subscription_t>> subscription_handles_;
|
||||
VectorRebind<std::weak_ptr<const rcl_service_t>> service_handles_;
|
||||
VectorRebind<std::weak_ptr<const rcl_client_t>> client_handles_;
|
||||
VectorRebind<std::weak_ptr<const rcl_timer_t>> timer_handles_;
|
||||
VectorRebind<std::shared_ptr<Waitable>> waitable_handles_;
|
||||
|
||||
std::shared_ptr<VoidAlloc> allocator_;
|
||||
|
||||
@@ -279,6 +279,50 @@ public:
|
||||
return message_memory_strategy_->borrow_serialized_message();
|
||||
}
|
||||
|
||||
/// Disable callbacks from being called
|
||||
/**
|
||||
* This method will block, until any subscription's callbacks provided during construction
|
||||
* currently being executed are finished.
|
||||
* \note This method also temporary removes the on new message callback and all
|
||||
* on new event callbacks from the rmw layer to prevent them from being called. However, this
|
||||
* method will not block and wait until the currently executing on_new_[message]event callbacks
|
||||
* are finished.
|
||||
*/
|
||||
void
|
||||
disable_callbacks() override
|
||||
{
|
||||
SubscriptionBase::disable_callbacks();
|
||||
any_callback_.disable();
|
||||
if (subscription_intra_process_) {
|
||||
subscription_intra_process_->disable_callbacks();
|
||||
}
|
||||
for (const auto & [_, event_ptr] : event_handlers_) {
|
||||
if (event_ptr) {
|
||||
event_ptr->disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Enable the callbacks to be called
|
||||
/**
|
||||
* This method is thread safe, and provides a safe way to atomically enable the callbacks
|
||||
* in a multithreaded environment.
|
||||
*/
|
||||
void
|
||||
enable_callbacks() override
|
||||
{
|
||||
SubscriptionBase::enable_callbacks();
|
||||
any_callback_.enable();
|
||||
if (subscription_intra_process_) {
|
||||
subscription_intra_process_->enable_callbacks();
|
||||
}
|
||||
for (const auto & [_, event_ptr] : event_handlers_) {
|
||||
if (event_ptr) {
|
||||
event_ptr->enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handle_message(
|
||||
std::shared_ptr<void> & message,
|
||||
|
||||
@@ -212,6 +212,23 @@ public:
|
||||
std::shared_ptr<rclcpp::SerializedMessage>
|
||||
create_serialized_message() = 0;
|
||||
|
||||
/// Disable callbacks from being called
|
||||
/**
|
||||
* This function temporary removes the on_new_message_callback to prevent it from being called.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void disable_callbacks();
|
||||
|
||||
/// Enable the callbacks to be called
|
||||
/**
|
||||
* This function sets back the on_new_message_callback if it was previously removed in
|
||||
* disable_callbacks().
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void enable_callbacks();
|
||||
|
||||
/// Check if we need to handle the message, and execute the callback if we do.
|
||||
/**
|
||||
* \param[in] message Shared pointer to the message to handle.
|
||||
@@ -383,7 +400,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
|
||||
|
||||
// Set it temporarily to the new callback, while we replace the old one.
|
||||
// This two-step setting, prevents a gap where the old std::function has
|
||||
@@ -406,7 +423,7 @@ public:
|
||||
void
|
||||
clear_on_new_message_callback()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
|
||||
|
||||
if (on_new_message_callback_) {
|
||||
set_on_new_message_callback(nullptr, nullptr);
|
||||
@@ -646,7 +663,7 @@ protected:
|
||||
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::recursive_mutex on_new_message_callback_mutex_;
|
||||
// It is important to declare on_new_message_callback_ before
|
||||
// subscription_handle_, so on destruction the subscription is
|
||||
// destroyed first. Otherwise, the rmw subscription callback
|
||||
|
||||
@@ -56,7 +56,7 @@ bool wait_for_message(
|
||||
}
|
||||
});
|
||||
|
||||
rclcpp::WaitSet wait_set;
|
||||
rclcpp::WaitSet wait_set({}, {}, {}, {}, {}, {}, context);
|
||||
wait_set.add_subscription(subscription);
|
||||
RCPPUTILS_SCOPE_EXIT(wait_set.remove_subscription(subscription); );
|
||||
wait_set.add_guard_condition(gc);
|
||||
|
||||
@@ -704,10 +704,12 @@ public:
|
||||
return WaitResult<WaitSetTemplate>::from_timeout_wait_result_kind();
|
||||
case WaitResultKind::Empty:
|
||||
return WaitResult<WaitSetTemplate>::from_empty_wait_result_kind();
|
||||
default:
|
||||
auto msg = "unknown WaitResultKind with value: " + std::to_string(wait_result_kind);
|
||||
case WaitResultKind::Invalid:
|
||||
auto msg = "invalid WaitResultKind with value: " + std::to_string(wait_result_kind);
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
// This should never be reached, but is needed to satisfy the compiler
|
||||
throw std::runtime_error("unreachable code in wait result kind switch");
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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>30.1.2</version>
|
||||
<version>30.1.5</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
@@ -47,9 +47,9 @@
|
||||
<depend>statistics_msgs</depend>
|
||||
<depend>tracetools</depend>
|
||||
|
||||
<test_depend>ament_cmake_gmock</test_depend>
|
||||
<test_depend>ament_cmake_google_benchmark</test_depend>
|
||||
<test_depend>ament_cmake_gtest</test_depend>
|
||||
<test_depend>ament_cmake_ros</test_depend>
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
<test_depend>mimick_vendor</test_depend>
|
||||
|
||||
@@ -640,7 +640,7 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
|
||||
throw std::runtime_error("Unimplemented");
|
||||
}
|
||||
|
||||
default:
|
||||
case rclcpp::DeliveredMessageKind::INVALID:
|
||||
{
|
||||
throw std::runtime_error("Delivered message kind is not supported");
|
||||
}
|
||||
|
||||
@@ -37,6 +37,30 @@ GenericSubscription::create_serialized_message()
|
||||
return std::make_shared<rclcpp::SerializedMessage>(0);
|
||||
}
|
||||
|
||||
void
|
||||
GenericSubscription::disable_callbacks()
|
||||
{
|
||||
SubscriptionBase::disable_callbacks();
|
||||
any_callback_.disable();
|
||||
for (const auto & [_, event_ptr] : event_handlers_) {
|
||||
if (event_ptr) {
|
||||
event_ptr->disable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GenericSubscription::enable_callbacks()
|
||||
{
|
||||
SubscriptionBase::enable_callbacks();
|
||||
any_callback_.enable();
|
||||
for (const auto & [_, event_ptr] : event_handlers_) {
|
||||
if (event_ptr) {
|
||||
event_ptr->enable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GenericSubscription::handle_message(
|
||||
std::shared_ptr<void> &,
|
||||
|
||||
@@ -547,6 +547,18 @@ Node::get_subscriptions_info_by_topic(const std::string & topic_name, bool no_ma
|
||||
return node_graph_->get_subscriptions_info_by_topic(topic_name, no_mangle);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
Node::get_clients_info_by_service(const std::string & service_name, bool no_mangle) const
|
||||
{
|
||||
return node_graph_->get_clients_info_by_service(service_name, no_mangle);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
Node::get_servers_info_by_service(const std::string & service_name, bool no_mangle) const
|
||||
{
|
||||
return node_graph_->get_servers_info_by_service(service_name, no_mangle);
|
||||
}
|
||||
|
||||
void
|
||||
Node::for_each_callback_group(
|
||||
const node_interfaces::NodeBaseInterface::CallbackGroupFunction & func)
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "rclcpp/node_interfaces/node_graph.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstddef>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
@@ -753,6 +754,122 @@ NodeGraph::get_subscriptions_info_by_topic(
|
||||
rcl_get_subscriptions_info_by_topic);
|
||||
}
|
||||
|
||||
static
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
convert_to_service_info_list(const rcl_service_endpoint_info_array_t & info_array)
|
||||
{
|
||||
std::vector<rclcpp::ServiceEndpointInfo> service_info_list;
|
||||
for (size_t i = 0; i < info_array.size; ++i) {
|
||||
service_info_list.push_back(rclcpp::ServiceEndpointInfo(info_array.info_array[i]));
|
||||
}
|
||||
return service_info_list;
|
||||
}
|
||||
|
||||
template<const char * EndpointType, typename FunctionT>
|
||||
static std::vector<rclcpp::ServiceEndpointInfo>
|
||||
get_info_by_service(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::string & service_name,
|
||||
bool no_mangle,
|
||||
FunctionT rcl_get_info_by_service)
|
||||
{
|
||||
std::string fqdn;
|
||||
auto rcl_node_handle = node_base->get_rcl_node_handle();
|
||||
|
||||
if (no_mangle) {
|
||||
fqdn = service_name;
|
||||
} else {
|
||||
fqdn = rclcpp::expand_topic_or_service_name(
|
||||
service_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle),
|
||||
true);
|
||||
|
||||
// Get the node options
|
||||
const rcl_node_options_t * node_options = rcl_node_get_options(rcl_node_handle);
|
||||
if (nullptr == node_options) {
|
||||
throw std::runtime_error("Need valid node options in get_info_by_service()");
|
||||
}
|
||||
const rcl_arguments_t * global_args = nullptr;
|
||||
if (node_options->use_global_arguments) {
|
||||
global_args = &(rcl_node_handle->context->global_arguments);
|
||||
}
|
||||
|
||||
char * remapped_service_name = nullptr;
|
||||
rcl_ret_t ret = rcl_remap_service_name(
|
||||
&(node_options->arguments),
|
||||
global_args,
|
||||
fqdn.c_str(),
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle),
|
||||
node_options->allocator,
|
||||
&remapped_service_name);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, std::string("Failed to remap service name ") + fqdn);
|
||||
} else if (nullptr != remapped_service_name) {
|
||||
fqdn = remapped_service_name;
|
||||
node_options->allocator.deallocate(remapped_service_name, node_options->allocator.state);
|
||||
}
|
||||
}
|
||||
|
||||
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
||||
rcl_service_endpoint_info_array_t info_array =
|
||||
rcl_get_zero_initialized_service_endpoint_info_array();
|
||||
rcl_ret_t ret =
|
||||
rcl_get_info_by_service(rcl_node_handle, &allocator, fqdn.c_str(), no_mangle, &info_array);
|
||||
if (RCL_RET_OK != ret) {
|
||||
auto error_msg =
|
||||
std::string("Failed to get information by service for ") + EndpointType + std::string(":");
|
||||
if (RCL_RET_UNSUPPORTED == ret) {
|
||||
error_msg += std::string("function not supported by RMW_IMPLEMENTATION");
|
||||
} else {
|
||||
error_msg += rcl_get_error_string().str;
|
||||
}
|
||||
rcl_reset_error();
|
||||
if (RCL_RET_OK != rcl_service_endpoint_info_array_fini(&info_array, &allocator)) {
|
||||
error_msg += std::string(", failed also to cleanup service info array, leaking memory: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
throw_from_rcl_error(ret, error_msg);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::ServiceEndpointInfo> service_info_list =
|
||||
convert_to_service_info_list(info_array);
|
||||
ret = rcl_service_endpoint_info_array_fini(&info_array, &allocator);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "rcl_service_info_array_fini failed.");
|
||||
}
|
||||
|
||||
return service_info_list;
|
||||
}
|
||||
|
||||
static constexpr char kClientEndpointTypeName[] = "clients";
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
NodeGraph::get_clients_info_by_service(
|
||||
const std::string & service_name,
|
||||
bool no_mangle) const
|
||||
{
|
||||
return get_info_by_service<kClientEndpointTypeName>(
|
||||
node_base_,
|
||||
service_name,
|
||||
no_mangle,
|
||||
rcl_get_clients_info_by_service);
|
||||
}
|
||||
|
||||
static constexpr char kServerEndpointTypeName[] = "servers";
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
NodeGraph::get_servers_info_by_service(
|
||||
const std::string & service_name,
|
||||
bool no_mangle) const
|
||||
{
|
||||
return get_info_by_service<kServerEndpointTypeName>(
|
||||
node_base_,
|
||||
service_name,
|
||||
no_mangle,
|
||||
rcl_get_servers_info_by_service);
|
||||
}
|
||||
|
||||
std::string &
|
||||
rclcpp::TopicEndpointInfo::node_name()
|
||||
{
|
||||
@@ -836,3 +953,99 @@ rclcpp::TopicEndpointInfo::topic_type_hash() const
|
||||
{
|
||||
return topic_type_hash_;
|
||||
}
|
||||
|
||||
std::string &
|
||||
rclcpp::ServiceEndpointInfo::node_name()
|
||||
{
|
||||
return node_name_;
|
||||
}
|
||||
|
||||
const std::string &
|
||||
rclcpp::ServiceEndpointInfo::node_name() const
|
||||
{
|
||||
return node_name_;
|
||||
}
|
||||
|
||||
std::string &
|
||||
rclcpp::ServiceEndpointInfo::node_namespace()
|
||||
{
|
||||
return node_namespace_;
|
||||
}
|
||||
|
||||
const std::string &
|
||||
rclcpp::ServiceEndpointInfo::node_namespace() const
|
||||
{
|
||||
return node_namespace_;
|
||||
}
|
||||
|
||||
std::string &
|
||||
rclcpp::ServiceEndpointInfo::service_type()
|
||||
{
|
||||
return service_type_;
|
||||
}
|
||||
|
||||
const std::string &
|
||||
rclcpp::ServiceEndpointInfo::service_type() const
|
||||
{
|
||||
return service_type_;
|
||||
}
|
||||
|
||||
rclcpp::EndpointType &
|
||||
rclcpp::ServiceEndpointInfo::endpoint_type()
|
||||
{
|
||||
return endpoint_type_;
|
||||
}
|
||||
|
||||
const rclcpp::EndpointType &
|
||||
rclcpp::ServiceEndpointInfo::endpoint_type() const
|
||||
{
|
||||
return endpoint_type_;
|
||||
}
|
||||
|
||||
size_t &
|
||||
rclcpp::ServiceEndpointInfo::endpoint_count()
|
||||
{
|
||||
return endpoint_count_;
|
||||
}
|
||||
|
||||
const size_t &
|
||||
rclcpp::ServiceEndpointInfo::endpoint_count() const
|
||||
{
|
||||
return endpoint_count_;
|
||||
}
|
||||
|
||||
std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
|
||||
rclcpp::ServiceEndpointInfo::endpoint_gids()
|
||||
{
|
||||
return endpoint_gids_;
|
||||
}
|
||||
|
||||
const std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
|
||||
rclcpp::ServiceEndpointInfo::endpoint_gids() const
|
||||
{
|
||||
return endpoint_gids_;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::QoS> &
|
||||
rclcpp::ServiceEndpointInfo::qos_profiles()
|
||||
{
|
||||
return qos_profiles_;
|
||||
}
|
||||
|
||||
const std::vector<rclcpp::QoS> &
|
||||
rclcpp::ServiceEndpointInfo::qos_profiles() const
|
||||
{
|
||||
return qos_profiles_;
|
||||
}
|
||||
|
||||
rosidl_type_hash_t &
|
||||
rclcpp::ServiceEndpointInfo::service_type_hash()
|
||||
{
|
||||
return service_type_hash_;
|
||||
}
|
||||
|
||||
const rosidl_type_hash_t &
|
||||
rclcpp::ServiceEndpointInfo::service_type_hash() const
|
||||
{
|
||||
return service_type_hash_;
|
||||
}
|
||||
|
||||
97
rclcpp/src/rclcpp/parameter_descriptor_wrapper.cpp
Normal file
97
rclcpp/src/rclcpp/parameter_descriptor_wrapper.cpp
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2023 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/parameter_descriptor_wrapper.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
ParameterDescription::ParameterDescription()
|
||||
{
|
||||
// Need to set this in the constructor, but it doesn't necessarily need to be used
|
||||
parameter_descriptor.type = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET;
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ParameterDescriptor ParameterDescription::build() const
|
||||
{
|
||||
// Return some some sort message
|
||||
return parameter_descriptor;
|
||||
}
|
||||
|
||||
// Builder methods which set up the original class
|
||||
// They all follow the same format of initing the value given within the base class
|
||||
// then returning the current class
|
||||
ParameterDescription & ParameterDescription::set_name(const std::string & name)
|
||||
{
|
||||
parameter_descriptor.name = name;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ParameterDescription & ParameterDescription::set_type(std::uint8_t type)
|
||||
{
|
||||
parameter_descriptor.type = type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ParameterDescription & ParameterDescription::set_description_text(const std::string & description)
|
||||
{
|
||||
parameter_descriptor.description = description;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ParameterDescription & ParameterDescription::set_additional_constraints(
|
||||
const std::string & constraints)
|
||||
{
|
||||
parameter_descriptor.additional_constraints = constraints;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ParameterDescription & ParameterDescription::set_read_only(bool read_only)
|
||||
{
|
||||
parameter_descriptor.read_only = read_only;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ParameterDescription & ParameterDescription::set_dynamic_typing(bool dynamic_typing)
|
||||
{
|
||||
parameter_descriptor.dynamic_typing = dynamic_typing;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Here is the Specific range function for this parameter description
|
||||
ParameterDescription & ParameterDescription::set_floating_point_description_range(
|
||||
float min, float max, float step)
|
||||
{
|
||||
if (parameter_descriptor.type == rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
|
||||
parameter_descriptor.floating_point_range.resize(1);
|
||||
parameter_descriptor.floating_point_range.at(0).from_value = min;
|
||||
parameter_descriptor.floating_point_range.at(0).to_value = max;
|
||||
parameter_descriptor.floating_point_range.at(0).step = step;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
ParameterDescription & ParameterDescription::set_integer_description_range(
|
||||
int min, int max, int step)
|
||||
{
|
||||
if (parameter_descriptor.type == rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER) {
|
||||
parameter_descriptor.integer_range.resize(1);
|
||||
parameter_descriptor.integer_range.at(0).from_value = min;
|
||||
parameter_descriptor.integer_range.at(0).to_value = max;
|
||||
parameter_descriptor.integer_range.at(0).step = step;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -74,6 +74,38 @@ ParameterEventHandler::add_parameter_callback(
|
||||
return handle;
|
||||
}
|
||||
|
||||
bool
|
||||
ParameterEventHandler::configure_nodes_filter(const std::vector<std::string> & node_names)
|
||||
{
|
||||
if (node_names.empty()) {
|
||||
// Clear content filter
|
||||
event_subscription_->set_content_filter("");
|
||||
if (event_subscription_->is_cft_enabled()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string filter_expression;
|
||||
size_t total = node_names.size();
|
||||
for (size_t i = 0; i < total; ++i) {
|
||||
filter_expression += "node = %" + std::to_string(i);
|
||||
if (i < total - 1) {
|
||||
filter_expression += " OR ";
|
||||
}
|
||||
}
|
||||
|
||||
// Enclose each node name in "'".
|
||||
std::vector<std::string> quoted_node_names;
|
||||
for (const auto & name : node_names) {
|
||||
quoted_node_names.push_back("'" + resolve_path(name) + "'");
|
||||
}
|
||||
|
||||
event_subscription_->set_content_filter(filter_expression, quoted_node_names);
|
||||
|
||||
return event_subscription_->is_cft_enabled();
|
||||
}
|
||||
|
||||
void
|
||||
ParameterEventHandler::remove_parameter_callback(
|
||||
ParameterCallbackHandle::SharedPtr callback_handle)
|
||||
|
||||
@@ -47,9 +47,9 @@ ParameterService::ParameterService(
|
||||
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());
|
||||
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to get parameters: %s", ex.what());
|
||||
} catch (const rclcpp::exceptions::ParameterUninitializedException & ex) {
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to get parameters: %s", ex.what());
|
||||
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to get parameters: %s", ex.what());
|
||||
}
|
||||
},
|
||||
qos_profile, nullptr);
|
||||
@@ -70,7 +70,7 @@ ParameterService::ParameterService(
|
||||
return static_cast<rclcpp::ParameterType>(type);
|
||||
});
|
||||
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to get parameter types: %s", ex.what());
|
||||
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to get parameter types: %s", ex.what());
|
||||
}
|
||||
},
|
||||
qos_profile, nullptr);
|
||||
@@ -91,7 +91,7 @@ ParameterService::ParameterService(
|
||||
result = node_params->set_parameters_atomically(
|
||||
{rclcpp::Parameter::from_parameter_msg(p)});
|
||||
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to set parameter: %s", ex.what());
|
||||
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to set parameter: %s", ex.what());
|
||||
result.successful = false;
|
||||
result.reason = ex.what();
|
||||
}
|
||||
@@ -119,7 +119,7 @@ ParameterService::ParameterService(
|
||||
auto result = node_params->set_parameters_atomically(pvariants);
|
||||
response->result = result;
|
||||
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
|
||||
RCLCPP_DEBUG(
|
||||
RCLCPP_WARN(
|
||||
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";
|
||||
@@ -139,7 +139,7 @@ ParameterService::ParameterService(
|
||||
auto descriptors = node_params->describe_parameters(request->names);
|
||||
response->descriptors = descriptors;
|
||||
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp"), "Failed to describe parameters: %s", ex.what());
|
||||
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to describe parameters: %s", ex.what());
|
||||
}
|
||||
},
|
||||
qos_profile, nullptr);
|
||||
|
||||
@@ -44,9 +44,9 @@ rclcpp::to_string(const ParameterType type)
|
||||
return "double_array";
|
||||
case ParameterType::PARAMETER_STRING_ARRAY:
|
||||
return "string_array";
|
||||
default:
|
||||
return "unknown type";
|
||||
}
|
||||
|
||||
return "unknown type";
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
@@ -103,9 +103,9 @@ rclcpp::to_string(const ParameterValue & value)
|
||||
return array_to_string(value.get<std::vector<double>>());
|
||||
case ParameterType::PARAMETER_STRING_ARRAY:
|
||||
return array_to_string(value.get<std::vector<std::string>>());
|
||||
default:
|
||||
return "unknown type";
|
||||
}
|
||||
|
||||
return "unknown type";
|
||||
}
|
||||
|
||||
ParameterValue::ParameterValue()
|
||||
|
||||
@@ -40,6 +40,12 @@ std::string qos_policy_name_from_kind(rmw_qos_policy_kind_t policy_kind)
|
||||
return "HISTORY_QOS_POLICY";
|
||||
case RMW_QOS_POLICY_LIFESPAN:
|
||||
return "LIFESPAN_QOS_POLICY";
|
||||
case RMW_QOS_POLICY_DEPTH:
|
||||
return "DEPTH_QOS_POLICY";
|
||||
case RMW_QOS_POLICY_LIVELINESS_LEASE_DURATION:
|
||||
return "LIVELINESS_LEASE_DURATION_QOS_POLICY";
|
||||
case RMW_QOS_POLICY_AVOID_ROS_NAMESPACE_CONVENTIONS:
|
||||
return "AVOID_ROS_NAMESPACE_CONVENTIONS_QOS_POLICY";
|
||||
default:
|
||||
return "INVALID_QOS_POLICY";
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ void
|
||||
SignalHandler::signal_handler(
|
||||
int signum, siginfo_t * siginfo, void * context)
|
||||
{
|
||||
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum);
|
||||
auto & instance = SignalHandler::get_global_signal_handler();
|
||||
|
||||
auto old_signal_handler = instance.get_old_signal_handler(signum);
|
||||
@@ -85,13 +84,12 @@ SignalHandler::signal_handler(
|
||||
old_signal_handler.sa_handler(signum);
|
||||
}
|
||||
}
|
||||
instance.signal_handler_common();
|
||||
instance.signal_handler_common(signum);
|
||||
}
|
||||
#else
|
||||
void
|
||||
SignalHandler::signal_handler(int signum)
|
||||
{
|
||||
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum);
|
||||
auto & instance = SignalHandler::get_global_signal_handler();
|
||||
auto old_signal_handler = instance.get_old_signal_handler(signum);
|
||||
if (
|
||||
@@ -100,7 +98,7 @@ SignalHandler::signal_handler(int signum)
|
||||
{
|
||||
old_signal_handler(signum);
|
||||
}
|
||||
instance.signal_handler_common();
|
||||
instance.signal_handler_common(signum);
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -245,13 +243,11 @@ SignalHandler::get_old_signal_handler(int signum)
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::signal_handler_common()
|
||||
SignalHandler::signal_handler_common(int signum)
|
||||
{
|
||||
auto & instance = SignalHandler::get_global_signal_handler();
|
||||
instance.signal_number_.store(signum);
|
||||
instance.signal_received_.store(true);
|
||||
RCLCPP_DEBUG(
|
||||
get_logger(),
|
||||
"signal_handler(): notifying deferred signal handler");
|
||||
instance.notify_signal_handler();
|
||||
}
|
||||
|
||||
@@ -260,6 +256,8 @@ SignalHandler::deferred_signal_handler()
|
||||
{
|
||||
while (true) {
|
||||
if (signal_received_.exchange(false)) {
|
||||
int signum = signal_number_.load();
|
||||
RCLCPP_INFO(get_logger(), "signal_handler(signum=%d)", signum);
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): shutting down");
|
||||
for (auto context_ptr : rclcpp::get_contexts()) {
|
||||
if (context_ptr->get_init_options().shutdown_on_signal) {
|
||||
|
||||
@@ -116,7 +116,7 @@ private:
|
||||
|
||||
/// Common signal handler code between sigaction and non-sigaction versions.
|
||||
void
|
||||
signal_handler_common();
|
||||
signal_handler_common(int signum);
|
||||
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
/// Signal handler function.
|
||||
@@ -189,6 +189,8 @@ private:
|
||||
|
||||
// Whether or not a signal has been received.
|
||||
std::atomic_bool signal_received_ = false;
|
||||
// The signal number that was received.
|
||||
std::atomic_int signal_number_ = 0;
|
||||
// A thread to which signal handling tasks are deferred.
|
||||
std::thread signal_handler_thread_;
|
||||
|
||||
|
||||
@@ -577,3 +577,26 @@ SubscriptionBase::take_dynamic_message(
|
||||
throw std::runtime_error("Unimplemented");
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
SubscriptionBase::disable_callbacks()
|
||||
{
|
||||
// Temporary remove the on_new_message_callback_ to prevent it from being called
|
||||
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
|
||||
if (on_new_message_callback_) {
|
||||
set_on_new_message_callback(nullptr, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SubscriptionBase::enable_callbacks()
|
||||
{
|
||||
// Set callback again if it was previously removed in disable_callbacks()
|
||||
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
|
||||
if (on_new_message_callback_) {
|
||||
set_on_new_message_callback(
|
||||
rclcpp::detail::cpp_callback_trampoline<
|
||||
decltype(on_new_message_callback_), const void *, size_t>,
|
||||
static_cast<const void *>(&on_new_message_callback_));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,3 +40,17 @@ SubscriptionIntraProcessBase::is_durability_transient_local() const
|
||||
{
|
||||
return qos_profile_.durability() == rclcpp::DurabilityPolicy::TransientLocal;
|
||||
}
|
||||
|
||||
void
|
||||
SubscriptionIntraProcessBase::disable_callbacks()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
|
||||
on_new_message_callback_disabled_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
SubscriptionIntraProcessBase::enable_callbacks()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
|
||||
on_new_message_callback_disabled_ = false;
|
||||
}
|
||||
|
||||
@@ -123,27 +123,27 @@ std::string get_typesupport_library_path(
|
||||
{
|
||||
const char * dynamic_library_folder;
|
||||
#ifdef _WIN32
|
||||
dynamic_library_folder = "/bin/";
|
||||
dynamic_library_folder = "bin";
|
||||
#elif __APPLE__
|
||||
dynamic_library_folder = "/lib/";
|
||||
dynamic_library_folder = "lib";
|
||||
#else
|
||||
dynamic_library_folder = "/lib/";
|
||||
dynamic_library_folder = "lib";
|
||||
#endif
|
||||
|
||||
std::string package_prefix;
|
||||
std::filesystem::path package_prefix;
|
||||
try {
|
||||
package_prefix = ament_index_cpp::get_package_prefix(package_name);
|
||||
ament_index_cpp::get_package_prefix(package_name, package_prefix);
|
||||
} catch (ament_index_cpp::PackageNotFoundError & e) {
|
||||
throw std::runtime_error(e.what());
|
||||
}
|
||||
|
||||
const std::string library_path = rcpputils::path_for_library(
|
||||
package_prefix + dynamic_library_folder,
|
||||
(package_prefix / dynamic_library_folder).string(),
|
||||
package_name + "__" + typesupport_identifier);
|
||||
if (library_path.empty()) {
|
||||
throw std::runtime_error(
|
||||
"Typesupport library for " + package_name + " does not exist in '" + package_prefix +
|
||||
"'.");
|
||||
"Typesupport library for " + package_name + " does not exist in '" +
|
||||
package_prefix.string() + "'.");
|
||||
}
|
||||
return library_path;
|
||||
}
|
||||
|
||||
@@ -662,32 +662,24 @@ endif()
|
||||
function(test_on_all_rmws)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
|
||||
# If the rmw_implementation is rmw_zenoh_cpp, run the tests with multicast discovery enabled.
|
||||
# Note: This is a temporary change that will be reverted before we branch into Kilted.
|
||||
if(rmw_implementation STREQUAL "rmw_zenoh_cpp")
|
||||
list(APPEND rmw_implementation_env_var
|
||||
ZENOH_CONFIG_OVERRIDE=scouting/multicast/enabled=true
|
||||
)
|
||||
endif()
|
||||
|
||||
ament_add_gmock_test(test_qos_event
|
||||
ament_add_ros_isolated_gmock_test(test_qos_event
|
||||
TEST_NAME test_qos_event${target_suffix}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
)
|
||||
ament_add_test_label(test_qos_event${target_suffix} mimick)
|
||||
|
||||
ament_add_gmock_test(test_generic_pubsub
|
||||
ament_add_ros_isolated_gmock_test(test_generic_pubsub
|
||||
TEST_NAME test_generic_pubsub${target_suffix}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
)
|
||||
|
||||
ament_add_gmock_test(test_add_callback_groups_to_executor
|
||||
ament_add_ros_isolated_gmock_test(test_add_callback_groups_to_executor
|
||||
TEST_NAME test_add_callback_groups_to_executor${target_suffix}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
TIMEOUT 120
|
||||
)
|
||||
|
||||
ament_add_gmock_test(test_subscription_content_filter
|
||||
ament_add_ros_isolated_gmock_test(test_subscription_content_filter
|
||||
TEST_NAME test_subscription_content_filter${target_suffix}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
TIMEOUT 120
|
||||
|
||||
@@ -1,617 +0,0 @@
|
||||
// Copyright 2020 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 <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "rcpputils/scope_exit.hpp"
|
||||
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
|
||||
#include "../../mocking_utils/patch.hpp"
|
||||
#include "../../utils/rclcpp_gtest_macros.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
struct NumberOfEntities
|
||||
{
|
||||
size_t subscriptions = 0;
|
||||
size_t timers = 0;
|
||||
size_t services = 0;
|
||||
size_t clients = 0;
|
||||
size_t waitables = 0;
|
||||
};
|
||||
|
||||
std::unique_ptr<NumberOfEntities> get_number_of_default_entities(rclcpp::Node::SharedPtr node)
|
||||
{
|
||||
auto number_of_entities = std::make_unique<NumberOfEntities>();
|
||||
node->for_each_callback_group(
|
||||
[&number_of_entities](rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
if (!group->can_be_taken_from().load()) {
|
||||
return;
|
||||
}
|
||||
group->find_subscription_ptrs_if(
|
||||
[&number_of_entities](rclcpp::SubscriptionBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->subscriptions++; return false;
|
||||
});
|
||||
group->find_timer_ptrs_if(
|
||||
[&number_of_entities](rclcpp::TimerBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->timers++; return false;
|
||||
});
|
||||
group->find_service_ptrs_if(
|
||||
[&number_of_entities](rclcpp::ServiceBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->services++; return false;
|
||||
});
|
||||
group->find_client_ptrs_if(
|
||||
[&number_of_entities](rclcpp::ClientBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->clients++; return false;
|
||||
});
|
||||
group->find_waitable_ptrs_if(
|
||||
[&number_of_entities](rclcpp::Waitable::SharedPtr &)
|
||||
{
|
||||
number_of_entities->waitables++; return false;
|
||||
});
|
||||
});
|
||||
|
||||
return number_of_entities;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class TestStaticExecutorEntitiesCollector : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
entities_collector_ =
|
||||
std::make_shared<rclcpp::executors::StaticExecutorEntitiesCollector>();
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
rclcpp::executors::StaticExecutorEntitiesCollector::SharedPtr entities_collector_;
|
||||
};
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, construct_destruct) {
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_subscriptions());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_timers());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_services());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_clients());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_waitables());
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node) {
|
||||
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
|
||||
EXPECT_NO_THROW(entities_collector_->add_node(node1->get_node_base_interface()));
|
||||
|
||||
// Check adding second time
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->add_node(node1->get_node_base_interface()),
|
||||
std::runtime_error("Node has already been added to an executor."));
|
||||
|
||||
auto node2 = std::make_shared<rclcpp::Node>("node2", "ns");
|
||||
EXPECT_FALSE(entities_collector_->remove_node(node2->get_node_base_interface()));
|
||||
EXPECT_NO_THROW(entities_collector_->add_node(node2->get_node_base_interface()));
|
||||
|
||||
EXPECT_TRUE(entities_collector_->remove_node(node1->get_node_base_interface()));
|
||||
EXPECT_FALSE(entities_collector_->remove_node(node1->get_node_base_interface()));
|
||||
EXPECT_TRUE(entities_collector_->remove_node(node2->get_node_base_interface()));
|
||||
|
||||
auto node3 = std::make_shared<rclcpp::Node>("node3", "ns");
|
||||
node3->get_node_base_interface()->get_associated_with_executor_atomic().exchange(true);
|
||||
EXPECT_FALSE(entities_collector_->remove_node(node3->get_node_base_interface()));
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, init_bad_arguments) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
auto shared_context = node->get_node_base_interface()->get_context();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
EXPECT_EQ(
|
||||
RCL_RET_OK,
|
||||
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
|
||||
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
|
||||
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
|
||||
// Check memory strategy is nullptr
|
||||
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy = nullptr;
|
||||
EXPECT_THROW(
|
||||
entities_collector_->init(&wait_set, memory_strategy),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, add_remove_basic_node) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
const auto expected_number_of_entities = get_number_of_default_entities(node);
|
||||
EXPECT_NE(nullptr, expected_number_of_entities);
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
auto shared_context = node->get_node_base_interface()->get_context();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
EXPECT_EQ(
|
||||
RCL_RET_OK,
|
||||
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
|
||||
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
|
||||
|
||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
|
||||
EXPECT_EQ(
|
||||
expected_number_of_entities->subscriptions,
|
||||
entities_collector_->get_number_of_subscriptions());
|
||||
EXPECT_EQ(expected_number_of_entities->timers, entities_collector_->get_number_of_timers());
|
||||
EXPECT_EQ(expected_number_of_entities->services, entities_collector_->get_number_of_services());
|
||||
EXPECT_EQ(expected_number_of_entities->clients, entities_collector_->get_number_of_clients());
|
||||
// One extra for the executor
|
||||
EXPECT_EQ(
|
||||
1u + expected_number_of_entities->waitables,
|
||||
entities_collector_->get_number_of_waitables());
|
||||
|
||||
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_subscriptions());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_timers());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_services());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_clients());
|
||||
|
||||
// Still one for the executor
|
||||
EXPECT_EQ(1u, entities_collector_->get_number_of_waitables());
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node_out_of_scope) {
|
||||
rclcpp::Context::SharedPtr shared_context = nullptr;
|
||||
{
|
||||
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
|
||||
auto node2 = std::make_shared<rclcpp::Node>("node2", "ns");
|
||||
auto node3 = std::make_shared<rclcpp::Node>("node3", "ns");
|
||||
entities_collector_->add_node(node1->get_node_base_interface());
|
||||
entities_collector_->add_node(node2->get_node_base_interface());
|
||||
entities_collector_->add_node(node3->get_node_base_interface());
|
||||
shared_context = node1->get_node_base_interface()->get_context();
|
||||
}
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
EXPECT_EQ(
|
||||
RCL_RET_OK,
|
||||
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
|
||||
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
|
||||
|
||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
|
||||
// Expect weak_node pointers to be cleaned up and used
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_subscriptions());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_timers());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_services());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_clients());
|
||||
|
||||
// Still one for the executor
|
||||
EXPECT_EQ(1u, entities_collector_->get_number_of_waitables());
|
||||
}
|
||||
|
||||
class TestWaitable : public rclcpp::Waitable
|
||||
{
|
||||
public:
|
||||
void add_to_wait_set(rcl_wait_set_t &) override {}
|
||||
|
||||
bool is_ready(const rcl_wait_set_t &) override {return true;}
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
void
|
||||
execute(const std::shared_ptr<void> &) override {}
|
||||
};
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node_with_entities) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
auto expected_number_of_entities = get_number_of_default_entities(node);
|
||||
EXPECT_NE(nullptr, expected_number_of_entities);
|
||||
|
||||
// Create 1 of each entity type
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", rclcpp::QoS(10), [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
auto timer =
|
||||
node->create_wall_timer(std::chrono::seconds(60), []() {});
|
||||
auto service =
|
||||
node->create_service<test_msgs::srv::Empty>(
|
||||
"service",
|
||||
[](
|
||||
const test_msgs::srv::Empty::Request::SharedPtr,
|
||||
test_msgs::srv::Empty::Response::SharedPtr) {});
|
||||
auto client = node->create_client<test_msgs::srv::Empty>("service");
|
||||
auto waitable = std::make_shared<TestWaitable>();
|
||||
|
||||
// Adding a subscription could add another waitable, so we need to get the
|
||||
// current number of waitables just before adding the new waitable.
|
||||
expected_number_of_entities->waitables = get_number_of_default_entities(node)->waitables;
|
||||
node->get_node_waitables_interface()->add_waitable(waitable, nullptr);
|
||||
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
auto shared_context = node->get_node_base_interface()->get_context();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
EXPECT_EQ(
|
||||
RCL_RET_OK,
|
||||
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
|
||||
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
|
||||
|
||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
||||
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
|
||||
|
||||
EXPECT_EQ(
|
||||
1u + expected_number_of_entities->subscriptions,
|
||||
entities_collector_->get_number_of_subscriptions());
|
||||
EXPECT_EQ(1u + expected_number_of_entities->timers, entities_collector_->get_number_of_timers());
|
||||
EXPECT_EQ(
|
||||
1u + expected_number_of_entities->services,
|
||||
entities_collector_->get_number_of_services());
|
||||
EXPECT_EQ(
|
||||
1u + expected_number_of_entities->clients,
|
||||
entities_collector_->get_number_of_clients());
|
||||
|
||||
// One extra for the executor
|
||||
EXPECT_EQ(
|
||||
2u + expected_number_of_entities->waitables,
|
||||
entities_collector_->get_number_of_waitables());
|
||||
|
||||
entities_collector_->remove_node(node->get_node_base_interface());
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_subscriptions());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_timers());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_services());
|
||||
EXPECT_EQ(0u, entities_collector_->get_number_of_clients());
|
||||
// Still one for the executor
|
||||
EXPECT_EQ(1u, entities_collector_->get_number_of_waitables());
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, add_callback_group) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
|
||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
|
||||
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
|
||||
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u);
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, add_callback_group_after_add_node) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
|
||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface()),
|
||||
std::runtime_error("Callback group has already been added to an executor."));
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, add_callback_group_twice) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
|
||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
|
||||
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
|
||||
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u);
|
||||
cb_group->get_associated_with_executor_atomic().exchange(false);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface()),
|
||||
std::runtime_error("Callback group was already added to executor."));
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, prepare_wait_set_rcl_wait_set_clear_error) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
auto shared_context = node->get_node_base_interface()->get_context();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
EXPECT_EQ(
|
||||
RCL_RET_OK,
|
||||
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
|
||||
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
|
||||
|
||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
|
||||
std::shared_ptr<void> data = entities_collector_->take_data();
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->execute(data),
|
||||
std::runtime_error("Couldn't clear wait set"));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, prepare_wait_set_rcl_wait_set_resize_error) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
auto shared_context = node->get_node_base_interface()->get_context();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
EXPECT_EQ(
|
||||
RCL_RET_OK,
|
||||
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
|
||||
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
|
||||
|
||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_resize, RCL_RET_ERROR);
|
||||
std::shared_ptr<void> data = entities_collector_->take_data();
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->execute(data),
|
||||
std::runtime_error("Couldn't resize the wait set: error not set"));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, refresh_wait_set_not_initialized) {
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->refresh_wait_set(std::chrono::nanoseconds(1000)),
|
||||
std::runtime_error("Couldn't clear wait set"));
|
||||
rcl_reset_error();
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, refresh_wait_set_rcl_wait_failed) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
auto shared_context = node->get_node_base_interface()->get_context();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
EXPECT_EQ(
|
||||
RCL_RET_OK,
|
||||
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
|
||||
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
|
||||
|
||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->refresh_wait_set(std::chrono::nanoseconds(1000)),
|
||||
std::runtime_error("rcl_wait() failed: error not set"));
|
||||
}
|
||||
|
||||
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, refresh_wait_set_add_handles_to_wait_set_failed) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
|
||||
// Create 1 of each entity type
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", rclcpp::QoS(10), [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
auto timer =
|
||||
node->create_wall_timer(std::chrono::seconds(60), []() {});
|
||||
auto service =
|
||||
node->create_service<test_msgs::srv::Empty>(
|
||||
"service",
|
||||
[](
|
||||
const test_msgs::srv::Empty::Request::SharedPtr,
|
||||
test_msgs::srv::Empty::Response::SharedPtr) {});
|
||||
auto client = node->create_client<test_msgs::srv::Empty>("service");
|
||||
auto waitable = std::make_shared<TestWaitable>();
|
||||
|
||||
node->get_node_waitables_interface()->add_waitable(waitable, nullptr);
|
||||
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
auto shared_context = node->get_node_base_interface()->get_context();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
EXPECT_EQ(
|
||||
RCL_RET_OK,
|
||||
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
|
||||
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
|
||||
|
||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
||||
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_wait_set_add_subscription,
|
||||
RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->refresh_wait_set(std::chrono::nanoseconds(1000)),
|
||||
std::runtime_error("Couldn't fill wait set"));
|
||||
}
|
||||
|
||||
entities_collector_->remove_node(node->get_node_base_interface());
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, add_to_wait_set_nullptr) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
auto shared_context = node->get_node_base_interface()->get_context();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
EXPECT_EQ(
|
||||
RCL_RET_OK,
|
||||
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
|
||||
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
|
||||
|
||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
|
||||
|
||||
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, fill_memory_strategy_invalid_group) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
auto shared_context = node->get_node_base_interface()->get_context();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
EXPECT_EQ(
|
||||
RCL_RET_OK,
|
||||
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
|
||||
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
|
||||
|
||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
|
||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
|
||||
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
|
||||
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 2u);
|
||||
|
||||
cb_group.reset();
|
||||
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
|
||||
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u);
|
||||
|
||||
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, remove_callback_group_after_node) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
|
||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
|
||||
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
|
||||
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u);
|
||||
|
||||
node.reset();
|
||||
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->remove_callback_group(cb_group),
|
||||
std::runtime_error("Node must not be deleted before its callback group(s)."));
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, remove_callback_group_twice) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node1", "ns");
|
||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
|
||||
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
|
||||
ASSERT_EQ(entities_collector_->get_all_callback_groups().size(), 1u);
|
||||
|
||||
entities_collector_->remove_callback_group(cb_group);
|
||||
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->remove_callback_group(cb_group),
|
||||
std::runtime_error("Callback group needs to be associated with executor."));
|
||||
}
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, remove_node_opposite_order) {
|
||||
auto node1 = std::make_shared<rclcpp::Node>("node1", "ns");
|
||||
EXPECT_NO_THROW(entities_collector_->add_node(node1->get_node_base_interface()));
|
||||
|
||||
auto node2 = std::make_shared<rclcpp::Node>("node2", "ns");
|
||||
EXPECT_NO_THROW(entities_collector_->add_node(node2->get_node_base_interface()));
|
||||
|
||||
EXPECT_TRUE(entities_collector_->remove_node(node2->get_node_base_interface()));
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
TestStaticExecutorEntitiesCollector,
|
||||
add_callback_groups_from_nodes_associated_to_executor_add) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
|
||||
entities_collector_->add_callback_group(cb_group, node->get_node_base_interface());
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
auto shared_context = node->get_node_base_interface()->get_context();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
EXPECT_EQ(
|
||||
RCL_RET_OK,
|
||||
rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator));
|
||||
RCPPUTILS_SCOPE_EXIT({EXPECT_EQ(RCL_RET_OK, rcl_wait_set_fini(&wait_set));});
|
||||
|
||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
|
||||
entities_collector_->init(&wait_set, memory_strategy);
|
||||
RCPPUTILS_SCOPE_EXIT(entities_collector_->fini());
|
||||
|
||||
cb_group->get_associated_with_executor_atomic().exchange(false);
|
||||
std::shared_ptr<void> data = entities_collector_->take_data();
|
||||
entities_collector_->execute(data);
|
||||
|
||||
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
|
||||
}
|
||||
@@ -192,7 +192,7 @@ TEST_F(TestNodeInterfaces, ni_init) {
|
||||
/*
|
||||
Testing macro'ed getters.
|
||||
*/
|
||||
TEST_F(TestNodeInterfaces, ni_all_init) {
|
||||
TEST_F(TestNodeInterfaces, ni_all_init_non_const) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
using rclcpp::node_interfaces::NodeInterfaces;
|
||||
@@ -252,3 +252,64 @@ TEST_F(TestNodeInterfaces, ni_all_init) {
|
||||
time_source = ni.get_node_time_source_interface();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestNodeInterfaces, ni_all_init_const) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
using rclcpp::node_interfaces::NodeInterfaces;
|
||||
using rclcpp::node_interfaces::NodeBaseInterface;
|
||||
using rclcpp::node_interfaces::NodeClockInterface;
|
||||
using rclcpp::node_interfaces::NodeGraphInterface;
|
||||
using rclcpp::node_interfaces::NodeLoggingInterface;
|
||||
using rclcpp::node_interfaces::NodeTimersInterface;
|
||||
using rclcpp::node_interfaces::NodeTopicsInterface;
|
||||
using rclcpp::node_interfaces::NodeServicesInterface;
|
||||
using rclcpp::node_interfaces::NodeWaitablesInterface;
|
||||
using rclcpp::node_interfaces::NodeParametersInterface;
|
||||
using rclcpp::node_interfaces::NodeTimeSourceInterface;
|
||||
|
||||
const auto ni = rclcpp::node_interfaces::NodeInterfaces<ALL_RCLCPP_NODE_INTERFACES>(*node);
|
||||
|
||||
{
|
||||
auto base = ni.get<NodeBaseInterface>();
|
||||
base = ni.get_node_base_interface();
|
||||
EXPECT_STREQ(base->get_name(), "my_node"); // Test for functionality
|
||||
}
|
||||
{
|
||||
auto clock = ni.get<NodeClockInterface>();
|
||||
clock = ni.get_node_clock_interface();
|
||||
clock->get_clock();
|
||||
}
|
||||
{
|
||||
auto graph = ni.get<NodeGraphInterface>();
|
||||
graph = ni.get_node_graph_interface();
|
||||
}
|
||||
{
|
||||
auto logging = ni.get<NodeLoggingInterface>();
|
||||
logging = ni.get_node_logging_interface();
|
||||
}
|
||||
{
|
||||
auto timers = ni.get<NodeTimersInterface>();
|
||||
timers = ni.get_node_timers_interface();
|
||||
}
|
||||
{
|
||||
auto topics = ni.get<NodeTopicsInterface>();
|
||||
topics = ni.get_node_topics_interface();
|
||||
}
|
||||
{
|
||||
auto services = ni.get<NodeServicesInterface>();
|
||||
services = ni.get_node_services_interface();
|
||||
}
|
||||
{
|
||||
auto waitables = ni.get<NodeWaitablesInterface>();
|
||||
waitables = ni.get_node_waitables_interface();
|
||||
}
|
||||
{
|
||||
auto parameters = ni.get<NodeParametersInterface>();
|
||||
parameters = ni.get_node_parameters_interface();
|
||||
}
|
||||
{
|
||||
auto time_source = ni.get<NodeTimeSourceInterface>();
|
||||
time_source = ni.get_node_time_source_interface();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,6 +94,8 @@ public:
|
||||
void handle_dynamic_message(
|
||||
const DynamicMessage::SharedPtr &,
|
||||
const rclcpp::MessageInfo &) override {}
|
||||
void disable_callbacks() override {}
|
||||
void enable_callbacks() override {}
|
||||
};
|
||||
|
||||
class TestNodeTopics : public ::testing::Test
|
||||
|
||||
@@ -808,7 +808,8 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_service_out_of_scope) {
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
}
|
||||
EXPECT_EQ(1u, allocator_memory_strategy()->number_of_ready_services());
|
||||
// service is out of scope here, so should be cleaned up already.
|
||||
EXPECT_EQ(0u, allocator_memory_strategy()->number_of_ready_services());
|
||||
|
||||
rclcpp::AnyExecutable result;
|
||||
allocator_memory_strategy()->get_next_service(result, weak_groups_to_nodes);
|
||||
@@ -843,7 +844,8 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_client_out_of_scope) {
|
||||
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
}
|
||||
EXPECT_EQ(1u, allocator_memory_strategy()->number_of_ready_clients());
|
||||
// client is out of scope here, so should be cleaned up already.
|
||||
EXPECT_EQ(0u, allocator_memory_strategy()->number_of_ready_clients());
|
||||
|
||||
rclcpp::AnyExecutable result;
|
||||
allocator_memory_strategy()->get_next_client(result, weak_groups_to_nodes);
|
||||
@@ -871,7 +873,8 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_timer_out_of_scope) {
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
}
|
||||
EXPECT_EQ(1u, allocator_memory_strategy()->number_of_ready_timers());
|
||||
// timer is out of scope here, so should be cleaned up already.
|
||||
EXPECT_EQ(0u, allocator_memory_strategy()->number_of_ready_timers());
|
||||
|
||||
rclcpp::AnyExecutable result;
|
||||
allocator_memory_strategy()->get_next_timer(result, weak_groups_to_nodes);
|
||||
|
||||
@@ -14,11 +14,13 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#define RCLCPP_AVOID_DEPRECATIONS_FOR_UNIT_TESTS 1
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
|
||||
@@ -177,7 +179,7 @@ TEST_F(TestAnySubscriptionCallback, is_serialized_message_callback) {
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](std::shared_ptr<rclcpp::SerializedMessage>) {});
|
||||
asc.set([](std::shared_ptr<const rclcpp::SerializedMessage>) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
@@ -186,7 +188,7 @@ TEST_F(TestAnySubscriptionCallback, is_serialized_message_callback) {
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](std::shared_ptr<rclcpp::SerializedMessage>, const rclcpp::MessageInfo &) {});
|
||||
asc.set([](std::shared_ptr<const rclcpp::SerializedMessage>, const rclcpp::MessageInfo &) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
@@ -207,6 +209,126 @@ TEST_F(TestAnySubscriptionCallback, unset_dispatch_throw) {
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
//
|
||||
// Testing disable and enable callbacks
|
||||
//
|
||||
TEST_F(TestAnySubscriptionCallback, disable_enable_callbacks) {
|
||||
std::atomic<int> callback_count{0};
|
||||
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([&callback_count](const test_msgs::msg::Empty &) {
|
||||
++callback_count;
|
||||
});
|
||||
|
||||
// Callback should work initially
|
||||
asc.dispatch(msg_shared_ptr_, message_info_);
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Disable and verify callback is not called
|
||||
asc.disable();
|
||||
asc.dispatch(msg_shared_ptr_, message_info_);
|
||||
EXPECT_EQ(1, callback_count.load()); // Count should not increase
|
||||
|
||||
// Enable and verify callback works again
|
||||
asc.enable();
|
||||
asc.dispatch(msg_shared_ptr_, message_info_);
|
||||
EXPECT_EQ(2, callback_count.load());
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, disable_enable_callbacks_intra_process) {
|
||||
std::atomic<int> callback_count{0};
|
||||
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([&callback_count](std::shared_ptr<const test_msgs::msg::Empty>) {
|
||||
++callback_count;
|
||||
});
|
||||
|
||||
// Callback should work initially
|
||||
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Disable and verify callback is not called
|
||||
asc.disable();
|
||||
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Enable and verify callback works again
|
||||
asc.enable();
|
||||
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
|
||||
EXPECT_EQ(2, callback_count.load());
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, disable_enable_callbacks_unique_ptr) {
|
||||
std::atomic<int> callback_count{0};
|
||||
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([&callback_count](std::unique_ptr<test_msgs::msg::Empty>) {
|
||||
++callback_count;
|
||||
});
|
||||
|
||||
// Callback should work initially
|
||||
asc.dispatch_intra_process(get_unique_ptr_msg(), message_info_);
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Disable and verify callback is not called
|
||||
asc.disable();
|
||||
asc.dispatch_intra_process(get_unique_ptr_msg(), message_info_);
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Enable and verify callback works again
|
||||
asc.enable();
|
||||
asc.dispatch_intra_process(get_unique_ptr_msg(), message_info_);
|
||||
EXPECT_EQ(2, callback_count.load());
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, disable_enable_serialized_message) {
|
||||
std::atomic<int> callback_count{0};
|
||||
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([&callback_count](const rclcpp::SerializedMessage &) {
|
||||
++callback_count;
|
||||
});
|
||||
|
||||
auto serialized_msg = std::make_shared<rclcpp::SerializedMessage>();
|
||||
|
||||
// Callback should work initially
|
||||
asc.dispatch(serialized_msg, message_info_);
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Disable and verify callback is not called
|
||||
asc.disable();
|
||||
asc.dispatch(serialized_msg, message_info_);
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Enable and verify callback works again
|
||||
asc.enable();
|
||||
asc.dispatch(serialized_msg, message_info_);
|
||||
EXPECT_EQ(2, callback_count.load());
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallbackTA, disable_enable_callbacks_type_adapter) {
|
||||
std::atomic<int> callback_count{0};
|
||||
|
||||
rclcpp::AnySubscriptionCallback<MyTA> asc;
|
||||
asc.set([&callback_count](const MyEmpty &) {
|
||||
++callback_count;
|
||||
});
|
||||
|
||||
// Callback should work initially
|
||||
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Disable and verify callback is not called
|
||||
asc.disable();
|
||||
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Enable and verify callback works again
|
||||
asc.enable();
|
||||
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
|
||||
EXPECT_EQ(2, callback_count.load());
|
||||
}
|
||||
|
||||
//
|
||||
// Parameterized test to test across all callback types and dispatch types.
|
||||
//
|
||||
|
||||
@@ -342,12 +342,12 @@ TEST_F(TestExecutor, spin_all_fail_wait_set_clear) {
|
||||
subscription =
|
||||
rclcpp::create_subscription<test_msgs::msg::Empty>(
|
||||
node_topics, "test", rclcpp::QoS(10), std::move(callback));
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
|
||||
|
||||
dummy.spin_all(std::chrono::milliseconds(1));
|
||||
// second spin_all triggers rcl_wait_set_clear that should be called
|
||||
// whenever a waitset gets rebuild and it was not changed in size.
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
// the first spin might trigger the rebuild, but only the second triggers
|
||||
// it for sure, therefore we spin two times
|
||||
dummy.spin_all(std::chrono::milliseconds(1));
|
||||
dummy.spin_all(std::chrono::milliseconds(1)),
|
||||
std::runtime_error("Couldn't clear the wait set: error not set"));
|
||||
}
|
||||
|
||||
@@ -15,17 +15,20 @@
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
|
||||
#include "test_msgs/message_fixtures.hpp"
|
||||
#include "test_msgs/msg/basic_types.hpp"
|
||||
|
||||
#include "rcl/graph.h"
|
||||
|
||||
#include "rclcpp/executors/single_threaded_executor.hpp"
|
||||
#include "rclcpp/generic_publisher.hpp"
|
||||
#include "rclcpp/generic_subscription.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
@@ -34,6 +37,7 @@
|
||||
|
||||
using namespace ::testing; // NOLINT
|
||||
using namespace rclcpp; // NOLINT
|
||||
using namespace std::chrono_literals; // NOLINT
|
||||
|
||||
class RclcppGenericNodeFixture : public Test
|
||||
{
|
||||
@@ -314,3 +318,133 @@ TEST_F(RclcppGenericNodeFixture, generic_subscription_different_callbacks)
|
||||
ASSERT_TRUE(wait_for(connected, 5s));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(RclcppGenericNodeFixture, disable_enable_subscription_callbacks)
|
||||
{
|
||||
const std::string topic_name = "test_disable_topic";
|
||||
const std::string type = "test_msgs/msg/Strings";
|
||||
const auto qos = rclcpp::QoS(10U);
|
||||
|
||||
std::atomic<int> callback_count{0};
|
||||
auto callback = [&callback_count](std::shared_ptr<const rclcpp::SerializedMessage>) {
|
||||
++callback_count;
|
||||
};
|
||||
|
||||
auto subscription = node_->create_generic_subscription(topic_name, type, qos, callback);
|
||||
auto publisher = node_->create_generic_publisher(topic_name, type, qos);
|
||||
|
||||
// Wait for discovery
|
||||
auto connected = [publisher, subscription]() -> bool {
|
||||
return publisher->get_subscription_count() && subscription->get_publisher_count();
|
||||
};
|
||||
ASSERT_TRUE(wait_for(connected, 10s));
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node_);
|
||||
|
||||
// Publish and verify callback is called
|
||||
publisher->publish(serialize_message<std::string, test_msgs::msg::Strings>("test1"));
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (callback_count.load() == 0 && std::chrono::steady_clock::now() - start < 30s) {
|
||||
executor.spin_some();
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Disable callbacks
|
||||
EXPECT_NO_THROW(subscription->disable_callbacks());
|
||||
|
||||
// Publish again and verify callback is NOT called
|
||||
publisher->publish(serialize_message<std::string, test_msgs::msg::Strings>("test2"));
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
auto initial_count = callback_count.load();
|
||||
while (std::chrono::steady_clock::now() - start < 500ms) {
|
||||
executor.spin_some();
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
EXPECT_EQ(initial_count, callback_count.load()); // Should not increase
|
||||
|
||||
// Enable callbacks
|
||||
EXPECT_NO_THROW(subscription->enable_callbacks());
|
||||
|
||||
// Publish again and verify callback IS called
|
||||
publisher->publish(serialize_message<std::string, test_msgs::msg::Strings>("test3"));
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
while (callback_count.load() == initial_count && std::chrono::steady_clock::now() - start < 30s) {
|
||||
executor.spin_some();
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
EXPECT_EQ(callback_count.load(), initial_count + 1);
|
||||
}
|
||||
|
||||
TEST_F(RclcppGenericNodeFixture, disable_enable_subscription_event_callbacks)
|
||||
{
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_zenoh_cpp") == 0) {
|
||||
GTEST_SKIP() << "rmw_zenoh doesn't support deadline and liveliness events";
|
||||
}
|
||||
const std::string topic_name = "test_event_callbacks_topic";
|
||||
const std::string type = "test_msgs/msg/Strings";
|
||||
const auto qos = rclcpp::QoS(10U).deadline(100ms);
|
||||
|
||||
std::atomic<int> deadline_callback_count{0};
|
||||
std::atomic<int> liveliness_callback_count{0};
|
||||
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
sub_options.event_callbacks.deadline_callback =
|
||||
[&deadline_callback_count](rclcpp::QOSDeadlineRequestedInfo &) {
|
||||
++deadline_callback_count;
|
||||
};
|
||||
|
||||
sub_options.event_callbacks.liveliness_callback =
|
||||
[&liveliness_callback_count](rclcpp::QOSLivelinessChangedInfo &) {
|
||||
++liveliness_callback_count;
|
||||
};
|
||||
|
||||
auto do_nothing = [](std::shared_ptr<const rclcpp::SerializedMessage>) {};
|
||||
|
||||
auto subscription =
|
||||
node_->create_generic_subscription(topic_name, type, qos, do_nothing, sub_options);
|
||||
|
||||
const auto & event_handlers = subscription->get_event_handlers();
|
||||
|
||||
// Initially, both callbacks counters should be zero
|
||||
EXPECT_EQ(deadline_callback_count, 0);
|
||||
EXPECT_EQ(liveliness_callback_count, 0);
|
||||
|
||||
// Execute events and disable all event handlers
|
||||
for (const auto & [event_type, handler] : event_handlers) {
|
||||
if (handler) {
|
||||
const std::shared_ptr<void> data = handler->take_data();
|
||||
handler->execute(data);
|
||||
EXPECT_NO_THROW(handler->disable());
|
||||
}
|
||||
}
|
||||
// Expect both callbacks to have been called once since they should be enabled by default
|
||||
EXPECT_EQ(deadline_callback_count, 1);
|
||||
EXPECT_EQ(liveliness_callback_count, 1);
|
||||
|
||||
// Execute events one more time and enable all event handlers
|
||||
for (const auto & [event_type, handler] : event_handlers) {
|
||||
if (handler) {
|
||||
const std::shared_ptr<void> data = handler->take_data();
|
||||
handler->execute(data);
|
||||
EXPECT_NO_THROW(handler->enable());
|
||||
}
|
||||
}
|
||||
// Expect no change since they were disabled
|
||||
EXPECT_EQ(deadline_callback_count, 1);
|
||||
EXPECT_EQ(liveliness_callback_count, 1);
|
||||
|
||||
// Execute events one more time
|
||||
for (const auto & [event_type, handler] : event_handlers) {
|
||||
if (handler) {
|
||||
const std::shared_ptr<void> data = handler->take_data();
|
||||
handler->execute(data);
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(deadline_callback_count, 2);
|
||||
EXPECT_EQ(liveliness_callback_count, 2);
|
||||
}
|
||||
|
||||
@@ -535,7 +535,7 @@ TEST(TestIntraProcessManager, add_pub_sub) {
|
||||
* - Remove the first subscription from ipm and add a new one.
|
||||
* - Publishes a unique_ptr message with a subscription not requesting ownership.
|
||||
* - The received message is expected to be the same, the first subscription do not receive it.
|
||||
* - Publishes a shared_ptr message with a subscription not requesting ownership.
|
||||
* - Publishes a unique_ptr message with a subscription not requesting ownership.
|
||||
* - The received message is expected to be the same.
|
||||
*/
|
||||
TEST(TestIntraProcessManager, single_subscription) {
|
||||
@@ -587,9 +587,9 @@ TEST(TestIntraProcessManager, single_subscription) {
|
||||
* - One is expected to receive the published message, while the other will receive a copy.
|
||||
* - Publishes a unique_ptr message with 2 subscriptions not requesting ownership.
|
||||
* - Both received messages are expected to be the same as the published one.
|
||||
* - Publishes a shared_ptr message with 2 subscriptions requesting ownership.
|
||||
* - Publishes a unique_ptr message with 2 subscriptions requesting ownership.
|
||||
* - Both received messages are expected to be a copy of the published one.
|
||||
* - Publishes a shared_ptr message with 2 subscriptions not requesting ownership.
|
||||
* - Publishes a unique_ptr message with 2 subscriptions not requesting ownership.
|
||||
* - Both received messages are expected to be the same as the published one.
|
||||
*/
|
||||
TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
@@ -694,9 +694,9 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
* - The 2 subscriptions not requesting ownership are expected to both receive the same copy
|
||||
* of the message, one of the subscription requesting ownership is expected to receive a
|
||||
* different copy, while the last is expected to receive the published message.
|
||||
* - Publishes a shared_ptr message with 1 subscription requesting ownership and 1 not.
|
||||
* - The subscription requesting ownership is expected to receive a copy of the message, while
|
||||
* the other is expected to receive the published message
|
||||
* - Publishes a unique_ptr message with 1 subscription requesting ownership and 1 not.
|
||||
* - The subscription requesting ownership is expected to receive the published message, while
|
||||
* the other is expected to receive a copy of the message
|
||||
*/
|
||||
TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
|
||||
|
||||
@@ -3689,6 +3689,115 @@ TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
|
||||
}, rclcpp::exceptions::InvalidTopicNameError);
|
||||
}
|
||||
|
||||
// test that calling get_clients_info_by_service and get_servers_info_by_service
|
||||
TEST_F(TestNode, get_clients_servers_info_by_service) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
std::string service_name = "test_service_info";
|
||||
std::string fq_service_name = rclcpp::expand_topic_or_service_name(
|
||||
service_name, node->get_name(), node->get_namespace(), true);
|
||||
|
||||
// Lists should be empty
|
||||
EXPECT_TRUE(node->get_clients_info_by_service(fq_service_name).empty());
|
||||
EXPECT_TRUE(node->get_servers_info_by_service(fq_service_name).empty());
|
||||
|
||||
// Add a client
|
||||
rclcpp::QoSInitialization qos_initialization =
|
||||
{
|
||||
RMW_QOS_POLICY_HISTORY_KEEP_ALL,
|
||||
10
|
||||
};
|
||||
rmw_qos_profile_t rmw_qos_profile_default =
|
||||
{
|
||||
RMW_QOS_POLICY_HISTORY_KEEP_ALL,
|
||||
10,
|
||||
RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
|
||||
RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
|
||||
{1, 12345},
|
||||
{20, 9887665},
|
||||
RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC,
|
||||
{5, 23456},
|
||||
false
|
||||
};
|
||||
rclcpp::QoS qos = rclcpp::QoS(qos_initialization, rmw_qos_profile_default);
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(service_name, qos);
|
||||
|
||||
// Wait for the underlying RMW implementation to catch up with graph changes
|
||||
auto client_is_generated =
|
||||
[&]() {return node->get_clients_info_by_service(fq_service_name).size() == 1u;};
|
||||
ASSERT_TRUE(wait_for_event(node, client_is_generated));
|
||||
|
||||
// List should have one item
|
||||
auto clients_info = node->get_clients_info_by_service(fq_service_name);
|
||||
ASSERT_EQ(clients_info.size(), (size_t) 1);
|
||||
// Server list should be empty
|
||||
EXPECT_TRUE(node->get_servers_info_by_service(fq_service_name).empty());
|
||||
|
||||
// Verify client info has the right data.
|
||||
EXPECT_EQ(node->get_name(), clients_info[0].node_name());
|
||||
EXPECT_EQ(node->get_namespace(), clients_info[0].node_namespace());
|
||||
EXPECT_EQ("test_msgs/srv/Empty", clients_info[0].service_type());
|
||||
EXPECT_EQ(rclcpp::EndpointType::Client, clients_info[0].endpoint_type());
|
||||
EXPECT_TRUE(clients_info[0].endpoint_count() == 1 || clients_info[0].endpoint_count() == 2);
|
||||
for(size_t i = 0; i < clients_info[0].endpoint_count(); i++) {
|
||||
auto qos_profile = clients_info[0].qos_profiles()[i].get_rmw_qos_profile();
|
||||
expect_qos_profile_eq(qos.get_rmw_qos_profile(), qos_profile, false);
|
||||
}
|
||||
|
||||
// Add a service server
|
||||
rclcpp::QoSInitialization qos_initialization2 =
|
||||
{
|
||||
RMW_QOS_POLICY_HISTORY_KEEP_LAST,
|
||||
0
|
||||
};
|
||||
rmw_qos_profile_t rmw_qos_profile_default2 =
|
||||
{
|
||||
RMW_QOS_POLICY_HISTORY_KEEP_LAST,
|
||||
0,
|
||||
RMW_QOS_POLICY_RELIABILITY_RELIABLE,
|
||||
RMW_QOS_POLICY_DURABILITY_VOLATILE,
|
||||
{15, 1678},
|
||||
{29, 2345},
|
||||
RMW_QOS_POLICY_LIVELINESS_AUTOMATIC,
|
||||
{5, 23456},
|
||||
false
|
||||
};
|
||||
rclcpp::QoS qos2 = rclcpp::QoS(qos_initialization2, rmw_qos_profile_default2);
|
||||
auto callback = [](test_msgs::srv::Empty_Request::ConstSharedPtr req,
|
||||
test_msgs::srv::Empty_Response::ConstSharedPtr resp) {
|
||||
(void)req;
|
||||
(void)resp;
|
||||
};
|
||||
auto server = node->create_service<test_msgs::srv::Empty>(service_name, callback, qos2);
|
||||
// Wait for the underlying RMW implementation to catch up with graph changes
|
||||
auto server_is_generated =
|
||||
[&]() {return node->get_servers_info_by_service(fq_service_name).size() == 1u;};
|
||||
ASSERT_TRUE(wait_for_event(node, server_is_generated));
|
||||
|
||||
// List should have one item
|
||||
auto servers_info = node->get_servers_info_by_service(fq_service_name);
|
||||
ASSERT_EQ(servers_info.size(), (size_t) 1);
|
||||
// Verify server info has the right data.
|
||||
EXPECT_EQ(node->get_name(), servers_info[0].node_name());
|
||||
EXPECT_EQ(node->get_namespace(), servers_info[0].node_namespace());
|
||||
EXPECT_EQ("test_msgs/srv/Empty", servers_info[0].service_type());
|
||||
EXPECT_EQ(rclcpp::EndpointType::Server, servers_info[0].endpoint_type());
|
||||
EXPECT_TRUE(servers_info[0].endpoint_count() == 1 || servers_info[0].endpoint_count() == 2);
|
||||
for(size_t i = 0; i < servers_info[0].endpoint_count(); i++) {
|
||||
auto qos_profile = servers_info[0].qos_profiles()[i].get_rmw_qos_profile();
|
||||
expect_qos_profile_eq(qos2.get_rmw_qos_profile(), qos_profile, false);
|
||||
}
|
||||
|
||||
// Error cases
|
||||
EXPECT_THROW(
|
||||
{
|
||||
clients_info = node->get_clients_info_by_service("13");
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
EXPECT_THROW(
|
||||
{
|
||||
servers_info = node->get_servers_info_by_service("13");
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, callback_groups) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
size_t num_callback_groups_in_basic_node = 0;
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -20,6 +21,8 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class TestParameterEventHandler : public rclcpp::ParameterEventHandler
|
||||
{
|
||||
public:
|
||||
@@ -443,3 +446,199 @@ TEST_F(TestNode, LastInFirstCallForParameterEventCallbacks)
|
||||
param_handler->remove_parameter_event_callback(h2);
|
||||
EXPECT_EQ(param_handler->num_event_callbacks(), 0UL);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, ConfigureNodesFilterAndCheckAddParameterEventCallback)
|
||||
{
|
||||
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
|
||||
if (rmw_implementation_str != "rmw_fastrtps_cpp" && rmw_implementation_str != "rmw_connextdds") {
|
||||
GTEST_SKIP() << "Content filter is now only supported in FastDDS and ConnextDDS.";
|
||||
}
|
||||
|
||||
const std::string remote_node_name1 = "remote_node1";
|
||||
const std::string remote_node_name2 = "remote_node2";
|
||||
rclcpp::NodeOptions options;
|
||||
options.enable_rosout(false);
|
||||
auto remote_node1 = std::make_shared<rclcpp::Node>(
|
||||
remote_node_name1, options);
|
||||
auto remote_node2 = std::make_shared<rclcpp::Node>(
|
||||
remote_node_name2, options);
|
||||
|
||||
const std::string remote_node1_param_name = "param_node1";
|
||||
const std::string remote_node2_param_name = "param_node2";
|
||||
remote_node1->declare_parameter(remote_node1_param_name, 10);
|
||||
remote_node2->declare_parameter(remote_node2_param_name, "Default");
|
||||
|
||||
std::atomic_bool received_from_remote_node1{false};
|
||||
std::atomic_bool received_from_remote_node2{false};
|
||||
|
||||
auto cb =
|
||||
[&remote_node_name1, &remote_node_name2, &received_from_remote_node1,
|
||||
&received_from_remote_node2]
|
||||
(const rcl_interfaces::msg::ParameterEvent & parm) {
|
||||
if (parm.node == "/" + remote_node_name1) {
|
||||
received_from_remote_node1 = true;
|
||||
} else if (parm.node == "/" + remote_node_name2) {
|
||||
received_from_remote_node2 = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Configure to only receive parameter events from remote_node_name2
|
||||
ASSERT_TRUE(param_handler->configure_nodes_filter({remote_node_name2}));
|
||||
|
||||
auto handler = param_handler->add_parameter_event_callback(cb);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node);
|
||||
executor.add_node(remote_node1);
|
||||
executor.add_node(remote_node2);
|
||||
|
||||
auto wait_param_event = [&executor]
|
||||
(std::chrono::seconds timeout, std::function<bool()> condition = nullptr) {
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (std::chrono::steady_clock::now() - start < timeout) {
|
||||
executor.spin_some();
|
||||
if (condition && condition()) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
std::thread thread(wait_param_event, 1s, nullptr);
|
||||
std::this_thread::sleep_for(100ms);
|
||||
remote_node1->set_parameter(rclcpp::Parameter(remote_node1_param_name, 20));
|
||||
remote_node2->set_parameter(rclcpp::Parameter(remote_node2_param_name, "abc"));
|
||||
thread.join();
|
||||
}
|
||||
|
||||
EXPECT_EQ(received_from_remote_node1, false);
|
||||
EXPECT_EQ(received_from_remote_node2, true);
|
||||
|
||||
// Clear node filter and all parameter events from remote nodes should be received
|
||||
ASSERT_TRUE(param_handler->configure_nodes_filter({}));
|
||||
|
||||
received_from_remote_node1 = false;
|
||||
received_from_remote_node2 = false;
|
||||
|
||||
{
|
||||
std::thread thread(
|
||||
wait_param_event,
|
||||
2s,
|
||||
[&received_from_remote_node1, &received_from_remote_node2]() {
|
||||
if (received_from_remote_node1 && received_from_remote_node2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
std::this_thread::sleep_for(100ms);
|
||||
remote_node1->set_parameter(rclcpp::Parameter(remote_node1_param_name, 30));
|
||||
remote_node2->set_parameter(rclcpp::Parameter(remote_node2_param_name, "def"));
|
||||
thread.join();
|
||||
}
|
||||
|
||||
EXPECT_EQ(received_from_remote_node1, true);
|
||||
EXPECT_EQ(received_from_remote_node2, true);
|
||||
}
|
||||
|
||||
TEST_F(TestNode, ConfigureNodesFilterAndCheckAddParameterCallback)
|
||||
{
|
||||
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
|
||||
if (rmw_implementation_str != "rmw_fastrtps_cpp" && rmw_implementation_str != "rmw_connextdds") {
|
||||
GTEST_SKIP() << "Content filter is now only supported in FastDDS and ConnextDDS.";
|
||||
}
|
||||
|
||||
const std::string remote_node_name1 = "remote_node1";
|
||||
const std::string remote_node_name2 = "remote_node2";
|
||||
rclcpp::NodeOptions options;
|
||||
options.enable_rosout(false);
|
||||
auto remote_node1 = std::make_shared<rclcpp::Node>(
|
||||
remote_node_name1, options);
|
||||
auto remote_node2 = std::make_shared<rclcpp::Node>(
|
||||
remote_node_name2, options);
|
||||
|
||||
const std::string remote_node1_param_name = "param_node1";
|
||||
const std::string remote_node2_param_name = "param_node2";
|
||||
remote_node1->declare_parameter(remote_node1_param_name, 10);
|
||||
remote_node2->declare_parameter(remote_node2_param_name, "Default");
|
||||
|
||||
std::atomic_bool received_from_remote_node1{false};
|
||||
std::atomic_bool received_from_remote_node2{false};
|
||||
|
||||
auto cb_remote_node1 =
|
||||
[&remote_node1_param_name, &received_from_remote_node1]
|
||||
(const rclcpp::Parameter & parm) {
|
||||
if (parm.get_name() == remote_node1_param_name) {
|
||||
received_from_remote_node1 = true;
|
||||
}
|
||||
};
|
||||
|
||||
auto cb_remote_node2 =
|
||||
[&remote_node2_param_name, &received_from_remote_node2]
|
||||
(const rclcpp::Parameter & parm) {
|
||||
if (parm.get_name() == remote_node2_param_name) {
|
||||
received_from_remote_node2 = true;
|
||||
}
|
||||
};
|
||||
|
||||
// Configure to only receive parameter events from remote_node_name2
|
||||
ASSERT_TRUE(param_handler->configure_nodes_filter({remote_node_name2}));
|
||||
|
||||
auto handler1 = param_handler->add_parameter_callback(
|
||||
remote_node1_param_name, cb_remote_node1, remote_node_name1);
|
||||
auto handler2 = param_handler->add_parameter_callback(
|
||||
remote_node2_param_name, cb_remote_node2, remote_node_name2);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node);
|
||||
executor.add_node(remote_node1);
|
||||
executor.add_node(remote_node2);
|
||||
|
||||
auto wait_param_event = [&executor]
|
||||
(std::chrono::seconds timeout, std::function<bool()> condition = nullptr) {
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (std::chrono::steady_clock::now() - start < timeout) {
|
||||
executor.spin_some();
|
||||
if (condition && condition()) {
|
||||
break;
|
||||
}
|
||||
std::this_thread::sleep_for(100ms);
|
||||
}
|
||||
};
|
||||
|
||||
{
|
||||
std::thread thread(wait_param_event, 1s, nullptr);
|
||||
std::this_thread::sleep_for(100ms);
|
||||
remote_node1->set_parameter(rclcpp::Parameter(remote_node1_param_name, 20));
|
||||
remote_node2->set_parameter(rclcpp::Parameter(remote_node2_param_name, "abc"));
|
||||
thread.join();
|
||||
}
|
||||
|
||||
EXPECT_EQ(received_from_remote_node1, false);
|
||||
EXPECT_EQ(received_from_remote_node2, true);
|
||||
|
||||
// Clear node filter and all parameter events from remote nodes should be received
|
||||
ASSERT_TRUE(param_handler->configure_nodes_filter({}));
|
||||
|
||||
received_from_remote_node1 = false;
|
||||
received_from_remote_node2 = false;
|
||||
|
||||
{
|
||||
std::thread thread(
|
||||
wait_param_event,
|
||||
2s,
|
||||
[&received_from_remote_node1, &received_from_remote_node2]() {
|
||||
if (received_from_remote_node1 && received_from_remote_node2) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
std::this_thread::sleep_for(100ms);
|
||||
remote_node1->set_parameter(rclcpp::Parameter(remote_node1_param_name, 30));
|
||||
remote_node2->set_parameter(rclcpp::Parameter(remote_node2_param_name, "def"));
|
||||
thread.join();
|
||||
}
|
||||
|
||||
EXPECT_EQ(received_from_remote_node1, true);
|
||||
EXPECT_EQ(received_from_remote_node2, true);
|
||||
}
|
||||
|
||||
@@ -120,3 +120,32 @@ TEST_F(TestParameterService, describe_parameters) {
|
||||
EXPECT_EQ(0u, parameter_descs.size());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestParameterService, parameter_descriptor) {
|
||||
{
|
||||
rclcpp::ParameterDescription param_description;
|
||||
rclcpp::ParameterValue param_value(1);
|
||||
|
||||
param_description.set_name("int_parameter");
|
||||
param_description.set_type(2);
|
||||
param_description.set_description_text("description");
|
||||
param_description.set_additional_constraints("constraints");
|
||||
param_description.set_read_only(false);
|
||||
param_description.set_integer_description_range(0, 10, 1);
|
||||
|
||||
auto param = param_description.build();
|
||||
param_description.declare_parameter(param_value, node);
|
||||
|
||||
EXPECT_EQ("int_parameter", param.name);
|
||||
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, param.type);
|
||||
EXPECT_EQ("description", param.description);
|
||||
EXPECT_EQ("constraints", param.additional_constraints);
|
||||
EXPECT_EQ(0u, param.read_only);
|
||||
EXPECT_EQ(0u, param.dynamic_typing);
|
||||
EXPECT_EQ(0u, param.integer_range.at(0).from_value);
|
||||
EXPECT_EQ(10, param.integer_range.at(0).to_value);
|
||||
EXPECT_EQ(1, param.integer_range.at(0).step);
|
||||
|
||||
ASSERT_EQ(1, client->get_parameter("int_parameter", 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +219,18 @@ TEST(TestQoS, policy_name_from_kind) {
|
||||
EXPECT_EQ(
|
||||
"LIFESPAN_QOS_POLICY",
|
||||
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_LIFESPAN));
|
||||
|
||||
EXPECT_EQ(
|
||||
"DEPTH_QOS_POLICY",
|
||||
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_DEPTH));
|
||||
|
||||
EXPECT_EQ(
|
||||
"LIVELINESS_LEASE_DURATION_QOS_POLICY",
|
||||
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_LIVELINESS_LEASE_DURATION));
|
||||
|
||||
EXPECT_EQ(
|
||||
"AVOID_ROS_NAMESPACE_CONVENTIONS_QOS_POLICY",
|
||||
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_AVOID_ROS_NAMESPACE_CONVENTIONS));
|
||||
}
|
||||
|
||||
TEST(TestQoS, qos_check_compatible)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
@@ -24,6 +25,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/executors/single_threaded_executor.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "../mocking_utils/patch.hpp"
|
||||
@@ -668,3 +670,212 @@ TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws_intrapro
|
||||
options);},
|
||||
std::runtime_error("Unrecognized IntraProcessSetting value"));
|
||||
}
|
||||
|
||||
/*
|
||||
Testing disable and enable callbacks on subscriptions
|
||||
*/
|
||||
TEST_F(TestSubscription, disable_enable_callbacks)
|
||||
{
|
||||
const std::string topic_name = "~/test_disable";
|
||||
initialize();
|
||||
std::atomic<int> callback_count{0};
|
||||
|
||||
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {
|
||||
++callback_count;
|
||||
};
|
||||
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
sub_options.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10U, callback, sub_options);
|
||||
|
||||
rclcpp::PublisherOptions pub_options;
|
||||
pub_options.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
|
||||
auto pub = node_->create_publisher<test_msgs::msg::Empty>(topic_name, 10U, pub_options);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node_);
|
||||
|
||||
// Wait for discovery
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while ((pub->get_subscription_count() == 0U || sub->get_publisher_count() == 0U) &&
|
||||
std::chrono::steady_clock::now() - start < 10s)
|
||||
{
|
||||
executor.spin_some();
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
|
||||
// Publish and verify callback is called
|
||||
{
|
||||
test_msgs::msg::Empty msg;
|
||||
pub->publish(msg);
|
||||
}
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
while (callback_count.load() == 0 && std::chrono::steady_clock::now() - start < 30s) {
|
||||
executor.spin_some();
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Disable callbacks
|
||||
sub->disable_callbacks();
|
||||
|
||||
// Publish again and verify callback is NOT called
|
||||
{
|
||||
test_msgs::msg::Empty msg;
|
||||
pub->publish(msg);
|
||||
}
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
auto initial_count = callback_count.load();
|
||||
while (std::chrono::steady_clock::now() - start < 500ms) {
|
||||
executor.spin_some();
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
EXPECT_EQ(initial_count, callback_count.load()); // Should not increase
|
||||
|
||||
// Enable callbacks
|
||||
sub->enable_callbacks();
|
||||
|
||||
// Publish again and verify callback IS called
|
||||
{
|
||||
test_msgs::msg::Empty msg;
|
||||
pub->publish(msg);
|
||||
}
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
while (callback_count.load() == initial_count && std::chrono::steady_clock::now() - start < 30s) {
|
||||
executor.spin_some();
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
EXPECT_EQ(callback_count.load(), initial_count + 1);
|
||||
}
|
||||
|
||||
TEST_F(TestSubscription, disable_enable_callbacks_intra_process)
|
||||
{
|
||||
const std::string topic_name = "~/test_disable_ipc";
|
||||
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||
std::atomic<int> callback_count{0};
|
||||
|
||||
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {
|
||||
++callback_count;
|
||||
};
|
||||
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>(topic_name, 10U, callback);
|
||||
auto pub = node_->create_publisher<test_msgs::msg::Empty>(topic_name, 10U);
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node_);
|
||||
|
||||
// Publish and verify callback is called
|
||||
{
|
||||
test_msgs::msg::Empty msg;
|
||||
pub->publish(msg);
|
||||
}
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (callback_count.load() == 0 && std::chrono::steady_clock::now() - start < 30s) {
|
||||
executor.spin_some();
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
EXPECT_EQ(1, callback_count.load());
|
||||
|
||||
// Disable callbacks
|
||||
sub->disable_callbacks();
|
||||
|
||||
// Publish again and verify callback is NOT called
|
||||
{
|
||||
test_msgs::msg::Empty msg;
|
||||
pub->publish(msg);
|
||||
}
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
auto initial_count = callback_count.load();
|
||||
while (std::chrono::steady_clock::now() - start < 500ms) {
|
||||
executor.spin_some();
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
EXPECT_EQ(initial_count, callback_count.load());
|
||||
|
||||
// Enable callbacks
|
||||
sub->enable_callbacks();
|
||||
|
||||
// Publish again and verify callback IS called
|
||||
{
|
||||
test_msgs::msg::Empty msg;
|
||||
pub->publish(msg);
|
||||
}
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
while (callback_count.load() == initial_count && std::chrono::steady_clock::now() - start < 30s) {
|
||||
executor.spin_some();
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
EXPECT_EQ(callback_count.load(), initial_count + 1);
|
||||
}
|
||||
|
||||
TEST_F(TestSubscription, disable_enable_event_callbacks)
|
||||
{
|
||||
initialize();
|
||||
std::atomic<int> deadline_callback_count{0};
|
||||
std::atomic<int> liveliness_callback_count{0};
|
||||
|
||||
rclcpp::SubscriptionOptions subscription_options;
|
||||
subscription_options.event_callbacks.deadline_callback =
|
||||
[&deadline_callback_count](rclcpp::QOSDeadlineRequestedInfo &) {
|
||||
++deadline_callback_count;
|
||||
};
|
||||
|
||||
subscription_options.event_callbacks.liveliness_callback =
|
||||
[&liveliness_callback_count](rclcpp::QOSLivelinessChangedInfo &) {
|
||||
++liveliness_callback_count;
|
||||
};
|
||||
|
||||
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
|
||||
|
||||
const auto qos = rclcpp::QoS(10U).deadline(100ms);
|
||||
|
||||
auto subscription = node_->create_subscription<test_msgs::msg::Empty>(
|
||||
"test_event_callbacks_topic", qos, do_nothing, subscription_options);
|
||||
|
||||
const auto & event_handlers = subscription->get_event_handlers();
|
||||
|
||||
// Initially, both callbacks counters should be zero
|
||||
EXPECT_EQ(deadline_callback_count, 0);
|
||||
EXPECT_EQ(liveliness_callback_count, 0);
|
||||
|
||||
// Execute events and disable all event handlers
|
||||
for (const auto & [event_type, handler] : event_handlers) {
|
||||
if (handler) {
|
||||
const std::shared_ptr<void> data = handler->take_data();
|
||||
handler->execute(data);
|
||||
EXPECT_NO_THROW(handler->disable());
|
||||
}
|
||||
}
|
||||
// Expect both callbacks to have been called once since they should be enabled by default
|
||||
EXPECT_EQ(deadline_callback_count, 1);
|
||||
EXPECT_EQ(liveliness_callback_count, 1);
|
||||
|
||||
// Execute events one more time and enable all event handlers
|
||||
for (const auto & [event_type, handler] : event_handlers) {
|
||||
if (handler) {
|
||||
const std::shared_ptr<void> data = handler->take_data();
|
||||
handler->execute(data);
|
||||
EXPECT_NO_THROW(handler->enable());
|
||||
}
|
||||
}
|
||||
// Expect no change since they were disabled
|
||||
EXPECT_EQ(deadline_callback_count, 1);
|
||||
EXPECT_EQ(liveliness_callback_count, 1);
|
||||
|
||||
// Execute events one more time
|
||||
for (const auto & [event_type, handler] : event_handlers) {
|
||||
if (handler) {
|
||||
const std::shared_ptr<void> data = handler->take_data();
|
||||
handler->execute(data);
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(deadline_callback_count, 2);
|
||||
EXPECT_EQ(liveliness_callback_count, 2);
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ TEST_F(
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<std::string> msg) -> void {
|
||||
std::shared_ptr<const std::string> msg) -> void {
|
||||
is_received = true;
|
||||
EXPECT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
};
|
||||
@@ -230,7 +230,7 @@ TEST_F(
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<std::string> msg,
|
||||
std::shared_ptr<const std::string> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
EXPECT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
@@ -422,7 +422,7 @@ TEST_F(
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<std::string> msg) -> void {
|
||||
std::shared_ptr<const std::string> msg) -> void {
|
||||
is_received = true;
|
||||
EXPECT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
};
|
||||
@@ -440,7 +440,7 @@ TEST_F(
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<std::string> msg,
|
||||
std::shared_ptr<const std::string> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
EXPECT_STREQ(message_data.c_str(), (*msg).c_str());
|
||||
@@ -637,7 +637,7 @@ TEST_F(
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<double> msg) -> void {
|
||||
std::shared_ptr<const double> msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_EQ(message_data, *msg);
|
||||
};
|
||||
@@ -656,7 +656,7 @@ TEST_F(
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<double> msg,
|
||||
std::shared_ptr<const double> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_EQ(message_data, *msg);
|
||||
@@ -862,7 +862,7 @@ TEST_F(
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<double> msg) -> void {
|
||||
std::shared_ptr<const double> msg) -> void {
|
||||
is_received = true;
|
||||
ASSERT_EQ(message_data, *msg);
|
||||
};
|
||||
@@ -882,7 +882,7 @@ TEST_F(
|
||||
bool is_received = false;
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
std::shared_ptr<double> msg,
|
||||
std::shared_ptr<const double> msg,
|
||||
const rclcpp::MessageInfo & message_info) -> void {
|
||||
is_received = true;
|
||||
ASSERT_EQ(message_data, *msg);
|
||||
|
||||
@@ -134,3 +134,32 @@ TEST(TestUtilities, wait_for_last_message) {
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestUtilities, wait_for_message_custom_context) {
|
||||
auto context = std::make_shared<rclcpp::Context>();
|
||||
context->init(0, nullptr);
|
||||
|
||||
auto node_opt = rclcpp::NodeOptions().context(context);
|
||||
auto node = std::make_shared<rclcpp::Node>("wait_for_message_custom_context_node", node_opt);
|
||||
|
||||
using MsgT = test_msgs::msg::Strings;
|
||||
auto pub = node->create_publisher<MsgT>("wait_for_message_topic", 10);
|
||||
|
||||
MsgT out;
|
||||
auto received = false;
|
||||
auto wait = std::async(
|
||||
[&]() {
|
||||
auto ret = rclcpp::wait_for_message(out, node, "wait_for_message_topic", 5s);
|
||||
EXPECT_TRUE(ret);
|
||||
received = true;
|
||||
});
|
||||
|
||||
for (auto i = 0u; i < 10 && received == false; ++i) {
|
||||
pub->publish(*get_messages_strings()[0]);
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
ASSERT_TRUE(received);
|
||||
EXPECT_EQ(out, *get_messages_strings()[0]);
|
||||
|
||||
context->shutdown("test complete");
|
||||
}
|
||||
|
||||
@@ -3,6 +3,20 @@ Changelog for package rclcpp_action
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
30.1.5 (2026-02-09)
|
||||
-------------------
|
||||
* remove default: so that compiler can detect the missing case. (`#3048 <https://github.com/ros2/rclcpp/issues/3048>`_)
|
||||
* Update exception documentation for goal cancellation in ServerGoalHandle (`#3019 <https://github.com/ros2/rclcpp/issues/3019>`_)
|
||||
* Contributors: Andrei Costinescu, Tomoya Fujita
|
||||
|
||||
30.1.4 (2025-12-23)
|
||||
-------------------
|
||||
|
||||
30.1.3 (2025-11-18)
|
||||
-------------------
|
||||
* Fix REP url locations (`#2987 <https://github.com/ros2/rclcpp/issues/2987>`_)
|
||||
* Contributors: Tim Clephas
|
||||
|
||||
30.1.2 (2025-10-21)
|
||||
-------------------
|
||||
* it misses the iterator second to lock the weakptr. (`#2958 <https://github.com/ros2/rclcpp/issues/2958>`_)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
This document is a declaration of software quality for the `rclcpp_action` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
|
||||
This document is a declaration of software quality for the `rclcpp_action` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/).
|
||||
|
||||
# rclcpp_action Quality Declaration
|
||||
|
||||
The package `rclcpp_action` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware.
|
||||
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html).
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://reps.openrobotics.org/rep-2004/).
|
||||
|
||||
## Version Policy [1]
|
||||
|
||||
@@ -53,7 +53,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d
|
||||
|
||||
### Continuous Integration [2.iv]
|
||||
|
||||
All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers)
|
||||
All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers)
|
||||
|
||||
Currently nightly results can be seen here:
|
||||
|
||||
@@ -179,7 +179,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
|
||||
|
||||
## Platform Support [6]
|
||||
|
||||
`rclcpp_action` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them.
|
||||
`rclcpp_action` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them.
|
||||
|
||||
Currently nightly build status can be seen here:
|
||||
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp_action/)
|
||||
@@ -191,4 +191,4 @@ Currently nightly build status can be seen here:
|
||||
|
||||
### Vulnerability Disclosure Policy [7.i]
|
||||
|
||||
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html).
|
||||
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/).
|
||||
|
||||
@@ -210,7 +210,7 @@ public:
|
||||
* This is a terminal state, no more methods should be called on a goal handle after this is
|
||||
* called.
|
||||
*
|
||||
* \throws rclcpp::exceptions::RCLError If the goal is in any state besides executing.
|
||||
* \throws rclcpp::exceptions::RCLError If a cancel request for this goal has not been received.
|
||||
*
|
||||
* \param[in] result_msg the final result to send to clients.
|
||||
*/
|
||||
|
||||
@@ -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>30.1.2</version>
|
||||
<version>30.1.5</version>
|
||||
<description>Adds action APIs for C++.</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -614,10 +614,6 @@ ClientBase::set_on_ready_callback(
|
||||
user_data);
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw std::runtime_error("ClientBase::set_on_ready_callback: Unknown entity type.");
|
||||
break;
|
||||
}
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
|
||||
@@ -908,9 +908,10 @@ ServerBase::set_on_ready_callback(
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
throw std::runtime_error("ServerBase::set_on_ready_callback: Unknown entity type.");
|
||||
break;
|
||||
case EntityType::Expired:
|
||||
{
|
||||
throw std::runtime_error("Expired entity type does not support callbacks");
|
||||
}
|
||||
}
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
|
||||
@@ -2,6 +2,21 @@
|
||||
Changelog for package rclcpp_components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
30.1.5 (2026-02-09)
|
||||
-------------------
|
||||
* Add library dependency to node executable in rclcpp_components_register_node (`#3047 <https://github.com/ros2/rclcpp/issues/3047>`_)
|
||||
* Contributors: YuJin Hong
|
||||
|
||||
30.1.4 (2025-12-23)
|
||||
-------------------
|
||||
* Updated deprecated ament_index_cpp API (`#3011 <https://github.com/ros2/rclcpp/issues/3011>`_)
|
||||
* Contributors: Alejandro Hernández Cordero
|
||||
|
||||
30.1.3 (2025-11-18)
|
||||
-------------------
|
||||
* Fix REP url locations (`#2987 <https://github.com/ros2/rclcpp/issues/2987>`_)
|
||||
* Contributors: Tim Clephas
|
||||
|
||||
30.1.2 (2025-10-21)
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
This document is a declaration of software quality for the `rclcpp_components` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
|
||||
This document is a declaration of software quality for the `rclcpp_components` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/).
|
||||
|
||||
# rclcpp_components Quality Declaration
|
||||
|
||||
The package `rclcpp_components` claims to be in the **Quality Level 1** category.
|
||||
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html).
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://reps.openrobotics.org/rep-2004/).
|
||||
|
||||
## Version Policy [1]
|
||||
|
||||
@@ -53,7 +53,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d
|
||||
|
||||
### Continuous Integration [2.iv]
|
||||
|
||||
All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers)
|
||||
All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers)
|
||||
|
||||
Currently nightly results can be seen here:
|
||||
|
||||
@@ -191,7 +191,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
|
||||
|
||||
## Platform Support [6]
|
||||
|
||||
`rclcpp_components` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them.
|
||||
`rclcpp_components` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them.
|
||||
|
||||
Currently nightly build status can be seen here:
|
||||
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp_components/)
|
||||
@@ -203,4 +203,4 @@ Currently nightly build status can be seen here:
|
||||
|
||||
### Vulnerability Disclosure Policy [7.i]
|
||||
|
||||
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html).
|
||||
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/).
|
||||
|
||||
@@ -30,9 +30,11 @@
|
||||
# :type RESOURCE_INDEX: string
|
||||
# :param NO_UNDEFINED_SYMBOLS: add linker flags to deny undefined symbols
|
||||
# :type NO_UNDEFINED_SYMBOLS: option
|
||||
# :param SKIP_LIBRARY_DEPENDENCY: skip adding dependency on the library target
|
||||
# :type SKIP_LIBRARY_DEPENDENCY: option
|
||||
#
|
||||
macro(rclcpp_components_register_node target)
|
||||
cmake_parse_arguments(ARGS "NO_UNDEFINED_SYMBOLS" "PLUGIN;EXECUTABLE;EXECUTOR;RESOURCE_INDEX" "" ${ARGN})
|
||||
cmake_parse_arguments(ARGS "NO_UNDEFINED_SYMBOLS;SKIP_LIBRARY_DEPENDENCY" "PLUGIN;EXECUTABLE;EXECUTOR;RESOURCE_INDEX" "" ${ARGN})
|
||||
if(ARGS_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "rclcpp_components_register_node() called with unused "
|
||||
"arguments: ${ARGS_UNPARSED_ARGUMENTS}")
|
||||
@@ -97,6 +99,9 @@ macro(rclcpp_components_register_node target)
|
||||
file(GENERATE OUTPUT ${PROJECT_BINARY_DIR}/rclcpp_components/node_main_${node}.cpp
|
||||
INPUT ${PROJECT_BINARY_DIR}/rclcpp_components/node_main_configured_${node}.cpp.in)
|
||||
add_executable(${node} ${PROJECT_BINARY_DIR}/rclcpp_components/node_main_${node}.cpp)
|
||||
if(NOT ARGS_SKIP_LIBRARY_DEPENDENCY)
|
||||
add_dependencies(${node} ${target})
|
||||
endif()
|
||||
target_link_libraries(${node}
|
||||
class_loader::class_loader
|
||||
rclcpp::rclcpp
|
||||
|
||||
@@ -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>30.1.2</version>
|
||||
<version>30.1.5</version>
|
||||
<description>Package containing tools for dynamically loadable components</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -93,17 +93,13 @@ std::vector<ComponentManager::ComponentResource>
|
||||
ComponentManager::get_component_resources(
|
||||
const std::string & package_name, const std::string & resource_index) const
|
||||
{
|
||||
std::string content;
|
||||
std::string base_path;
|
||||
if (
|
||||
!ament_index_cpp::get_resource(
|
||||
resource_index, package_name, content, &base_path))
|
||||
{
|
||||
auto result = ament_index_cpp::get_resource(resource_index, package_name);
|
||||
if (result.resourcePath == std::nullopt) {
|
||||
throw ComponentManagerException("Could not find requested resource in ament index");
|
||||
}
|
||||
|
||||
std::vector<ComponentResource> resources;
|
||||
std::vector<std::string> lines = rcpputils::split(content, '\n', true);
|
||||
std::vector<std::string> lines = rcpputils::split(result.contents, '\n', true);
|
||||
for (const auto & line : lines) {
|
||||
std::vector<std::string> parts = rcpputils::split(line, ';');
|
||||
if (parts.size() != 2) {
|
||||
@@ -112,7 +108,7 @@ ComponentManager::get_component_resources(
|
||||
|
||||
std::filesystem::path library_path = parts[1];
|
||||
if (!library_path.is_absolute()) {
|
||||
library_path = (base_path / library_path);
|
||||
library_path = (result.resourcePath.value() / library_path);
|
||||
}
|
||||
resources.push_back({parts[0], library_path.string()});
|
||||
}
|
||||
|
||||
@@ -2,6 +2,20 @@
|
||||
Changelog for package rclcpp_lifecycle
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
30.1.5 (2026-02-09)
|
||||
-------------------
|
||||
* Compatiblity with 'Populate Transitions' `ros2/rcl#1269 <https://github.com/ros2/rcl/issues/1269>`_ (`#2967 <https://github.com/ros2/rclcpp/issues/2967>`_)
|
||||
* Contributors: Jasper van Brakel
|
||||
|
||||
30.1.4 (2025-12-23)
|
||||
-------------------
|
||||
|
||||
30.1.3 (2025-11-18)
|
||||
-------------------
|
||||
* add : get clients, servers info (`#2569 <https://github.com/ros2/rclcpp/issues/2569>`_)
|
||||
* Fix REP url locations (`#2987 <https://github.com/ros2/rclcpp/issues/2987>`_)
|
||||
* Add get_parameter_or overload returning value or alternative (`#2973 <https://github.com/ros2/rclcpp/issues/2973>`_)
|
||||
* Contributors: Minju, Lee, Tim Clephas, Zheng Qu
|
||||
|
||||
30.1.2 (2025-10-21)
|
||||
-------------------
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
This document is a declaration of software quality for the `rclcpp_lifecycle` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
|
||||
This document is a declaration of software quality for the `rclcpp_lifecycle` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/).
|
||||
|
||||
# rclcpp_lifecycle Quality Declaration
|
||||
|
||||
The package `rclcpp_lifecycle` claims to be in the **Quality Level 1** category when used with a **Quality Level 1** middleware.
|
||||
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html).
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://reps.openrobotics.org/rep-2004/).
|
||||
|
||||
## Version Policy [1]
|
||||
|
||||
@@ -53,7 +53,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d
|
||||
|
||||
### Continuous Integration [2.iv]
|
||||
|
||||
All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers)
|
||||
All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers)
|
||||
|
||||
Currently nightly results can be seen here:
|
||||
|
||||
@@ -191,7 +191,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
|
||||
|
||||
## Platform Support [6]
|
||||
|
||||
`rclcpp_lifecycle` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them.
|
||||
`rclcpp_lifecycle` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them.
|
||||
|
||||
Currently nightly build status can be seen here:
|
||||
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp_lifecycle/)
|
||||
@@ -203,4 +203,4 @@ Currently nightly build status can be seen here:
|
||||
|
||||
### Vulnerability Disclosure Policy [7.i]
|
||||
|
||||
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html).
|
||||
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/).
|
||||
|
||||
@@ -486,6 +486,16 @@ public:
|
||||
ParameterT & value,
|
||||
const ParameterT & alternative_value) const;
|
||||
|
||||
/// Return the parameter value, or the "alternative_value" if not set.
|
||||
/**
|
||||
* \sa rclcpp::Node::get_parameter_or
|
||||
*/
|
||||
template<typename ParameterT>
|
||||
ParameterT
|
||||
get_parameter_or(
|
||||
const std::string & name,
|
||||
const ParameterT & alternative_value) const;
|
||||
|
||||
/// Return the parameters by the given parameter names.
|
||||
/**
|
||||
* \sa rclcpp::Node::get_parameters
|
||||
@@ -693,6 +703,22 @@ public:
|
||||
std::vector<rclcpp::TopicEndpointInfo>
|
||||
get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle = false) const;
|
||||
|
||||
/// Return the service endpoint information about clients on a given service.
|
||||
/**
|
||||
* \sa rclcpp::Node::get_clients_info_by_service
|
||||
*/
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
get_clients_info_by_service(const std::string & service_name, bool no_mangle = false) const;
|
||||
|
||||
/// Return the service endpoint information about server on a given service.
|
||||
/**
|
||||
* \sa rclcpp::Node::get_servers_info_by_service
|
||||
*/
|
||||
RCLCPP_LIFECYCLE_PUBLIC
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
get_servers_info_by_service(const std::string & service_name, bool no_mangle = false) const;
|
||||
|
||||
/// Return a graph event, which will be set anytime a graph change occurs.
|
||||
/* The graph Event object is a loan which must be returned.
|
||||
* The Event object is scoped and therefore to return the load just let it go
|
||||
|
||||
@@ -306,5 +306,16 @@ LifecycleNode::get_parameter_or(
|
||||
return got_parameter;
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
ParameterT
|
||||
LifecycleNode::get_parameter_or(
|
||||
const std::string & name,
|
||||
const ParameterT & alternative_value) const
|
||||
{
|
||||
ParameterT parameter;
|
||||
get_parameter_or(name, parameter, alternative_value);
|
||||
return parameter;
|
||||
}
|
||||
|
||||
} // namespace rclcpp_lifecycle
|
||||
#endif // RCLCPP_LIFECYCLE__LIFECYCLE_NODE_IMPL_HPP_
|
||||
|
||||
@@ -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>30.1.2</version>
|
||||
<version>30.1.5</version>
|
||||
<description>Package containing a prototype for lifecycle implementation</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -122,7 +122,7 @@ LifecycleNode::LifecycleNode(
|
||||
)),
|
||||
node_waitables_(new rclcpp::node_interfaces::NodeWaitables(node_base_.get())),
|
||||
node_options_(options),
|
||||
impl_(new LifecycleNodeInterfaceImpl(node_base_, node_services_, node_logging_))
|
||||
impl_(new LifecycleNodeInterfaceImpl(node_base_, node_services_, node_logging_, node_clock_))
|
||||
{
|
||||
impl_->init(enable_communication_interface);
|
||||
|
||||
@@ -422,6 +422,18 @@ LifecycleNode::get_subscriptions_info_by_topic(const std::string & topic_name, b
|
||||
return node_graph_->get_subscriptions_info_by_topic(topic_name, no_mangle);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
LifecycleNode::get_clients_info_by_service(const std::string & service_name, bool no_mangle) const
|
||||
{
|
||||
return node_graph_->get_clients_info_by_service(service_name, no_mangle);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::ServiceEndpointInfo>
|
||||
LifecycleNode::get_servers_info_by_service(const std::string & service_name, bool no_mangle) const
|
||||
{
|
||||
return node_graph_->get_servers_info_by_service(service_name, no_mangle);
|
||||
}
|
||||
|
||||
void
|
||||
LifecycleNode::for_each_callback_group(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::CallbackGroupFunction & func)
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_logging_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_services_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_clock_interface.hpp"
|
||||
|
||||
#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
|
||||
|
||||
@@ -52,10 +53,12 @@ namespace rclcpp_lifecycle
|
||||
LifecycleNode::LifecycleNodeInterfaceImpl::LifecycleNodeInterfaceImpl(
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base_interface,
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeServicesInterface> node_services_interface,
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface> node_logging_interface)
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface> node_logging_interface,
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeClockInterface> node_clock_interface)
|
||||
: node_base_interface_(node_base_interface),
|
||||
node_services_interface_(node_services_interface),
|
||||
node_logging_interface_(node_logging_interface)
|
||||
node_logging_interface_(node_logging_interface),
|
||||
node_clock_interface_(node_clock_interface)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -86,6 +89,8 @@ LifecycleNode::LifecycleNodeInterfaceImpl::init(bool enable_communication_interf
|
||||
state_machine_options.enable_com_interface = enable_communication_interface;
|
||||
state_machine_options.allocator = node_options->allocator;
|
||||
|
||||
rcl_clock_t * clock = node_clock_interface_->get_clock()->get_clock_handle();
|
||||
|
||||
// The call to initialize the state machine takes
|
||||
// currently five different typesupports for all publishers/services
|
||||
// created within the RCL_LIFECYCLE structure.
|
||||
@@ -97,6 +102,7 @@ LifecycleNode::LifecycleNodeInterfaceImpl::init(bool enable_communication_interf
|
||||
rcl_ret_t ret = rcl_lifecycle_state_machine_init(
|
||||
&state_machine_,
|
||||
node_handle,
|
||||
clock,
|
||||
ROSIDL_GET_MSG_TYPE_SUPPORT(lifecycle_msgs, msg, TransitionEvent),
|
||||
rosidl_typesupport_cpp::get_service_type_support_handle<ChangeStateSrv>(),
|
||||
rosidl_typesupport_cpp::get_service_type_support_handle<GetStateSrv>(),
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_logging_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_services_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_clock_interface.hpp"
|
||||
|
||||
#include "rclcpp_lifecycle/node_interfaces/lifecycle_node_interface.hpp"
|
||||
|
||||
@@ -54,7 +55,8 @@ public:
|
||||
LifecycleNodeInterfaceImpl(
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface> node_base_interface,
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeServicesInterface> node_services_interface,
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface> node_logging_interface);
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface> node_logging_interface,
|
||||
std::shared_ptr<rclcpp::node_interfaces::NodeClockInterface> node_clock_interface);
|
||||
|
||||
~LifecycleNodeInterfaceImpl();
|
||||
|
||||
@@ -155,6 +157,7 @@ private:
|
||||
using NodeBasePtr = std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface>;
|
||||
using NodeServicesPtr = std::shared_ptr<rclcpp::node_interfaces::NodeServicesInterface>;
|
||||
using NodeLoggingPtr = std::shared_ptr<rclcpp::node_interfaces::NodeLoggingInterface>;
|
||||
using NodeClockPtr = std::shared_ptr<rclcpp::node_interfaces::NodeClockInterface>;
|
||||
using ChangeStateSrvPtr = std::shared_ptr<rclcpp::Service<ChangeStateSrv>>;
|
||||
using GetStateSrvPtr = std::shared_ptr<rclcpp::Service<GetStateSrv>>;
|
||||
using GetAvailableStatesSrvPtr =
|
||||
@@ -167,6 +170,7 @@ private:
|
||||
NodeBasePtr node_base_interface_;
|
||||
NodeServicesPtr node_services_interface_;
|
||||
NodeLoggingPtr node_logging_interface_;
|
||||
NodeClockPtr node_clock_interface_;
|
||||
ChangeStateSrvPtr srv_change_state_;
|
||||
GetStateSrvPtr srv_get_state_;
|
||||
GetAvailableStatesSrvPtr srv_get_available_states_;
|
||||
|
||||
@@ -840,6 +840,26 @@ TEST_F(TestDefaultStateMachine, check_parameters) {
|
||||
EXPECT_TRUE(parameter.as_bool());
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, test_get_parameter_or) {
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
|
||||
const std::string param_name = "test_param";
|
||||
int param_int = -999;
|
||||
|
||||
// Parameter does not exist, should return "or" value
|
||||
EXPECT_FALSE(test_node->get_parameter_or(param_name, param_int, 123));
|
||||
EXPECT_EQ(param_int, 123);
|
||||
EXPECT_EQ(test_node->get_parameter_or(param_name, 456), 456);
|
||||
|
||||
// Declare param_int
|
||||
test_node->declare_parameter(param_name, rclcpp::ParameterValue(789));
|
||||
|
||||
// Parameter exists, should return existing value
|
||||
EXPECT_TRUE(test_node->get_parameter_or(param_name, param_int, 123));
|
||||
EXPECT_EQ(param_int, 789);
|
||||
EXPECT_EQ(test_node->get_parameter_or(param_name, 456), 789);
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, test_getters) {
|
||||
auto test_node = std::make_shared<EmptyLifecycleNode>("testnode");
|
||||
auto options = test_node->get_node_options();
|
||||
@@ -922,6 +942,12 @@ TEST_F(TestDefaultStateMachine, test_graph_services) {
|
||||
EXPECT_EQ(1u, test_node->count_services("/testnode/get_available_transitions"));
|
||||
EXPECT_EQ(1u, test_node->count_services("/testnode/get_state"));
|
||||
EXPECT_EQ(1u, test_node->count_services("/testnode/get_transition_graph"));
|
||||
|
||||
auto clients_info =
|
||||
test_node->get_clients_info_by_service("/testnode/change_state");
|
||||
EXPECT_EQ(0u, clients_info.size());
|
||||
auto servers_info = test_node->get_servers_info_by_service("/testnode/change_state");
|
||||
EXPECT_EQ(1u, servers_info.size());
|
||||
}
|
||||
|
||||
TEST_F(TestDefaultStateMachine, test_graph_services_by_node) {
|
||||
|
||||
Reference in New Issue
Block a user