Compare commits
91 Commits
runtime_in
...
16.0.18
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
035777622b | ||
|
|
a490ca1418 | ||
|
|
fea542a131 | ||
|
|
78bc4734df | ||
|
|
6040d745e7 | ||
|
|
66dbc789bc | ||
|
|
41182a9720 | ||
|
|
bc14074e3b | ||
|
|
c42bb23a52 | ||
|
|
4e834faf91 | ||
|
|
2ba0b01d09 | ||
|
|
b6238def58 | ||
|
|
36d0aee198 | ||
|
|
3c5631b4fe | ||
|
|
a9d1abe402 | ||
|
|
e69382c357 | ||
|
|
632a41e6fa | ||
|
|
e41abc37f3 | ||
|
|
81b628d4e6 | ||
|
|
443b69b6e1 | ||
|
|
0036533e94 | ||
|
|
76cdd45da3 | ||
|
|
8e19cbaa14 | ||
|
|
75b8684b86 | ||
|
|
5a0c24c0dd | ||
|
|
4a5bbfa42f | ||
|
|
5237763f7d | ||
|
|
6212355775 | ||
|
|
a0e2240ca3 | ||
|
|
c4e82ddabb | ||
|
|
f43d4edc6b | ||
|
|
53eed44771 | ||
|
|
b6cd8393db | ||
|
|
1b040e7df8 | ||
|
|
c751dfb76b | ||
|
|
493fe2b5d5 | ||
|
|
6084057f89 | ||
|
|
d3e6254ff1 | ||
|
|
19773973a8 | ||
|
|
99f1d8d124 | ||
|
|
1a353f09c0 | ||
|
|
e9edc3fda0 | ||
|
|
82ec3f000e | ||
|
|
28de27e4ff | ||
|
|
9be01cf400 | ||
|
|
e97d4e8616 | ||
|
|
6737773a5d | ||
|
|
32f19615bb | ||
|
|
595badb55c | ||
|
|
f95ac7cdda | ||
|
|
844ab6b6c5 | ||
|
|
ecf4ac4b2b | ||
|
|
058b54f7c7 | ||
|
|
0f9604d1b7 | ||
|
|
4fb589eea5 | ||
|
|
c1cfcb6880 | ||
|
|
47c977d1bc | ||
|
|
3594381e04 | ||
|
|
f279b707fe | ||
|
|
2c8d2aa453 | ||
|
|
47712ecf58 | ||
|
|
24f059c5aa | ||
|
|
c1bf0d382e | ||
|
|
8709146df8 | ||
|
|
adfc546408 | ||
|
|
37f38e30a9 | ||
|
|
0f6b5449f6 | ||
|
|
724b4588ec | ||
|
|
2ae824e8e8 | ||
|
|
689e510cf0 | ||
|
|
ec4d00e405 | ||
|
|
52327dd3a3 | ||
|
|
25263e838d | ||
|
|
a75baa6b26 | ||
|
|
5f7485f4fd | ||
|
|
613d192cd6 | ||
|
|
00ef09cbf3 | ||
|
|
b2b7bdeac1 | ||
|
|
19a666f1c9 | ||
|
|
c8ac675035 | ||
|
|
4196a2a8b4 | ||
|
|
9171122eae | ||
|
|
ce13f1afba | ||
|
|
df08474d38 | ||
|
|
f9050cd666 | ||
|
|
33cbd76c07 | ||
|
|
ae8b033ae0 | ||
|
|
4fa3489cfd | ||
|
|
7f575103d8 | ||
|
|
166007dde3 | ||
|
|
cf2a27805e |
@@ -2,6 +2,122 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
16.0.18 (2026-02-09)
|
||||
--------------------
|
||||
* print warning message on owner node if the parameter operation fails. (backport `#3037 <https://github.com/ros2/rclcpp/issues/3037>`_) (`#3040 <https://github.com/ros2/rclcpp/issues/3040>`_)
|
||||
* fix context in wait for message wait set (`#3030 <https://github.com/ros2/rclcpp/issues/3030>`_) (`#3033 <https://github.com/ros2/rclcpp/issues/3033>`_)
|
||||
* Improve the robustness of the TopicEndpointInfo constructor (backport `#3013 <https://github.com/ros2/rclcpp/issues/3013>`_) (`#3016 <https://github.com/ros2/rclcpp/issues/3016>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.17 (2025-12-23)
|
||||
--------------------
|
||||
* Unified Node Interfaces: Add const version of get_node_x_interface() (`#3006 <https://github.com/ros2/rclcpp/issues/3006>`_) (`#3010 <https://github.com/ros2/rclcpp/issues/3010>`_)
|
||||
* [Humble] Implement Unified Node Interface (NodeInterfaces class) (backport `#2041 <https://github.com/ros2/rclcpp/issues/2041>`_) (`#3002 <https://github.com/ros2/rclcpp/issues/3002>`_)
|
||||
* remove I/O from signal handler. (`#3000 <https://github.com/ros2/rclcpp/issues/3000>`_) (`#3005 <https://github.com/ros2/rclcpp/issues/3005>`_)
|
||||
* correct test function descriptions (backport `#2970 <https://github.com/ros2/rclcpp/issues/2970>`_) (`#2994 <https://github.com/ros2/rclcpp/issues/2994>`_)
|
||||
* Contributors: fabianhirmann, mergify[bot]
|
||||
|
||||
16.0.16 (2025-11-18)
|
||||
--------------------
|
||||
* Fix REP url locations (backport `#2987 <https://github.com/ros2/rclcpp/issues/2987>`_) (`#2991 <https://github.com/ros2/rclcpp/issues/2991>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.15 (2025-09-11)
|
||||
--------------------
|
||||
* Allow for implicitly convertable loggers as well (`#2922 <https://github.com/ros2/rclcpp/issues/2922>`_) (`#2936 <https://github.com/ros2/rclcpp/issues/2936>`_) (`#2938 <https://github.com/ros2/rclcpp/issues/2938>`_)
|
||||
* Fix: improve exception context for parameter_value_from (backport `#2917 <https://github.com/ros2/rclcpp/issues/2917>`_) (`#2921 <https://github.com/ros2/rclcpp/issues/2921>`_)
|
||||
* Removed warning test_qos (`#2859 <https://github.com/ros2/rclcpp/issues/2859>`_) (`#2925 <https://github.com/ros2/rclcpp/issues/2925>`_)
|
||||
* Add qos parameter for wait_for_message function (`#2903 <https://github.com/ros2/rclcpp/issues/2903>`_) (`#2907 <https://github.com/ros2/rclcpp/issues/2907>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.14 (2025-07-16)
|
||||
--------------------
|
||||
* fix test_publisher_with_system_default_qos. (backport `#2881 <https://github.com/ros2/rclcpp/issues/2881>`_) (`#2882 <https://github.com/ros2/rclcpp/issues/2882>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.13 (2025-06-23)
|
||||
--------------------
|
||||
* Fix for memory leaks in rclcpp::SerializedMessage (`#2861 <https://github.com/ros2/rclcpp/issues/2861>`_) (`#2865 <https://github.com/ros2/rclcpp/issues/2865>`_)
|
||||
* Added missing chrono includes (backport `#2854 <https://github.com/ros2/rclcpp/issues/2854>`_) (`#2857 <https://github.com/ros2/rclcpp/issues/2857>`_)
|
||||
* QoSInitialization::from_rmw does not validate invalid history policy … (backport `#2841 <https://github.com/ros2/rclcpp/issues/2841>`_) (`#2844 <https://github.com/ros2/rclcpp/issues/2844>`_)
|
||||
* throws std::invalid_argument if ParameterEvent is NULL. (`#2814 <https://github.com/ros2/rclcpp/issues/2814>`_) (`#2824 <https://github.com/ros2/rclcpp/issues/2824>`_)
|
||||
* remove redundant typesupport check in serialization module (`#2808 <https://github.com/ros2/rclcpp/issues/2808>`_) (`#2816 <https://github.com/ros2/rclcpp/issues/2816>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.12 (2025-03-25)
|
||||
--------------------
|
||||
* doc: Added warning to not instantiate Clock directly with RCL_ROS_TIME (`#2768 <https://github.com/ros2/rclcpp/issues/2768>`_) (`#2770 <https://github.com/ros2/rclcpp/issues/2770>`_)
|
||||
* apply actual QoS from rmw to the IPC publisher. (backport `#2707 <https://github.com/ros2/rclcpp/issues/2707>`_) (`#2711 <https://github.com/ros2/rclcpp/issues/2711>`_)
|
||||
* Adding in topic name to logging on IPC issues (`#2706 <https://github.com/ros2/rclcpp/issues/2706>`_) (`#2709 <https://github.com/ros2/rclcpp/issues/2709>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.11 (2024-11-25)
|
||||
--------------------
|
||||
* Fix subscription.is_serialized() for callbacks with message info (`#1950 <https://github.com/ros2/rclcpp/issues/1950>`_) (`#2622 <https://github.com/ros2/rclcpp/issues/2622>`_)
|
||||
* Use the same context for the specified node in rclcpp::spin functions… (`#2618 <https://github.com/ros2/rclcpp/issues/2618>`_) (`#2620 <https://github.com/ros2/rclcpp/issues/2620>`_)
|
||||
* Contributors: mergify[bot], roscan-tech
|
||||
|
||||
16.0.10 (2024-07-26)
|
||||
--------------------
|
||||
* Add test creating two content filter topics with the same topic name (`#2546 <https://github.com/ros2/rclcpp/issues/2546>`_) (`#2549 <https://github.com/ros2/rclcpp/issues/2549>`_) (`#2551 <https://github.com/ros2/rclcpp/issues/2551>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.9 (2024-05-15)
|
||||
-------------------
|
||||
* Fix logging macros to build with msvc and cpp20 (`#2063 <https://github.com/ros2/rclcpp/issues/2063>`_) (`#2529 <https://github.com/ros2/rclcpp/issues/2529>`_)
|
||||
* address ambiguous auto variable. (`#2481 <https://github.com/ros2/rclcpp/issues/2481>`_) (`#2485 <https://github.com/ros2/rclcpp/issues/2485>`_)
|
||||
* Fix clang warning: bugprone-use-after-move (`#2116 <https://github.com/ros2/rclcpp/issues/2116>`_) (`#2459 <https://github.com/ros2/rclcpp/issues/2459>`_)
|
||||
* Contributors: Tamaki Nishino, mergify[bot]
|
||||
|
||||
16.0.8 (2024-01-24)
|
||||
-------------------
|
||||
* Add missing stdexcept include (`#2186 <https://github.com/ros2/rclcpp/issues/2186>`_) (`#2394 <https://github.com/ros2/rclcpp/issues/2394>`_)
|
||||
* Contributors: gentoo90
|
||||
|
||||
16.0.7 (2023-11-13)
|
||||
-------------------
|
||||
* Disable the loaned messages inside the executor. (backport `#2335 <https://github.com/ros2/rclcpp/issues/2335>`_) (`#2364 <https://github.com/ros2/rclcpp/issues/2364>`_)
|
||||
* Add missing 'enable_rosout' comments (`#2345 <https://github.com/ros2/rclcpp/issues/2345>`_) (`#2347 <https://github.com/ros2/rclcpp/issues/2347>`_)
|
||||
* address rate related flaky tests. (`#2329 <https://github.com/ros2/rclcpp/issues/2329>`_) (`#2342 <https://github.com/ros2/rclcpp/issues/2342>`_)
|
||||
* Update SignalHandler get_global_signal_handler to avoid complex types in static memory (`#2316 <https://github.com/ros2/rclcpp/issues/2316>`_) (`#2321 <https://github.com/ros2/rclcpp/issues/2321>`_)
|
||||
* Fix C++20 allocator construct deprecation (`#2292 <https://github.com/ros2/rclcpp/issues/2292>`_) (`#2319 <https://github.com/ros2/rclcpp/issues/2319>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.6 (2023-09-19)
|
||||
-------------------
|
||||
* Topic correct typeadapter deduction (`#2294 <https://github.com/ros2/rclcpp/issues/2294>`_) (`#2297 <https://github.com/ros2/rclcpp/issues/2297>`_)
|
||||
* check thread whether joinable before join (`#2019 <https://github.com/ros2/rclcpp/issues/2019>`_) (`#2275 <https://github.com/ros2/rclcpp/issues/2275>`_)
|
||||
* Do not crash Executor when send_response fails due to client failure. (`#2276 <https://github.com/ros2/rclcpp/issues/2276>`_) (`#2280 <https://github.com/ros2/rclcpp/issues/2280>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.5 (2023-07-17)
|
||||
-------------------
|
||||
* warning: comparison of integer expressions of different signedness (`#2219 <https://github.com/ros2/rclcpp/issues/2219>`_) (`#2223 <https://github.com/ros2/rclcpp/issues/2223>`_)
|
||||
* Trigger the intraprocess guard condition with data (`#2164 <https://github.com/ros2/rclcpp/issues/2164>`_) (`#2167 <https://github.com/ros2/rclcpp/issues/2167>`_)
|
||||
* Implement validity checks for rclcpp::Clock (`#2040 <https://github.com/ros2/rclcpp/issues/2040>`_) (`#2210 <https://github.com/ros2/rclcpp/issues/2210>`_)
|
||||
* Contributors: Tomoya Fujita, mergify[bot]
|
||||
|
||||
16.0.4 (2023-04-25)
|
||||
-------------------
|
||||
* use allocator via init_options argument. (`#2129 <https://github.com/ros2/rclcpp/issues/2129>`_) (`#2131 <https://github.com/ros2/rclcpp/issues/2131>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.3 (2023-01-10)
|
||||
-------------------
|
||||
* Fix SharedFuture from async_send_request never becomes valid (`#2044 <https://github.com/ros2/rclcpp/issues/2044>`_) (`#2076 <https://github.com/ros2/rclcpp/issues/2076>`_)
|
||||
* do not throw exception if trying to dequeue an empty intra-process buffer (`#2061 <https://github.com/ros2/rclcpp/issues/2061>`_) (`#2070 <https://github.com/ros2/rclcpp/issues/2070>`_)
|
||||
* fix nullptr dereference in prune_requests_older_than (`#2008 <https://github.com/ros2/rclcpp/issues/2008>`_) (`#2065 <https://github.com/ros2/rclcpp/issues/2065>`_)
|
||||
* Fix bug that a callback not reached (`#1640 <https://github.com/ros2/rclcpp/issues/1640>`_) (`#2033 <https://github.com/ros2/rclcpp/issues/2033>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.2 (2022-11-07)
|
||||
-------------------
|
||||
* fix mismatched issue if using zero_allocate (`#1995 <https://github.com/ros2/rclcpp/issues/1995>`_) (`#2026 <https://github.com/ros2/rclcpp/issues/2026>`_)
|
||||
* use regex for wildcard matching (backport `#1839 <https://github.com/ros2/rclcpp/issues/1839>`_) (`#1986 <https://github.com/ros2/rclcpp/issues/1986>`_)
|
||||
* Drop wrong template specialization (`#1926 <https://github.com/ros2/rclcpp/issues/1926>`_) (`#1937 <https://github.com/ros2/rclcpp/issues/1937>`_)
|
||||
* Add statistics for handle_loaned_message (`#1927 <https://github.com/ros2/rclcpp/issues/1927>`_) (`#1932 <https://github.com/ros2/rclcpp/issues/1932>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.1 (2022-04-13)
|
||||
-------------------
|
||||
* remove DEFINE_CONTENT_FILTER cmake option (`#1914 <https://github.com/ros2/rclcpp/issues/1914>`_)
|
||||
|
||||
@@ -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 Quality Categories in REP-2004](https://www.ros.org/reps/rep-2004.html) of the ROS2 developer guide.
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://reps.openrobotics.org/rep-2004/) of the ROS2 developer guide.
|
||||
|
||||
## 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://gitlab
|
||||
|
||||
## 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,6 +15,7 @@
|
||||
#ifndef RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_
|
||||
#define RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_
|
||||
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
|
||||
#include "rcl/allocator.h"
|
||||
@@ -39,6 +40,22 @@ void * retyped_allocate(size_t size, void * untyped_allocator)
|
||||
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
|
||||
}
|
||||
|
||||
template<typename Alloc>
|
||||
void * retyped_zero_allocate(size_t number_of_elem, size_t size_of_elem, void * untyped_allocator)
|
||||
{
|
||||
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
|
||||
if (!typed_allocator) {
|
||||
throw std::runtime_error("Received incorrect allocator type");
|
||||
}
|
||||
size_t size = number_of_elem * size_of_elem;
|
||||
void * allocated_memory =
|
||||
std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
|
||||
if (allocated_memory) {
|
||||
std::memset(allocated_memory, 0, size);
|
||||
}
|
||||
return allocated_memory;
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
void retyped_deallocate(void * untyped_pointer, void * untyped_allocator)
|
||||
{
|
||||
@@ -73,6 +90,7 @@ rcl_allocator_t get_rcl_allocator(Alloc & allocator)
|
||||
rcl_allocator_t rcl_allocator = rcl_get_default_allocator();
|
||||
#ifndef _WIN32
|
||||
rcl_allocator.allocate = &retyped_allocate<Alloc>;
|
||||
rcl_allocator.zero_allocate = &retyped_zero_allocate<Alloc>;
|
||||
rcl_allocator.deallocate = &retyped_deallocate<T, Alloc>;
|
||||
rcl_allocator.reallocate = &retyped_reallocate<T, Alloc>;
|
||||
rcl_allocator.state = &allocator;
|
||||
|
||||
@@ -950,7 +950,13 @@ public:
|
||||
std::holds_alternative<UniquePtrSerializedMessageCallback>(callback_variant_) ||
|
||||
std::holds_alternative<SharedConstPtrSerializedMessageCallback>(callback_variant_) ||
|
||||
std::holds_alternative<ConstRefSharedConstPtrSerializedMessageCallback>(callback_variant_) ||
|
||||
std::holds_alternative<SharedPtrSerializedMessageCallback>(callback_variant_);
|
||||
std::holds_alternative<SharedPtrSerializedMessageCallback>(callback_variant_) ||
|
||||
std::holds_alternative<ConstRefSerializedMessageWithInfoCallback>(callback_variant_) ||
|
||||
std::holds_alternative<UniquePtrSerializedMessageWithInfoCallback>(callback_variant_) ||
|
||||
std::holds_alternative<SharedConstPtrSerializedMessageWithInfoCallback>(callback_variant_) ||
|
||||
std::holds_alternative<ConstRefSharedConstPtrSerializedMessageWithInfoCallback>(
|
||||
callback_variant_) ||
|
||||
std::holds_alternative<SharedPtrSerializedMessageWithInfoCallback>(callback_variant_);
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -16,11 +16,14 @@
|
||||
#define RCLCPP__CALLBACK_GROUP_HPP_
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
@@ -95,6 +98,10 @@ public:
|
||||
CallbackGroupType group_type,
|
||||
bool automatically_add_to_executor_with_node = true);
|
||||
|
||||
/// Default destructor.
|
||||
RCLCPP_PUBLIC
|
||||
~CallbackGroup();
|
||||
|
||||
template<typename Function>
|
||||
rclcpp::SubscriptionBase::SharedPtr
|
||||
find_subscription_ptrs_if(Function func) const
|
||||
@@ -171,6 +178,16 @@ public:
|
||||
bool
|
||||
automatically_add_to_executor_with_node() const;
|
||||
|
||||
/// Defer creating the notify guard condition and return it.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::GuardCondition::SharedPtr
|
||||
get_notify_guard_condition(const rclcpp::Context::SharedPtr context_ptr);
|
||||
|
||||
/// Trigger the notify guard condition.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
trigger_notify_guard_condition();
|
||||
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(CallbackGroup)
|
||||
|
||||
@@ -213,6 +230,9 @@ protected:
|
||||
std::vector<rclcpp::Waitable::WeakPtr> waitable_ptrs_;
|
||||
std::atomic_bool can_be_taken_from_;
|
||||
const bool automatically_add_to_executor_with_node_;
|
||||
// defer the creation of the guard condition
|
||||
std::shared_ptr<rclcpp::GuardCondition> notify_guard_condition_ = nullptr;
|
||||
std::recursive_mutex notify_guard_condition_mutex_;
|
||||
|
||||
private:
|
||||
template<typename TypeT, typename Function>
|
||||
|
||||
@@ -769,7 +769,9 @@ public:
|
||||
auto old_size = pending_requests_.size();
|
||||
for (auto it = pending_requests_.begin(), last = pending_requests_.end(); it != last; ) {
|
||||
if (it->second.first < time_point) {
|
||||
pruned_requests->push_back(it->first);
|
||||
if (pruned_requests) {
|
||||
pruned_requests->push_back(it->first);
|
||||
}
|
||||
it = pending_requests_.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
@@ -792,16 +794,14 @@ protected:
|
||||
async_send_request_impl(const Request & request, CallbackInfoVariant value)
|
||||
{
|
||||
int64_t sequence_number;
|
||||
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
|
||||
rcl_ret_t ret = rcl_send_request(get_client_handle().get(), &request, &sequence_number);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
|
||||
}
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
|
||||
pending_requests_.try_emplace(
|
||||
sequence_number,
|
||||
std::make_pair(std::chrono::system_clock::now(), std::move(value)));
|
||||
}
|
||||
pending_requests_.try_emplace(
|
||||
sequence_number,
|
||||
std::make_pair(std::chrono::system_clock::now(), std::move(value)));
|
||||
return sequence_number;
|
||||
}
|
||||
|
||||
@@ -816,7 +816,7 @@ protected:
|
||||
"Received invalid sequence number. Ignoring...");
|
||||
return std::nullopt;
|
||||
}
|
||||
auto value = std::move(it->second.second);
|
||||
std::optional<CallbackInfoVariant> value = std::move(it->second.second);
|
||||
this->pending_requests_.erase(request_number);
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -60,6 +60,13 @@ public:
|
||||
/**
|
||||
* Initializes the clock instance with the given clock_type.
|
||||
*
|
||||
* WARNING Don't instantiate a clock using RCL_ROS_TIME directly,
|
||||
* unless you really know what you are doing. By default no TimeSource
|
||||
* is attached to a new clock. This will lead to the unexpected behavior,
|
||||
* that your RCL_ROS_TIME will run always on system time. If you want
|
||||
* a RCL_ROS_TIME use Node::get_clock(), or make sure to attach a
|
||||
* TimeSource yourself.
|
||||
*
|
||||
* \param clock_type type of the clock.
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
@@ -137,6 +144,51 @@ public:
|
||||
Duration rel_time,
|
||||
Context::SharedPtr context = contexts::get_global_default_context());
|
||||
|
||||
/**
|
||||
* Check if the clock is started.
|
||||
*
|
||||
* A started clock is a clock that reflects non-zero time.
|
||||
* Typically a clock will be unstarted if it is using RCL_ROS_TIME with ROS time and
|
||||
* nothing has been published on the clock topic yet.
|
||||
*
|
||||
* \return true if clock is started
|
||||
* \throws std::runtime_error if the clock is not rcl_clock_valid
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
started();
|
||||
|
||||
/**
|
||||
* Wait until clock to start.
|
||||
*
|
||||
* \rclcpp::Clock::started
|
||||
* \param context the context to wait in
|
||||
* \return true if clock was already started or became started
|
||||
* \throws std::runtime_error if the context is invalid or clock is not rcl_clock_valid
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
wait_until_started(Context::SharedPtr context = contexts::get_global_default_context());
|
||||
|
||||
/**
|
||||
* Wait for clock to start, with timeout.
|
||||
*
|
||||
* The timeout is waited in steady time.
|
||||
*
|
||||
* \rclcpp::Clock::started
|
||||
* \param timeout the maximum time to wait for.
|
||||
* \param context the context to wait in.
|
||||
* \param wait_tick_ns the time to wait between each iteration of the wait loop (in nanoseconds).
|
||||
* \return true if clock was or became valid
|
||||
* \throws std::runtime_error if the context is invalid or clock is not rcl_clock_valid
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
wait_until_started(
|
||||
const rclcpp::Duration & timeout,
|
||||
Context::SharedPtr context = contexts::get_global_default_context(),
|
||||
const rclcpp::Duration & wait_tick_ns = rclcpp::Duration(0, static_cast<uint32_t>(1e7)));
|
||||
|
||||
/**
|
||||
* Returns the clock of the type `RCL_ROS_TIME` is active.
|
||||
*
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "rcl/context.h"
|
||||
#include "rcl/guard_condition.h"
|
||||
|
||||
47
rclcpp/include/rclcpp/detail/template_contains.hpp
Normal file
47
rclcpp/include/rclcpp/detail/template_contains.hpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2022 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_
|
||||
#define RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// Template meta-function that checks if a given T is contained in the list Us.
|
||||
template<typename T, typename ... Us>
|
||||
struct template_contains;
|
||||
|
||||
template<typename ... Args>
|
||||
inline constexpr bool template_contains_v = template_contains<Args ...>::value;
|
||||
|
||||
template<typename T, typename NextT, typename ... Us>
|
||||
struct template_contains<T, NextT, Us ...>
|
||||
{
|
||||
enum { value = (std::is_same_v<T, NextT>|| template_contains_v<T, Us ...>)};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct template_contains<T>
|
||||
{
|
||||
enum { value = false };
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_
|
||||
49
rclcpp/include/rclcpp/detail/template_unique.hpp
Normal file
49
rclcpp/include/rclcpp/detail/template_unique.hpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2022 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_
|
||||
#define RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "rclcpp/detail/template_contains.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// Template meta-function that checks if a given list Ts contains unique types.
|
||||
template<typename ... Ts>
|
||||
struct template_unique;
|
||||
|
||||
template<typename ... Args>
|
||||
inline constexpr bool template_unique_v = template_unique<Args ...>::value;
|
||||
|
||||
template<typename NextT, typename ... Ts>
|
||||
struct template_unique<NextT, Ts ...>
|
||||
{
|
||||
enum { value = !template_contains_v<NextT, Ts ...>&& template_unique_v<Ts ...>};
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct template_unique<T>
|
||||
{
|
||||
enum { value = true };
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_
|
||||
@@ -560,14 +560,14 @@ protected:
|
||||
virtual void
|
||||
spin_once_impl(std::chrono::nanoseconds timeout);
|
||||
|
||||
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
||||
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
|
||||
const rclcpp::GuardCondition *,
|
||||
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
|
||||
WeakNodesToGuardConditionsMap;
|
||||
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>
|
||||
WeakCallbackGroupsToGuardConditionsMap;
|
||||
|
||||
/// maps nodes to guard conditions
|
||||
WeakNodesToGuardConditionsMap
|
||||
weak_nodes_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||
/// maps callback groups to guard conditions
|
||||
WeakCallbackGroupsToGuardConditionsMap
|
||||
weak_groups_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||
|
||||
/// maps callback groups associated to nodes
|
||||
WeakCallbackGroupsToNodesMap
|
||||
|
||||
@@ -107,7 +107,9 @@ spin_until_future_complete(
|
||||
const FutureT & future,
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
rclcpp::ExecutorOptions options;
|
||||
options.context = node_ptr->get_context();
|
||||
rclcpp::executors::SingleThreadedExecutor executor(options);
|
||||
return executors::spin_node_until_future_complete<FutureT>(executor, node_ptr, future, timeout);
|
||||
}
|
||||
|
||||
|
||||
@@ -86,8 +86,7 @@ public:
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (!has_data_()) {
|
||||
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Calling dequeue on empty intra-process buffer");
|
||||
throw std::runtime_error("Calling dequeue on empty intra-process buffer");
|
||||
return BufferT();
|
||||
}
|
||||
|
||||
auto request = std::move(ring_buffer_[read_index_]);
|
||||
|
||||
@@ -454,6 +454,8 @@ private:
|
||||
if (std::next(it) == subscription_ids.end()) {
|
||||
// If this is the last subscription, give up ownership
|
||||
subscription->provide_intra_process_data(std::move(message));
|
||||
// Last message delivered, break from for loop
|
||||
break;
|
||||
} else {
|
||||
// Copy the message since we have additional subscriptions to serve
|
||||
Deleter deleter = message.get_deleter();
|
||||
@@ -479,13 +481,13 @@ private:
|
||||
"subscription use different allocator types, which is not supported");
|
||||
}
|
||||
|
||||
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
|
||||
if constexpr (rclcpp::TypeAdapter<MessageT, ROSMessageType>::is_specialized::value) {
|
||||
ROSMessageTypeAllocator ros_message_alloc(allocator);
|
||||
auto ptr = ros_message_alloc.allocate(1);
|
||||
ros_message_alloc.construct(ptr);
|
||||
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_alloc, 1);
|
||||
ROSMessageTypeAllocatorTraits::construct(ros_message_alloc, ptr);
|
||||
ROSMessageTypeDeleter deleter;
|
||||
allocator::set_allocator_for_deleter(&deleter, &allocator);
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*message, *ptr);
|
||||
rclcpp::TypeAdapter<MessageT, ROSMessageType>::convert_to_ros_message(*message, *ptr);
|
||||
auto ros_msg = std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, deleter);
|
||||
ros_message_subscription->provide_intra_process_message(std::move(ros_msg));
|
||||
} else {
|
||||
@@ -493,6 +495,8 @@ private:
|
||||
if (std::next(it) == subscription_ids.end()) {
|
||||
// If this is the last subscription, give up ownership
|
||||
ros_message_subscription->provide_intra_process_message(std::move(message));
|
||||
// Last message delivered, break from for loop
|
||||
break;
|
||||
} else {
|
||||
// Copy the message since we have additional subscriptions to serve
|
||||
Deleter deleter = message.get_deleter();
|
||||
|
||||
@@ -109,9 +109,22 @@ public:
|
||||
|
||||
if (any_callback_.use_take_shared_method()) {
|
||||
shared_msg = this->buffer_->consume_shared();
|
||||
if (!shared_msg) {
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
unique_msg = this->buffer_->consume_unique();
|
||||
if (!unique_msg) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (this->buffer_->has_data()) {
|
||||
// If there is data still to be processed, indicate to the
|
||||
// executor or waitset by triggering the guard condition.
|
||||
this->trigger_guard_condition();
|
||||
}
|
||||
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
|
||||
std::pair<ConstMessageSharedPtr, MessageUniquePtr>(
|
||||
@@ -138,7 +151,7 @@ protected:
|
||||
execute_impl(std::shared_ptr<void> & data)
|
||||
{
|
||||
if (!data) {
|
||||
throw std::runtime_error("'data' is empty");
|
||||
return;
|
||||
}
|
||||
|
||||
rmw_message_info_t msg_info;
|
||||
|
||||
@@ -0,0 +1,210 @@
|
||||
// Copyright 2022 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__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// Support and Helper template classes for the NodeInterfaces class.
|
||||
|
||||
template<typename NodeT, typename ... Ts>
|
||||
std::tuple<std::shared_ptr<Ts>...>
|
||||
init_tuple(NodeT & n);
|
||||
|
||||
/// Stores the interfaces in a tuple, provides constructors, and getters.
|
||||
template<typename ... InterfaceTs>
|
||||
struct NodeInterfacesStorage
|
||||
{
|
||||
template<typename NodeT>
|
||||
NodeInterfacesStorage(NodeT & node) // NOLINT(runtime/explicit)
|
||||
: interfaces_(init_tuple<decltype(node), InterfaceTs ...>(node))
|
||||
{}
|
||||
|
||||
explicit NodeInterfacesStorage(std::shared_ptr<InterfaceTs>... args)
|
||||
: interfaces_(args ...)
|
||||
{}
|
||||
|
||||
/// Individual Node Interface non-const getter.
|
||||
template<typename NodeInterfaceT>
|
||||
std::shared_ptr<NodeInterfaceT>
|
||||
get()
|
||||
{
|
||||
static_assert(
|
||||
(std::is_same_v<NodeInterfaceT, InterfaceTs>|| ...),
|
||||
"NodeInterfaces class does not contain given NodeInterfaceT");
|
||||
return std::get<std::shared_ptr<NodeInterfaceT>>(interfaces_);
|
||||
}
|
||||
|
||||
/// Individual Node Interface const getter.
|
||||
template<typename NodeInterfaceT>
|
||||
std::shared_ptr<const NodeInterfaceT>
|
||||
get() const
|
||||
{
|
||||
static_assert(
|
||||
(std::is_same_v<NodeInterfaceT, InterfaceTs>|| ...),
|
||||
"NodeInterfaces class does not contain given NodeInterfaceT");
|
||||
return std::get<std::shared_ptr<NodeInterfaceT>>(interfaces_);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::tuple<std::shared_ptr<InterfaceTs>...> interfaces_;
|
||||
};
|
||||
|
||||
/// Prototype of NodeInterfacesSupports.
|
||||
/**
|
||||
* Should read NodeInterfacesSupports<..., T, ...> as "NodeInterfaces supports T", and
|
||||
* if NodeInterfacesSupport is specialized for T, the is_supported should be
|
||||
* set to std::true_type, but by default it is std::false_type, which will
|
||||
* lead to a compiler error when trying to use T with NodeInterfaces.
|
||||
*/
|
||||
template<typename StorageClassT, typename ... Ts>
|
||||
struct NodeInterfacesSupports;
|
||||
|
||||
/// Prototype of NodeInterfacesSupportCheck template meta-function.
|
||||
/**
|
||||
* This meta-function checks that all the types given are supported,
|
||||
* throwing a more human-readable error if an unsupported type is used.
|
||||
*/
|
||||
template<typename StorageClassT, typename ... InterfaceTs>
|
||||
struct NodeInterfacesSupportCheck;
|
||||
|
||||
/// Iterating specialization that ensures classes are supported and inherited.
|
||||
template<typename StorageClassT, typename NextInterfaceT, typename ... RemainingInterfaceTs>
|
||||
struct NodeInterfacesSupportCheck<StorageClassT, NextInterfaceT, RemainingInterfaceTs ...>
|
||||
: public NodeInterfacesSupportCheck<StorageClassT, RemainingInterfaceTs ...>
|
||||
{
|
||||
static_assert(
|
||||
NodeInterfacesSupports<StorageClassT, NextInterfaceT>::is_supported::value,
|
||||
"given NodeInterfaceT is not supported by rclcpp::node_interfaces::NodeInterfaces");
|
||||
};
|
||||
|
||||
/// Terminating case when there are no more "RemainingInterfaceTs".
|
||||
template<typename StorageClassT>
|
||||
struct NodeInterfacesSupportCheck<StorageClassT>
|
||||
{};
|
||||
|
||||
/// Default specialization, needs to be specialized for each supported interface.
|
||||
template<typename StorageClassT, typename ... RemainingInterfaceTs>
|
||||
struct NodeInterfacesSupports
|
||||
{
|
||||
// Specializations need to set this to std::true_type in addition to other interfaces.
|
||||
using is_supported = std::false_type;
|
||||
};
|
||||
|
||||
/// Terminating specialization of NodeInterfacesSupports.
|
||||
template<typename StorageClassT>
|
||||
struct NodeInterfacesSupports<StorageClassT>
|
||||
: public StorageClassT
|
||||
{
|
||||
/// Perfect forwarding constructor to get arguments down to StorageClassT.
|
||||
template<typename ... ArgsT>
|
||||
explicit NodeInterfacesSupports(ArgsT && ... args)
|
||||
: StorageClassT(std::forward<ArgsT>(args) ...)
|
||||
{}
|
||||
};
|
||||
|
||||
// Helper functions to initialize the tuple in NodeInterfaces.
|
||||
|
||||
template<typename StorageClassT, typename ElementT, typename TupleT, typename NodeT>
|
||||
void
|
||||
init_element(TupleT & t, NodeT & n)
|
||||
{
|
||||
std::get<std::shared_ptr<ElementT>>(t) =
|
||||
NodeInterfacesSupports<StorageClassT, ElementT>::get_from_node_like(n);
|
||||
}
|
||||
|
||||
template<typename NodeT, typename ... Ts>
|
||||
std::tuple<std::shared_ptr<Ts>...>
|
||||
init_tuple(NodeT & n)
|
||||
{
|
||||
using StorageClassT = NodeInterfacesStorage<Ts ...>;
|
||||
std::tuple<std::shared_ptr<Ts>...> t;
|
||||
(init_element<StorageClassT, Ts>(t, n), ...);
|
||||
return t;
|
||||
}
|
||||
|
||||
/// Macro for creating specializations with less boilerplate.
|
||||
/**
|
||||
* You can use this macro to add support for your interface class if:
|
||||
*
|
||||
* - The standard getter is get_node_{NodeInterfaceName}_interface(), and
|
||||
* - the getter returns a non-const shared_ptr<{NodeInterfaceType}>
|
||||
*
|
||||
* Examples of using this can be seen in the standard node interface headers
|
||||
* in rclcpp, e.g. rclcpp/node_interfaces/node_base_interface.hpp has:
|
||||
*
|
||||
* RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base)
|
||||
*
|
||||
* If your interface has a non-standard getter, or you want to instrument it or
|
||||
* something like that, then you'll need to create your own specialization of
|
||||
* the NodeInterfacesSupports struct without this macro.
|
||||
*/
|
||||
#define RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(NodeInterfaceType, NodeInterfaceName) \
|
||||
namespace rclcpp::node_interfaces::detail { \
|
||||
template<typename StorageClassT, typename ... RemainingInterfaceTs> \
|
||||
struct NodeInterfacesSupports< \
|
||||
StorageClassT, \
|
||||
NodeInterfaceType, \
|
||||
RemainingInterfaceTs ...> \
|
||||
: public NodeInterfacesSupports<StorageClassT, RemainingInterfaceTs ...> \
|
||||
{ \
|
||||
using is_supported = std::true_type; \
|
||||
\
|
||||
template<typename NodeT> \
|
||||
static \
|
||||
std::shared_ptr<NodeInterfaceType> \
|
||||
get_from_node_like(NodeT & node_like) \
|
||||
{ \
|
||||
return node_like.get_node_ ## NodeInterfaceName ## _interface(); \
|
||||
} \
|
||||
\
|
||||
/* Perfect forwarding constructor to get arguments down to StorageClassT (eventually). */ \
|
||||
template<typename ... ArgsT> \
|
||||
explicit NodeInterfacesSupports(ArgsT && ... args) \
|
||||
: NodeInterfacesSupports<StorageClassT, RemainingInterfaceTs ...>( \
|
||||
std::forward<ArgsT>(args) ...) \
|
||||
{} \
|
||||
\
|
||||
std::shared_ptr<NodeInterfaceType> \
|
||||
get_node_ ## NodeInterfaceName ## _interface() \
|
||||
{ \
|
||||
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
|
||||
|
||||
} // namespace detail
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -177,4 +178,6 @@ public:
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base)
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_BASE_INTERFACE_HPP_
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "rclcpp/clock.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -50,4 +51,6 @@ public:
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeClockInterface, clock)
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_CLOCK_INTERFACE_HPP_
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
|
||||
#include "rclcpp/event.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -52,12 +53,16 @@ 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)
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -382,4 +387,6 @@ public:
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeGraphInterface, graph)
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_
|
||||
|
||||
171
rclcpp/include/rclcpp/node_interfaces/node_interfaces.hpp
Normal file
171
rclcpp/include/rclcpp/node_interfaces/node_interfaces.hpp
Normal file
@@ -0,0 +1,171 @@
|
||||
// Copyright 2022 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__NODE_INTERFACES__NODE_INTERFACES_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_INTERFACES_HPP_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/detail/template_unique.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
|
||||
#define ALL_RCLCPP_NODE_INTERFACES \
|
||||
rclcpp::node_interfaces::NodeBaseInterface, \
|
||||
rclcpp::node_interfaces::NodeClockInterface, \
|
||||
rclcpp::node_interfaces::NodeGraphInterface, \
|
||||
rclcpp::node_interfaces::NodeLoggingInterface, \
|
||||
rclcpp::node_interfaces::NodeParametersInterface, \
|
||||
rclcpp::node_interfaces::NodeServicesInterface, \
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface, \
|
||||
rclcpp::node_interfaces::NodeTimersInterface, \
|
||||
rclcpp::node_interfaces::NodeTopicsInterface, \
|
||||
rclcpp::node_interfaces::NodeWaitablesInterface
|
||||
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
|
||||
/// A helper class for aggregating node interfaces.
|
||||
template<typename ... InterfaceTs>
|
||||
class NodeInterfaces
|
||||
: public detail::NodeInterfacesSupportCheck<
|
||||
detail::NodeInterfacesStorage<InterfaceTs ...>,
|
||||
InterfaceTs ...
|
||||
>,
|
||||
public detail::NodeInterfacesSupports<
|
||||
detail::NodeInterfacesStorage<InterfaceTs ...>,
|
||||
InterfaceTs ...
|
||||
>
|
||||
{
|
||||
static_assert(
|
||||
0 != sizeof ...(InterfaceTs),
|
||||
"must provide at least one interface as a template argument");
|
||||
static_assert(
|
||||
rclcpp::detail::template_unique_v<InterfaceTs ...>,
|
||||
"must provide unique template parameters");
|
||||
|
||||
using NodeInterfacesSupportsT = detail::NodeInterfacesSupports<
|
||||
detail::NodeInterfacesStorage<InterfaceTs ...>,
|
||||
InterfaceTs ...
|
||||
>;
|
||||
|
||||
public:
|
||||
/// Create a new NodeInterfaces object using the given node-like object's interfaces.
|
||||
/**
|
||||
* Specify which interfaces you need by passing them as template parameters.
|
||||
*
|
||||
* This allows you to aggregate interfaces from different sources together to pass as a single
|
||||
* aggregate object to any functions that take node interfaces or node-likes, without needing to
|
||||
* templatize that function.
|
||||
*
|
||||
* You may also use this constructor to create a NodeInterfaces that contains a subset of
|
||||
* another NodeInterfaces' interfaces.
|
||||
*
|
||||
* Finally, this class supports implicit conversion from node-like objects, allowing you to
|
||||
* directly pass a node-like to a function that takes a NodeInterfaces object.
|
||||
*
|
||||
* Usage examples:
|
||||
* ```cpp
|
||||
* // Suppose we have some function:
|
||||
* void fn(NodeInterfaces<NodeBaseInterface, NodeClockInterface> interfaces);
|
||||
*
|
||||
* // Then we can, explicitly:
|
||||
* rclcpp::Node node("some_node");
|
||||
* auto ni = NodeInterfaces<NodeBaseInterface, NodeClockInterface>(node);
|
||||
* fn(ni);
|
||||
*
|
||||
* // But also:
|
||||
* fn(node);
|
||||
*
|
||||
* // Subsetting a NodeInterfaces object also works!
|
||||
* auto ni_base = NodeInterfaces<NodeBaseInterface>(ni);
|
||||
*
|
||||
* // Or aggregate them (you could aggregate interfaces from disparate node-likes)
|
||||
* auto ni_aggregated = NodeInterfaces<NodeBaseInterface, NodeClockInterface>(
|
||||
* node->get_node_base_interface(),
|
||||
* node->get_node_clock_interface()
|
||||
* )
|
||||
*
|
||||
* // And then to access the interfaces:
|
||||
* // Get with get<>
|
||||
* auto base = ni.get<NodeBaseInterface>();
|
||||
*
|
||||
* // Or the appropriate getter
|
||||
* auto clock = ni.get_clock_interface();
|
||||
* ```
|
||||
*
|
||||
* You may use any of the standard node interfaces that come with rclcpp:
|
||||
* - rclcpp::node_interfaces::NodeBaseInterface
|
||||
* - rclcpp::node_interfaces::NodeClockInterface
|
||||
* - rclcpp::node_interfaces::NodeGraphInterface
|
||||
* - rclcpp::node_interfaces::NodeLoggingInterface
|
||||
* - rclcpp::node_interfaces::NodeParametersInterface
|
||||
* - rclcpp::node_interfaces::NodeServicesInterface
|
||||
* - rclcpp::node_interfaces::NodeTimeSourceInterface
|
||||
* - rclcpp::node_interfaces::NodeTimersInterface
|
||||
* - rclcpp::node_interfaces::NodeTopicsInterface
|
||||
* - rclcpp::node_interfaces::NodeWaitablesInterface
|
||||
*
|
||||
* Or you use custom interfaces as long as you make a template specialization
|
||||
* of the rclcpp::node_interfaces::detail::NodeInterfacesSupport struct using
|
||||
* the RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT macro.
|
||||
*
|
||||
* Usage example:
|
||||
* ```RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base)```
|
||||
*
|
||||
* If you choose not to use the helper macro, then you can specialize the
|
||||
* template yourself, but you must:
|
||||
*
|
||||
* - Provide a template specialization of the get_from_node_like method that gets the interface
|
||||
* from any node-like that stores the interface, using the node-like's getter
|
||||
* - Designate the is_supported type as std::true_type using a using directive
|
||||
* - Provide any number of getter methods to be used to obtain the interface with the
|
||||
* NodeInterface object, noting that the getters of the storage class will apply to all
|
||||
* supported interfaces.
|
||||
* - The getter method names should not clash in name with any other interface getter
|
||||
* specializations if those other interfaces are meant to be aggregated in the same
|
||||
* NodeInterfaces object.
|
||||
*
|
||||
* \param[in] node Node-like object from which to get the node interfaces
|
||||
*/
|
||||
template<typename NodeT>
|
||||
NodeInterfaces(NodeT & node) // NOLINT(runtime/explicit)
|
||||
: NodeInterfacesSupportsT(node)
|
||||
{}
|
||||
|
||||
/// NodeT::SharedPtr Constructor
|
||||
template<typename NodeT>
|
||||
NodeInterfaces(std::shared_ptr<NodeT> node) // NOLINT(runtime/explicit)
|
||||
: NodeInterfaces(
|
||||
[&]() -> NodeT & {
|
||||
if (!node) {
|
||||
throw std::invalid_argument("given node pointer is nullptr");
|
||||
}
|
||||
return *node;
|
||||
}())
|
||||
{}
|
||||
|
||||
explicit NodeInterfaces(std::shared_ptr<InterfaceTs>... args)
|
||||
: NodeInterfacesSupportsT(args ...)
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_INTERFACES_HPP_
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "rclcpp/logger.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -54,4 +55,6 @@ public:
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeLoggingInterface, logging)
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_LOGGING_INTERFACE_HPP_
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "rcl_interfaces/msg/set_parameters_result.hpp"
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -215,4 +216,6 @@ public:
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeParametersInterface, parameters)
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -62,4 +63,6 @@ public:
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeServicesInterface, services)
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_INTERFACE_HPP_
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -37,4 +38,6 @@ public:
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeTimeSourceInterface, time_source)
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_INTERFACE_HPP_
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/timer.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -47,4 +48,6 @@ public:
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeTimersInterface, timers)
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_TIMERS_INTERFACE_HPP_
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
|
||||
#include "rclcpp/publisher.hpp"
|
||||
@@ -97,4 +98,6 @@ public:
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeTopicsInterface, topics)
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_TOPICS_INTERFACE_HPP_
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
@@ -54,4 +55,6 @@ public:
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeWaitablesInterface, waitables)
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_WAITABLES_INTERFACE_HPP_
|
||||
|
||||
@@ -42,6 +42,7 @@ public:
|
||||
* - arguments = {}
|
||||
* - parameter_overrides = {}
|
||||
* - use_global_arguments = true
|
||||
* - enable_rosout = true
|
||||
* - use_intra_process_comms = false
|
||||
* - enable_topic_statistics = false
|
||||
* - start_parameter_services = true
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
* \param[in] names A list of parameter names of interest.
|
||||
* \param[in] types A list of the types of parameter events of iterest.
|
||||
* EventType NEW, DELETED, or CHANGED
|
||||
* \throws std::invalid_argument if event is NULL.
|
||||
*
|
||||
* Example Usage:
|
||||
*
|
||||
|
||||
@@ -41,6 +41,16 @@ RCLCPP_PUBLIC
|
||||
ParameterMap
|
||||
parameter_map_from(const rcl_params_t * const c_params);
|
||||
|
||||
/// Convert parameters from rcl_yaml_param_parser into C++ class instances.
|
||||
/// \param[in] c_params C structures containing parameters for multiple nodes.
|
||||
/// \param[in] node_fqn a Fully Qualified Name of node, default value is nullptr.
|
||||
/// If it's not nullptr, return the relative node parameters belonging to this node_fqn.
|
||||
/// \returns a map where the keys are fully qualified node names and values a list of parameters.
|
||||
/// \throws InvalidParametersException if the `rcl_params_t` is inconsistent or invalid.
|
||||
RCLCPP_PUBLIC
|
||||
ParameterMap
|
||||
parameter_map_from(const rcl_params_t * const c_params, const char * node_fqn);
|
||||
|
||||
/// Convert parameter value from rcl_yaml_param_parser into a C++ class instance.
|
||||
/// \param[in] c_value C structure containing a value of a parameter.
|
||||
/// \returns an instance of a parameter value
|
||||
|
||||
@@ -177,8 +177,7 @@ public:
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
|
||||
{
|
||||
// Topic is unused for now.
|
||||
(void)topic;
|
||||
(void)qos;
|
||||
(void)options;
|
||||
|
||||
// If needed, setup intra process communication.
|
||||
@@ -186,19 +185,23 @@ public:
|
||||
auto context = node_base->get_context();
|
||||
// Get the intra process manager instance for this context.
|
||||
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
|
||||
// Register the publisher with the intra process manager.
|
||||
if (qos.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
// Check if the QoS is compatible with intra-process.
|
||||
auto qos_profile = get_actual_qos();
|
||||
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
"intraprocess communication on topic '" + topic +
|
||||
"' allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos.depth() == 0) {
|
||||
if (qos_profile.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with a zero qos history depth value");
|
||||
"intraprocess communication on topic '" + topic +
|
||||
"' is not allowed with a zero qos history depth value");
|
||||
}
|
||||
if (qos.durability() != rclcpp::DurabilityPolicy::Volatile) {
|
||||
if (qos_profile.durability() != rclcpp::DurabilityPolicy::Volatile) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication allowed only with volatile durability");
|
||||
}
|
||||
// Register the publisher with the intra process manager.
|
||||
uint64_t intra_process_publisher_id = ipm->add_publisher(this->shared_from_this());
|
||||
this->setup_intra_process(
|
||||
intra_process_publisher_id,
|
||||
|
||||
@@ -72,7 +72,7 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
|
||||
PublisherOptionsWithAllocator<Allocator>() {}
|
||||
PublisherOptionsWithAllocator() {}
|
||||
|
||||
/// Constructor using base class as input.
|
||||
explicit PublisherOptionsWithAllocator(const PublisherOptionsBase & publisher_options_base)
|
||||
|
||||
@@ -481,6 +481,14 @@ public:
|
||||
{
|
||||
rcl_ret_t ret = rcl_send_response(get_service_handle().get(), &req_id, &response);
|
||||
|
||||
if (ret == RCL_RET_TIMEOUT) {
|
||||
RCLCPP_WARN(
|
||||
node_logger_.get_child("rclcpp"),
|
||||
"failed to send response to %s (timeout): %s",
|
||||
this->get_service_name(), rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
return;
|
||||
}
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send response");
|
||||
}
|
||||
|
||||
@@ -186,11 +186,13 @@ public:
|
||||
auto qos_profile = get_actual_qos();
|
||||
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
"intraprocess communication on topic '" + topic_name +
|
||||
"' allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos_profile.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with 0 depth qos policy");
|
||||
"intraprocess communication on topic '" + topic_name +
|
||||
"' is not allowed with 0 depth qos policy");
|
||||
}
|
||||
if (qos_profile.durability() != rclcpp::DurabilityPolicy::Volatile) {
|
||||
throw std::invalid_argument(
|
||||
@@ -363,11 +365,31 @@ public:
|
||||
void * loaned_message,
|
||||
const rclcpp::MessageInfo & message_info) override
|
||||
{
|
||||
if (matches_any_intra_process_publishers(&message_info.get_rmw_message_info().publisher_gid)) {
|
||||
// In this case, the message will be delivered via intra process and
|
||||
// we should ignore this copy of the message.
|
||||
return;
|
||||
}
|
||||
|
||||
auto typed_message = static_cast<ROSMessageType *>(loaned_message);
|
||||
// message is loaned, so we have to make sure that the deleter does not deallocate the message
|
||||
auto sptr = std::shared_ptr<ROSMessageType>(
|
||||
typed_message, [](ROSMessageType * msg) {(void) msg;});
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> now;
|
||||
if (subscription_topic_statistics_) {
|
||||
// get current time before executing callback to
|
||||
// exclude callback duration from topic statistics result.
|
||||
now = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
any_callback_.dispatch(sptr, message_info);
|
||||
|
||||
if (subscription_topic_statistics_) {
|
||||
const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
|
||||
const auto time = rclcpp::Time(nanos.time_since_epoch().count());
|
||||
subscription_topic_statistics_->handle_message(*typed_message, time);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the borrowed message.
|
||||
|
||||
@@ -97,7 +97,7 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
|
||||
SubscriptionOptionsWithAllocator<Allocator>() {}
|
||||
SubscriptionOptionsWithAllocator() {}
|
||||
|
||||
/// Constructor using base class as input.
|
||||
explicit SubscriptionOptionsWithAllocator(
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP__WAIT_FOR_MESSAGE_HPP_
|
||||
#define RCLCPP__WAIT_FOR_MESSAGE_HPP_
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -23,6 +24,7 @@
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/wait_set.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -54,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);
|
||||
@@ -79,10 +81,11 @@ bool wait_for_message(
|
||||
/**
|
||||
* Wait for the next incoming message to arrive on a specified topic before the specified timeout.
|
||||
*
|
||||
* \param[out] out is the message to be filled when a new message is arriving.
|
||||
* \param[out] out is the message to be filled when a new message is arriving
|
||||
* \param[in] node the node pointer to initialize the subscription on.
|
||||
* \param[in] topic the topic to wait for messages.
|
||||
* \param[in] time_to_wait parameter specifying the timeout before returning.
|
||||
* \param[in] qos parameter specifying QoS settings for the subscription.
|
||||
* \return true if a message was successfully received, false if message could not
|
||||
* be obtained or shutdown was triggered asynchronously on the context.
|
||||
*/
|
||||
@@ -91,9 +94,10 @@ bool wait_for_message(
|
||||
MsgT & out,
|
||||
rclcpp::Node::SharedPtr node,
|
||||
const std::string & topic,
|
||||
std::chrono::duration<Rep, Period> time_to_wait = std::chrono::duration<Rep, Period>(-1))
|
||||
std::chrono::duration<Rep, Period> time_to_wait = std::chrono::duration<Rep, Period>(-1),
|
||||
const rclcpp::QoS & qos = rclcpp::SystemDefaultsQoS())
|
||||
{
|
||||
auto sub = node->create_subscription<MsgT>(topic, 1, [](const std::shared_ptr<const MsgT>) {});
|
||||
auto sub = node->create_subscription<MsgT>(topic, qos, [](const std::shared_ptr<const MsgT>) {});
|
||||
return wait_for_message<MsgT, Rep, Period>(
|
||||
out, sub, node->get_node_options().context(), time_to_wait);
|
||||
}
|
||||
|
||||
@@ -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>16.0.1</version>
|
||||
<version>16.0.18</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="jacob@openrobotics.org">Jacob Perron</maintainer>
|
||||
|
||||
@@ -125,8 +125,7 @@ def get_rclcpp_suffix_from_features(features):
|
||||
) \
|
||||
do { \
|
||||
static_assert( \
|
||||
::std::is_same<typename std::remove_cv<typename std::remove_reference<decltype(logger)>::type>::type, \
|
||||
typename ::rclcpp::Logger>::value, \
|
||||
::std::is_convertible_v<decltype(logger), ::rclcpp::Logger>, \
|
||||
"First argument to logging macros must be an rclcpp::Logger"); \
|
||||
@[ if 'throttle' in feature_combination]@ \
|
||||
auto get_time_point = [&c=clock](rcutils_time_point_value_t * time_point) -> rcutils_ret_t { \
|
||||
|
||||
@@ -12,9 +12,19 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
|
||||
#include <vector>
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
#include "rclcpp/timer.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
using rclcpp::CallbackGroup;
|
||||
using rclcpp::CallbackGroupType;
|
||||
@@ -27,6 +37,10 @@ CallbackGroup::CallbackGroup(
|
||||
automatically_add_to_executor_with_node_(automatically_add_to_executor_with_node)
|
||||
{}
|
||||
|
||||
CallbackGroup::~CallbackGroup()
|
||||
{
|
||||
trigger_notify_guard_condition();
|
||||
}
|
||||
|
||||
std::atomic_bool &
|
||||
CallbackGroup::can_be_taken_from()
|
||||
@@ -97,6 +111,33 @@ CallbackGroup::automatically_add_to_executor_with_node() const
|
||||
return automatically_add_to_executor_with_node_;
|
||||
}
|
||||
|
||||
rclcpp::GuardCondition::SharedPtr
|
||||
CallbackGroup::get_notify_guard_condition(const rclcpp::Context::SharedPtr context_ptr)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(notify_guard_condition_mutex_);
|
||||
if (notify_guard_condition_ && context_ptr != notify_guard_condition_->get_context()) {
|
||||
if (associated_with_executor_) {
|
||||
trigger_notify_guard_condition();
|
||||
}
|
||||
notify_guard_condition_ = nullptr;
|
||||
}
|
||||
|
||||
if (!notify_guard_condition_) {
|
||||
notify_guard_condition_ = std::make_shared<rclcpp::GuardCondition>(context_ptr);
|
||||
}
|
||||
|
||||
return notify_guard_condition_;
|
||||
}
|
||||
|
||||
void
|
||||
CallbackGroup::trigger_notify_guard_condition()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(notify_guard_condition_mutex_);
|
||||
if (notify_guard_condition_) {
|
||||
notify_guard_condition_->trigger();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
CallbackGroup::add_subscription(
|
||||
const rclcpp::SubscriptionBase::SharedPtr subscription_ptr)
|
||||
|
||||
@@ -182,6 +182,71 @@ Clock::sleep_for(Duration rel_time, Context::SharedPtr context)
|
||||
return sleep_until(now() + rel_time, context);
|
||||
}
|
||||
|
||||
bool
|
||||
Clock::started()
|
||||
{
|
||||
if (!rcl_clock_valid(get_clock_handle())) {
|
||||
throw std::runtime_error("clock is not rcl_clock_valid");
|
||||
}
|
||||
return rcl_clock_time_started(get_clock_handle());
|
||||
}
|
||||
|
||||
bool
|
||||
Clock::wait_until_started(Context::SharedPtr context)
|
||||
{
|
||||
if (!context || !context->is_valid()) {
|
||||
throw std::runtime_error("context cannot be slept with because it's invalid");
|
||||
}
|
||||
if (!rcl_clock_valid(get_clock_handle())) {
|
||||
throw std::runtime_error("clock cannot be waited on as it is not rcl_clock_valid");
|
||||
}
|
||||
|
||||
if (started()) {
|
||||
return true;
|
||||
} else {
|
||||
// Wait until the first non-zero time
|
||||
return sleep_until(rclcpp::Time(0, 1, get_clock_type()), context);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Clock::wait_until_started(
|
||||
const Duration & timeout,
|
||||
Context::SharedPtr context,
|
||||
const Duration & wait_tick_ns)
|
||||
{
|
||||
if (!context || !context->is_valid()) {
|
||||
throw std::runtime_error("context cannot be slept with because it's invalid");
|
||||
}
|
||||
if (!rcl_clock_valid(get_clock_handle())) {
|
||||
throw std::runtime_error("clock cannot be waited on as it is not rcl_clock_valid");
|
||||
}
|
||||
|
||||
Clock timeout_clock = Clock(RCL_STEADY_TIME);
|
||||
Time start = timeout_clock.now();
|
||||
|
||||
// Check if the clock has started every wait_tick_ns nanoseconds
|
||||
// Context check checks for rclcpp::shutdown()
|
||||
while (!started() && context->is_valid()) {
|
||||
if (timeout < wait_tick_ns) {
|
||||
timeout_clock.sleep_for(timeout);
|
||||
} else {
|
||||
Duration time_left = start + timeout - timeout_clock.now();
|
||||
if (time_left > wait_tick_ns) {
|
||||
timeout_clock.sleep_for(Duration(wait_tick_ns));
|
||||
} else {
|
||||
timeout_clock.sleep_for(time_left);
|
||||
}
|
||||
}
|
||||
|
||||
if (timeout_clock.now() - start > timeout) {
|
||||
return started();
|
||||
}
|
||||
}
|
||||
return started();
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
Clock::ros_time_is_active()
|
||||
{
|
||||
|
||||
@@ -217,7 +217,7 @@ Context::init(
|
||||
if (0u == count) {
|
||||
ret = rcl_logging_configure_with_output_handler(
|
||||
&rcl_context_->global_arguments,
|
||||
rcl_init_options_get_allocator(init_options_.get_rcl_init_options()),
|
||||
rcl_init_options_get_allocator(init_options.get_rcl_init_options()),
|
||||
rclcpp_logging_output_handler);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rcl_context_.reset();
|
||||
@@ -497,7 +497,7 @@ Context::sleep_for(const std::chrono::nanoseconds & nanoseconds)
|
||||
std::unique_lock<std::mutex> lock(interrupt_mutex_);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
// this will release the lock while waiting
|
||||
interrupt_condition_variable_.wait_for(lock, nanoseconds);
|
||||
interrupt_condition_variable_.wait_for(lock, time_left);
|
||||
time_left -= std::chrono::steady_clock::now() - start;
|
||||
}
|
||||
} while (time_left > std::chrono::nanoseconds::zero() && this->is_valid());
|
||||
|
||||
@@ -51,18 +51,13 @@ rclcpp::detail::resolve_parameter_overrides(
|
||||
[params]() {
|
||||
rcl_yaml_node_struct_fini(params);
|
||||
});
|
||||
rclcpp::ParameterMap initial_map = rclcpp::parameter_map_from(params);
|
||||
rclcpp::ParameterMap initial_map = rclcpp::parameter_map_from(params, node_fqn.c_str());
|
||||
|
||||
// Enforce wildcard matching precedence
|
||||
// TODO(cottsay) implement further wildcard matching
|
||||
const std::array<std::string, 2> node_matching_names{"/**", node_fqn};
|
||||
for (const auto & node_name : node_matching_names) {
|
||||
if (initial_map.count(node_name) > 0) {
|
||||
// Combine parameter yaml files, overwriting values in older ones
|
||||
for (const rclcpp::Parameter & param : initial_map.at(node_name)) {
|
||||
result[param.get_name()] =
|
||||
rclcpp::ParameterValue(param.get_value_message());
|
||||
}
|
||||
if (initial_map.count(node_fqn) > 0) {
|
||||
// Combine parameter yaml files, overwriting values in older ones
|
||||
for (const rclcpp::Parameter & param : initial_map.at(node_fqn)) {
|
||||
result[param.get_name()] =
|
||||
rclcpp::ParameterValue(param.get_value_message());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,11 +106,11 @@ Executor::~Executor()
|
||||
weak_groups_associated_with_executor_to_nodes_.clear();
|
||||
weak_groups_to_nodes_associated_with_executor_.clear();
|
||||
weak_groups_to_nodes_.clear();
|
||||
for (const auto & pair : weak_nodes_to_guard_conditions_) {
|
||||
for (const auto & pair : weak_groups_to_guard_conditions_) {
|
||||
auto guard_condition = pair.second;
|
||||
memory_strategy_->remove_guard_condition(guard_condition);
|
||||
}
|
||||
weak_nodes_to_guard_conditions_.clear();
|
||||
weak_groups_to_guard_conditions_.clear();
|
||||
|
||||
// Finalize the wait set.
|
||||
if (rcl_wait_set_fini(&wait_set_) != RCL_RET_OK) {
|
||||
@@ -204,8 +204,7 @@ Executor::add_callback_group_to_map(
|
||||
if (has_executor.exchange(true)) {
|
||||
throw std::runtime_error("Callback group has already been added to an executor.");
|
||||
}
|
||||
bool is_new_node = !has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_) &&
|
||||
!has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_);
|
||||
|
||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
|
||||
auto insert_info =
|
||||
weak_groups_to_nodes.insert(std::make_pair(weak_group_ptr, node_ptr));
|
||||
@@ -215,21 +214,24 @@ Executor::add_callback_group_to_map(
|
||||
}
|
||||
// Also add to the map that contains all callback groups
|
||||
weak_groups_to_nodes_.insert(std::make_pair(weak_group_ptr, node_ptr));
|
||||
if (is_new_node) {
|
||||
const auto & gc = node_ptr->get_notify_guard_condition();
|
||||
weak_nodes_to_guard_conditions_[node_ptr] = &gc;
|
||||
if (notify) {
|
||||
// Interrupt waiting to handle new node
|
||||
try {
|
||||
interrupt_guard_condition_.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to trigger guard condition on callback group add: ") + ex.what());
|
||||
}
|
||||
|
||||
if (node_ptr->get_context()->is_valid()) {
|
||||
auto callback_group_guard_condition =
|
||||
group_ptr->get_notify_guard_condition(node_ptr->get_context());
|
||||
weak_groups_to_guard_conditions_[weak_group_ptr] = callback_group_guard_condition.get();
|
||||
// Add the callback_group's notify condition to the guard condition handles
|
||||
memory_strategy_->add_guard_condition(*callback_group_guard_condition);
|
||||
}
|
||||
|
||||
if (notify) {
|
||||
// Interrupt waiting to handle new node
|
||||
try {
|
||||
interrupt_guard_condition_.trigger();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to trigger guard condition on callback group add: ") + ex.what());
|
||||
}
|
||||
// Add the node's notify condition to the guard condition handles
|
||||
memory_strategy_->add_guard_condition(gc);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -300,7 +302,12 @@ Executor::remove_callback_group_from_map(
|
||||
if (!has_node(node_ptr, weak_groups_to_nodes_associated_with_executor_) &&
|
||||
!has_node(node_ptr, weak_groups_associated_with_executor_to_nodes_))
|
||||
{
|
||||
weak_nodes_to_guard_conditions_.erase(node_ptr);
|
||||
auto iter = weak_groups_to_guard_conditions_.find(weak_group_ptr);
|
||||
if (iter != weak_groups_to_guard_conditions_.end()) {
|
||||
memory_strategy_->remove_guard_condition(iter->second);
|
||||
}
|
||||
weak_groups_to_guard_conditions_.erase(weak_group_ptr);
|
||||
|
||||
if (notify) {
|
||||
try {
|
||||
interrupt_guard_condition_.trigger();
|
||||
@@ -310,7 +317,6 @@ Executor::remove_callback_group_from_map(
|
||||
"Failed to trigger guard condition on callback group remove: ") + ex.what());
|
||||
}
|
||||
}
|
||||
memory_strategy_->remove_guard_condition(&node_ptr->get_notify_guard_condition());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -640,6 +646,11 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
|
||||
subscription->get_topic_name(),
|
||||
[&]() {return subscription->take_type_erased(message.get(), message_info);},
|
||||
[&]() {subscription->handle_message(message, message_info);});
|
||||
// TODO(clalancette): In the case that the user is using the MessageMemoryPool,
|
||||
// and they take a shared_ptr reference to the message in the callback, this can
|
||||
// inadvertently return the message to the pool when the user is still using it.
|
||||
// This is a bug that needs to be fixed in the pool, and we should probably have
|
||||
// a custom deleter for the message that actually does the return_message().
|
||||
subscription->return_message(message);
|
||||
}
|
||||
}
|
||||
@@ -700,12 +711,6 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
|
||||
auto weak_node_ptr = pair.second;
|
||||
if (weak_group_ptr.expired() || weak_node_ptr.expired()) {
|
||||
invalid_group_ptrs.push_back(weak_group_ptr);
|
||||
auto node_guard_pair = weak_nodes_to_guard_conditions_.find(weak_node_ptr);
|
||||
if (node_guard_pair != weak_nodes_to_guard_conditions_.end()) {
|
||||
auto guard_condition = node_guard_pair->second;
|
||||
weak_nodes_to_guard_conditions_.erase(weak_node_ptr);
|
||||
memory_strategy_->remove_guard_condition(guard_condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::for_each(
|
||||
@@ -721,6 +726,12 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
weak_groups_associated_with_executor_to_nodes_.erase(group_ptr);
|
||||
}
|
||||
auto callback_guard_pair = weak_groups_to_guard_conditions_.find(group_ptr);
|
||||
if (callback_guard_pair != weak_groups_to_guard_conditions_.end()) {
|
||||
auto guard_condition = callback_guard_pair->second;
|
||||
weak_groups_to_guard_conditions_.erase(group_ptr);
|
||||
memory_strategy_->remove_guard_condition(guard_condition);
|
||||
}
|
||||
weak_groups_to_nodes_.erase(group_ptr);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -17,7 +17,9 @@
|
||||
void
|
||||
rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
rclcpp::ExecutorOptions options;
|
||||
options.context = node_ptr->get_context();
|
||||
rclcpp::executors::SingleThreadedExecutor exec(options);
|
||||
exec.spin_node_some(node_ptr);
|
||||
}
|
||||
|
||||
@@ -30,7 +32,9 @@ rclcpp::spin_some(rclcpp::Node::SharedPtr node_ptr)
|
||||
void
|
||||
rclcpp::spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
rclcpp::ExecutorOptions options;
|
||||
options.context = node_ptr->get_context();
|
||||
rclcpp::executors::SingleThreadedExecutor exec(options);
|
||||
exec.add_node(node_ptr);
|
||||
exec.spin();
|
||||
exec.remove_node(node_ptr);
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "rcutils/macros.h"
|
||||
|
||||
|
||||
@@ -35,15 +35,17 @@ NodeServices::add_service(
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create service, group not in node.");
|
||||
}
|
||||
group->add_service(service_base_ptr);
|
||||
} else {
|
||||
node_base_->get_default_callback_group()->add_service(service_base_ptr);
|
||||
group = node_base_->get_default_callback_group();
|
||||
}
|
||||
|
||||
group->add_service(service_base_ptr);
|
||||
|
||||
// Notify the executor that a new service was created using the parent Node.
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
group->trigger_notify_guard_condition();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on service creation: ") + ex.what());
|
||||
@@ -60,15 +62,17 @@ NodeServices::add_client(
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create client, group not in node.");
|
||||
}
|
||||
group->add_client(client_base_ptr);
|
||||
} else {
|
||||
node_base_->get_default_callback_group()->add_client(client_base_ptr);
|
||||
group = node_base_->get_default_callback_group();
|
||||
}
|
||||
|
||||
group->add_client(client_base_ptr);
|
||||
|
||||
// Notify the executor that a new client was created using the parent Node.
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
group->trigger_notify_guard_condition();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on client creation: ") + ex.what());
|
||||
|
||||
@@ -37,14 +37,15 @@ NodeTimers::add_timer(
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create timer, group not in node.");
|
||||
}
|
||||
callback_group->add_timer(timer);
|
||||
} else {
|
||||
node_base_->get_default_callback_group()->add_timer(timer);
|
||||
callback_group = node_base_->get_default_callback_group();
|
||||
}
|
||||
callback_group->add_timer(timer);
|
||||
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
callback_group->trigger_notify_guard_condition();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on timer creation: ") + ex.what());
|
||||
|
||||
@@ -73,6 +73,7 @@ NodeTopics::add_publisher(
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
callback_group->trigger_notify_guard_condition();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on publisher creation: ") + ex.what());
|
||||
@@ -121,6 +122,7 @@ NodeTopics::add_subscription(
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
callback_group->trigger_notify_guard_condition();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on subscription creation: ") + ex.what());
|
||||
|
||||
@@ -35,15 +35,17 @@ NodeWaitables::add_waitable(
|
||||
// TODO(jacobperron): use custom exception
|
||||
throw std::runtime_error("Cannot create waitable, group not in node.");
|
||||
}
|
||||
group->add_waitable(waitable_ptr);
|
||||
} else {
|
||||
node_base_->get_default_callback_group()->add_waitable(waitable_ptr);
|
||||
group = node_base_->get_default_callback_group();
|
||||
}
|
||||
|
||||
group->add_waitable(waitable_ptr);
|
||||
|
||||
// Notify the executor that a new waitable was created using the parent Node.
|
||||
auto & node_gc = node_base_->get_notify_guard_condition();
|
||||
try {
|
||||
node_gc.trigger();
|
||||
group->trigger_notify_guard_condition();
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to notify wait set on waitable creation: ") + ex.what());
|
||||
|
||||
@@ -28,6 +28,9 @@ ParameterEventsFilter::ParameterEventsFilter(
|
||||
const std::vector<EventType> & types)
|
||||
: event_(event)
|
||||
{
|
||||
if (!event) {
|
||||
throw std::invalid_argument("event cannot be null");
|
||||
}
|
||||
if (std::find(types.begin(), types.end(), EventType::NEW) != types.end()) {
|
||||
for (auto & new_parameter : event->new_parameters) {
|
||||
if (std::find(names.begin(), names.end(), new_parameter.name) != names.end()) {
|
||||
|
||||
@@ -13,8 +13,10 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <string>
|
||||
#include <regex>
|
||||
#include <vector>
|
||||
|
||||
#include "rcpputils/find_and_replace.hpp"
|
||||
#include "rclcpp/parameter_map.hpp"
|
||||
|
||||
using rclcpp::exceptions::InvalidParametersException;
|
||||
@@ -24,6 +26,12 @@ using rclcpp::ParameterValue;
|
||||
|
||||
ParameterMap
|
||||
rclcpp::parameter_map_from(const rcl_params_t * const c_params)
|
||||
{
|
||||
return parameter_map_from(c_params, nullptr);
|
||||
}
|
||||
|
||||
ParameterMap
|
||||
rclcpp::parameter_map_from(const rcl_params_t * const c_params, const char * node_fqn)
|
||||
{
|
||||
if (NULL == c_params) {
|
||||
throw InvalidParametersException("parameters struct is NULL");
|
||||
@@ -49,6 +57,17 @@ rclcpp::parameter_map_from(const rcl_params_t * const c_params)
|
||||
node_name = c_node_name;
|
||||
}
|
||||
|
||||
if (node_fqn) {
|
||||
// Update the regular expression ["/*" -> "(/\\w+)" and "/**" -> "(/\\w+)*"]
|
||||
std::string regex = rcpputils::find_and_replace(node_name, "/*", "(/\\w+)");
|
||||
if (!std::regex_match(node_fqn, std::regex(regex))) {
|
||||
// No need to parse the items because the user just care about node_fqn
|
||||
continue;
|
||||
}
|
||||
|
||||
node_name = node_fqn;
|
||||
}
|
||||
|
||||
const rcl_node_params_t * const c_params_node = &(c_params->params[n]);
|
||||
|
||||
std::vector<Parameter> & params_node = parameters[node_name];
|
||||
@@ -62,9 +81,18 @@ rclcpp::parameter_map_from(const rcl_params_t * const c_params)
|
||||
throw InvalidParametersException(message);
|
||||
}
|
||||
const rcl_variant_t * const c_param_value = &(c_params_node->parameter_values[p]);
|
||||
params_node.emplace_back(c_param_name, parameter_value_from(c_param_value));
|
||||
ParameterValue value;
|
||||
try {
|
||||
value = parameter_value_from(c_param_value);
|
||||
} catch (const InvalidParameterValueException & e) {
|
||||
throw InvalidParameterValueException(
|
||||
std::string("parameter_value_from failed for parameter '") +
|
||||
c_param_name + "': " + e.what());
|
||||
}
|
||||
params_node.emplace_back(c_param_name, value);
|
||||
}
|
||||
}
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ 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());
|
||||
}
|
||||
},
|
||||
qos_profile, nullptr);
|
||||
@@ -68,7 +68,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);
|
||||
@@ -89,7 +89,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();
|
||||
}
|
||||
@@ -117,7 +117,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";
|
||||
@@ -137,7 +137,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);
|
||||
|
||||
@@ -56,8 +56,10 @@ QoSInitialization::from_rmw(const rmw_qos_profile_t & rmw_qos)
|
||||
case RMW_QOS_POLICY_HISTORY_KEEP_LAST:
|
||||
case RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT:
|
||||
case RMW_QOS_POLICY_HISTORY_UNKNOWN:
|
||||
default:
|
||||
return KeepLast(rmw_qos.depth);
|
||||
default:
|
||||
throw std::invalid_argument(
|
||||
"Invalid history policy enum value passed to QoSInitialization::from_rmw");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ SerializationBase::SerializationBase(const rosidl_message_type_support_t * type_
|
||||
void SerializationBase::serialize_message(
|
||||
const void * ros_message, SerializedMessage * serialized_message) const
|
||||
{
|
||||
rcpputils::check_true(nullptr != type_support_, "Typesupport is nullpointer.");
|
||||
rcpputils::check_true(nullptr != ros_message, "ROS message is nullpointer.");
|
||||
rcpputils::check_true(nullptr != serialized_message, "Serialized message is nullpointer.");
|
||||
|
||||
@@ -52,7 +51,6 @@ void SerializationBase::serialize_message(
|
||||
void SerializationBase::deserialize_message(
|
||||
const SerializedMessage * serialized_message, void * ros_message) const
|
||||
{
|
||||
rcpputils::check_true(nullptr != type_support_, "Typesupport is nullpointer.");
|
||||
rcpputils::check_true(nullptr != serialized_message, "Serialized message is nullpointer.");
|
||||
rcpputils::check_true(
|
||||
0u != serialized_message->capacity(),
|
||||
|
||||
@@ -26,8 +26,13 @@ namespace rclcpp
|
||||
|
||||
inline void copy_rcl_message(const rcl_serialized_message_t & from, rcl_serialized_message_t & to)
|
||||
{
|
||||
const auto ret = rmw_serialized_message_init(
|
||||
&to, from.buffer_capacity, &from.allocator);
|
||||
auto ret = RCL_RET_ERROR;
|
||||
if (nullptr == to.buffer) {
|
||||
ret = rmw_serialized_message_init(&to, from.buffer_capacity, &from.allocator);
|
||||
} else {
|
||||
ret = rmw_serialized_message_resize(&to, from.buffer_capacity);
|
||||
}
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
@@ -78,7 +83,6 @@ SerializedMessage::SerializedMessage(rcl_serialized_message_t && other)
|
||||
SerializedMessage & SerializedMessage::operator=(const SerializedMessage & other)
|
||||
{
|
||||
if (this != &other) {
|
||||
serialized_message_ = rmw_get_zero_initialized_serialized_message();
|
||||
copy_rcl_message(other.serialized_message_, serialized_message_);
|
||||
}
|
||||
|
||||
@@ -88,7 +92,6 @@ SerializedMessage & SerializedMessage::operator=(const SerializedMessage & other
|
||||
SerializedMessage & SerializedMessage::operator=(const rcl_serialized_message_t & other)
|
||||
{
|
||||
if (&serialized_message_ != &other) {
|
||||
serialized_message_ = rmw_get_zero_initialized_serialized_message();
|
||||
copy_rcl_message(other, serialized_message_);
|
||||
}
|
||||
|
||||
@@ -98,6 +101,14 @@ SerializedMessage & SerializedMessage::operator=(const rcl_serialized_message_t
|
||||
SerializedMessage & SerializedMessage::operator=(SerializedMessage && other)
|
||||
{
|
||||
if (this != &other) {
|
||||
if (nullptr != serialized_message_.buffer) {
|
||||
const auto fini_ret = rmw_serialized_message_fini(&serialized_message_);
|
||||
if (RCL_RET_OK != fini_ret) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger("rclcpp"),
|
||||
"Failed to destroy serialized message: %s", rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
serialized_message_ =
|
||||
std::exchange(other.serialized_message_, rmw_get_zero_initialized_serialized_message());
|
||||
}
|
||||
@@ -108,6 +119,14 @@ SerializedMessage & SerializedMessage::operator=(SerializedMessage && other)
|
||||
SerializedMessage & SerializedMessage::operator=(rcl_serialized_message_t && other)
|
||||
{
|
||||
if (&serialized_message_ != &other) {
|
||||
if (nullptr != serialized_message_.buffer) {
|
||||
const auto fini_ret = rmw_serialized_message_fini(&serialized_message_);
|
||||
if (RCL_RET_OK != fini_ret) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger("rclcpp"),
|
||||
"Failed to destroy serialized message: %s", rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
serialized_message_ =
|
||||
std::exchange(other, rmw_get_zero_initialized_serialized_message());
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
@@ -91,7 +90,6 @@ SignalHandler::signal_handler(
|
||||
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 (
|
||||
@@ -113,7 +111,7 @@ SignalHandler::get_logger()
|
||||
SignalHandler &
|
||||
SignalHandler::get_global_signal_handler()
|
||||
{
|
||||
static SignalHandler signal_handler;
|
||||
static SignalHandler & signal_handler = *new SignalHandler();
|
||||
return signal_handler;
|
||||
}
|
||||
|
||||
@@ -191,7 +189,9 @@ SignalHandler::uninstall()
|
||||
signal_handlers_options_ = SignalHandlerOptions::None;
|
||||
RCLCPP_DEBUG(get_logger(), "SignalHandler::uninstall(): notifying deferred signal handler");
|
||||
notify_signal_handler();
|
||||
signal_handler_thread_.join();
|
||||
if (signal_handler_thread_.joinable()) {
|
||||
signal_handler_thread_.join();
|
||||
}
|
||||
teardown_wait_for_signal();
|
||||
} catch (...) {
|
||||
installed_.exchange(true);
|
||||
@@ -247,9 +247,6 @@ SignalHandler::signal_handler_common()
|
||||
{
|
||||
auto & instance = SignalHandler::get_global_signal_handler();
|
||||
instance.signal_received_.store(true);
|
||||
RCLCPP_DEBUG(
|
||||
get_logger(),
|
||||
"signal_handler(): notifying deferred signal handler");
|
||||
instance.notify_signal_handler();
|
||||
}
|
||||
|
||||
@@ -258,6 +255,7 @@ SignalHandler::deferred_signal_handler()
|
||||
{
|
||||
while (true) {
|
||||
if (signal_received_.exchange(false)) {
|
||||
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(SIGINT/SIGTERM)");
|
||||
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) {
|
||||
|
||||
@@ -229,7 +229,20 @@ SubscriptionBase::setup_intra_process(
|
||||
bool
|
||||
SubscriptionBase::can_loan_messages() const
|
||||
{
|
||||
return rcl_subscription_can_loan_messages(subscription_handle_.get());
|
||||
bool retval = rcl_subscription_can_loan_messages(subscription_handle_.get());
|
||||
if (retval) {
|
||||
// TODO(clalancette): The loaned message interface is currently not safe to use with
|
||||
// shared_ptr callbacks. If a user takes a copy of the shared_ptr, it can get freed from
|
||||
// underneath them via rcl_return_loaned_message_from_subscription(). The correct solution is
|
||||
// to return the loaned message in a custom deleter, but that needs to be carefully handled
|
||||
// with locking. Warn the user about this until we fix it.
|
||||
RCLCPP_WARN_ONCE(
|
||||
this->node_logger_,
|
||||
"Loaned messages are only safe with const ref subscription callbacks. "
|
||||
"If you are using any other kind of subscriptions, "
|
||||
"set the ROS_DISABLE_LOANED_MESSAGES environment variable to 1 (the default).");
|
||||
}
|
||||
return retval;
|
||||
}
|
||||
|
||||
rclcpp::Waitable::SharedPtr
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -100,15 +100,20 @@ if(TARGET test_create_subscription)
|
||||
"test_msgs"
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_add_callback_groups_to_executor
|
||||
test_add_callback_groups_to_executor.cpp
|
||||
TIMEOUT 120)
|
||||
if(TARGET test_add_callback_groups_to_executor)
|
||||
target_link_libraries(test_add_callback_groups_to_executor ${PROJECT_NAME})
|
||||
ament_target_dependencies(test_add_callback_groups_to_executor
|
||||
"test_msgs"
|
||||
function(test_add_callback_groups_to_executor_for_rmw_implementation)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
ament_add_gmock(test_add_callback_groups_to_executor${target_suffix} test_add_callback_groups_to_executor.cpp
|
||||
ENV ${rmw_implementation_env_var}
|
||||
TIMEOUT 120
|
||||
)
|
||||
endif()
|
||||
if(TARGET test_add_callback_groups_to_executor${target_suffix})
|
||||
target_link_libraries(test_add_callback_groups_to_executor${target_suffix} ${PROJECT_NAME})
|
||||
ament_target_dependencies(test_add_callback_groups_to_executor${target_suffix}
|
||||
"test_msgs"
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
call_for_each_rmw_implementation(test_add_callback_groups_to_executor_for_rmw_implementation)
|
||||
ament_add_gtest(test_expand_topic_or_service_name test_expand_topic_or_service_name.cpp)
|
||||
if(TARGET test_expand_topic_or_service_name)
|
||||
ament_target_dependencies(test_expand_topic_or_service_name
|
||||
@@ -229,6 +234,11 @@ if(TARGET test_node_interfaces__node_graph)
|
||||
"test_msgs")
|
||||
target_link_libraries(test_node_interfaces__node_graph ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
ament_add_gtest(test_node_interfaces__node_interfaces
|
||||
node_interfaces/test_node_interfaces.cpp)
|
||||
if(TARGET test_node_interfaces__node_interfaces)
|
||||
target_link_libraries(test_node_interfaces__node_interfaces ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
ament_add_gtest(test_node_interfaces__node_parameters
|
||||
node_interfaces/test_node_parameters.cpp)
|
||||
if(TARGET test_node_interfaces__node_parameters)
|
||||
@@ -257,6 +267,11 @@ ament_add_gtest(test_node_interfaces__node_waitables
|
||||
if(TARGET test_node_interfaces__node_waitables)
|
||||
target_link_libraries(test_node_interfaces__node_waitables ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
ament_add_gtest(test_node_interfaces__test_template_utils # Compile time test
|
||||
node_interfaces/detail/test_template_utils.cpp)
|
||||
if(TARGET test_node_interfaces__test_template_utils)
|
||||
target_link_libraries(test_node_interfaces__test_template_utils ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
# TODO(wjwwood): reenable these build failure tests when I can get Jenkins to ignore their output
|
||||
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_rclcpp_node
|
||||
|
||||
@@ -51,6 +51,53 @@ TEST(TestAllocatorCommon, retyped_allocate) {
|
||||
EXPECT_NO_THROW(code2());
|
||||
}
|
||||
|
||||
TEST(TestAllocatorCommon, retyped_zero_allocate_basic) {
|
||||
std::allocator<int> allocator;
|
||||
void * untyped_allocator = &allocator;
|
||||
void * allocated_mem =
|
||||
rclcpp::allocator::retyped_zero_allocate<std::allocator<char>>(20u, 1u, untyped_allocator);
|
||||
ASSERT_TRUE(nullptr != allocated_mem);
|
||||
|
||||
auto code = [&untyped_allocator, allocated_mem]() {
|
||||
rclcpp::allocator::retyped_deallocate<char, std::allocator<char>>(
|
||||
allocated_mem, untyped_allocator);
|
||||
};
|
||||
EXPECT_NO_THROW(code());
|
||||
}
|
||||
|
||||
TEST(TestAllocatorCommon, retyped_zero_allocate) {
|
||||
std::allocator<int> allocator;
|
||||
void * untyped_allocator = &allocator;
|
||||
void * allocated_mem =
|
||||
rclcpp::allocator::retyped_zero_allocate<std::allocator<char>>(20u, 1u, untyped_allocator);
|
||||
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
|
||||
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
|
||||
ASSERT_TRUE(nullptr != allocated_mem);
|
||||
|
||||
auto code = [&untyped_allocator, allocated_mem]() {
|
||||
rclcpp::allocator::retyped_deallocate<int, std::allocator<int>>(
|
||||
allocated_mem, untyped_allocator);
|
||||
};
|
||||
EXPECT_NO_THROW(code());
|
||||
|
||||
allocated_mem = allocator.allocate(1);
|
||||
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
|
||||
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
|
||||
ASSERT_TRUE(nullptr != allocated_mem);
|
||||
void * reallocated_mem =
|
||||
rclcpp::allocator::retyped_reallocate<int, std::allocator<int>>(
|
||||
allocated_mem, 2u, untyped_allocator);
|
||||
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
|
||||
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
|
||||
ASSERT_TRUE(nullptr != reallocated_mem);
|
||||
|
||||
auto code2 = [&untyped_allocator, reallocated_mem]() {
|
||||
rclcpp::allocator::retyped_deallocate<int, std::allocator<int>>(
|
||||
reallocated_mem, untyped_allocator);
|
||||
};
|
||||
EXPECT_NO_THROW(code2());
|
||||
}
|
||||
|
||||
TEST(TestAllocatorCommon, get_rcl_allocator) {
|
||||
std::allocator<int> allocator;
|
||||
auto rcl_allocator = rclcpp::allocator::get_rcl_allocator<int>(allocator);
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -593,3 +594,134 @@ TEST(TestExecutors, testSpinUntilFutureCompleteNodePtr) {
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
class TestIntraprocessExecutors : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
const auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||
std::stringstream test_name;
|
||||
test_name << test_info->test_case_name() << "_" << test_info->name();
|
||||
node = std::make_shared<rclcpp::Node>("node", test_name.str());
|
||||
|
||||
callback_count = 0u;
|
||||
|
||||
const std::string topic_name = std::string("topic_") + test_name.str();
|
||||
|
||||
rclcpp::PublisherOptions po;
|
||||
po.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
|
||||
publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, rclcpp::QoS(1), po);
|
||||
|
||||
auto callback = [this](test_msgs::msg::Empty::ConstSharedPtr) {
|
||||
this->callback_count.fetch_add(1u);
|
||||
};
|
||||
|
||||
rclcpp::SubscriptionOptions so;
|
||||
so.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
|
||||
subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, rclcpp::QoS(kNumMessages), std::move(callback), so);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
publisher.reset();
|
||||
subscription.reset();
|
||||
node.reset();
|
||||
}
|
||||
|
||||
const size_t kNumMessages = 100;
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr publisher;
|
||||
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
|
||||
std::atomic_size_t callback_count;
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE(TestIntraprocessExecutors, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
|
||||
// This tests that executors will continue to service intraprocess subscriptions in the case
|
||||
// that publishers aren't continuing to publish.
|
||||
// This was previously broken in that intraprocess guard conditions were only triggered on
|
||||
// publish and the test was added to prevent future regressions.
|
||||
const size_t kNumMessages = 100;
|
||||
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
EXPECT_EQ(0u, this->callback_count.load());
|
||||
this->publisher->publish(test_msgs::msg::Empty());
|
||||
|
||||
// Wait for up to 5 seconds for the first message to come available.
|
||||
const std::chrono::milliseconds sleep_per_loop(10);
|
||||
int loops = 0;
|
||||
while (1u != this->callback_count.load() && loops < 500) {
|
||||
rclcpp::sleep_for(sleep_per_loop);
|
||||
executor.spin_some();
|
||||
loops++;
|
||||
}
|
||||
EXPECT_EQ(1u, this->callback_count.load());
|
||||
|
||||
// reset counter
|
||||
this->callback_count.store(0u);
|
||||
|
||||
for (size_t ii = 0; ii < kNumMessages; ++ii) {
|
||||
this->publisher->publish(test_msgs::msg::Empty());
|
||||
}
|
||||
|
||||
// Fire a timer every 10ms up to 5 seconds waiting for subscriptions to be read.
|
||||
loops = 0;
|
||||
auto timer = this->node->create_wall_timer(
|
||||
std::chrono::milliseconds(10), [this, &executor, &loops, &kNumMessages]() {
|
||||
loops++;
|
||||
if (kNumMessages == this->callback_count.load() ||
|
||||
loops == 500)
|
||||
{
|
||||
executor.cancel();
|
||||
}
|
||||
});
|
||||
executor.spin();
|
||||
EXPECT_EQ(kNumMessages, this->callback_count.load());
|
||||
}
|
||||
|
||||
// Check spin functions with non default context
|
||||
TEST(TestExecutors, testSpinWithNonDefaultContext)
|
||||
{
|
||||
auto non_default_context = std::make_shared<rclcpp::Context>();
|
||||
non_default_context->init(0, nullptr);
|
||||
|
||||
{
|
||||
auto node =
|
||||
std::make_unique<rclcpp::Node>("node", rclcpp::NodeOptions().context(non_default_context));
|
||||
|
||||
EXPECT_NO_THROW(rclcpp::spin_some(node->get_node_base_interface()));
|
||||
|
||||
auto check_spin_until_future_complete = [&]() {
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
|
||||
auto shared_future = future.share();
|
||||
auto ret = rclcpp::spin_until_future_complete(
|
||||
node->get_node_base_interface(), shared_future, 1s);
|
||||
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
|
||||
};
|
||||
EXPECT_NO_THROW(check_spin_until_future_complete());
|
||||
}
|
||||
|
||||
rclcpp::shutdown(non_default_context);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
// Copyright 2022 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 "rclcpp/detail/template_contains.hpp"
|
||||
#include "rclcpp/detail/template_unique.hpp"
|
||||
|
||||
|
||||
TEST(NoOpTests, test_node_interfaces_template_utils) {
|
||||
} // This is just to let gtest work
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// This tests template logic at compile time
|
||||
namespace
|
||||
{
|
||||
|
||||
struct test_template_unique
|
||||
{
|
||||
static_assert(template_unique_v<int>, "failed");
|
||||
static_assert(template_unique_v<int, double>, "failed");
|
||||
static_assert(!template_unique_v<int, int>, "failed");
|
||||
static_assert(!template_unique_v<int, double, int>, "failed");
|
||||
static_assert(!template_unique_v<int, int, double>, "failed");
|
||||
};
|
||||
|
||||
|
||||
struct test_template_contains
|
||||
{
|
||||
static_assert(template_contains_v<int, int, double>, "failed");
|
||||
static_assert(template_contains_v<double, int, double>, "failed");
|
||||
static_assert(template_contains_v<int, int>, "failed");
|
||||
static_assert(!template_contains_v<int>, "failed");
|
||||
static_assert(!template_contains_v<int, float>, "failed");
|
||||
static_assert(!template_contains_v<int, float, double>, "failed");
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
306
rclcpp/test/rclcpp/node_interfaces/test_node_interfaces.cpp
Normal file
306
rclcpp/test/rclcpp/node_interfaces/test_node_interfaces.cpp
Normal file
@@ -0,0 +1,306 @@
|
||||
// Copyright 2022 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 "rclcpp/node.hpp"
|
||||
#include "rclcpp/node_interfaces/node_interfaces.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
|
||||
class TestNodeInterfaces : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Testing NodeInterfaces construction from nodes.
|
||||
*/
|
||||
TEST_F(TestNodeInterfaces, node_interfaces_nominal) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node");
|
||||
|
||||
// Create a NodeInterfaces for base and graph using a rclcpp::Node.
|
||||
{
|
||||
using rclcpp::node_interfaces::NodeInterfaces;
|
||||
using rclcpp::node_interfaces::NodeBaseInterface;
|
||||
using rclcpp::node_interfaces::NodeGraphInterface;
|
||||
auto node_interfaces = NodeInterfaces<NodeBaseInterface, NodeGraphInterface>(node);
|
||||
}
|
||||
|
||||
// Implicit conversion of rclcpp::Node into function that uses NodeInterfaces of base.
|
||||
{
|
||||
using rclcpp::node_interfaces::NodeInterfaces;
|
||||
using rclcpp::node_interfaces::NodeBaseInterface;
|
||||
auto some_func = [](NodeInterfaces<NodeBaseInterface> ni) {
|
||||
auto base_interface = ni.get<NodeBaseInterface>();
|
||||
};
|
||||
|
||||
some_func(node);
|
||||
}
|
||||
|
||||
// Implicit narrowing of NodeInterfaces into a new interface NodeInterfaces with fewer interfaces.
|
||||
{
|
||||
using rclcpp::node_interfaces::NodeInterfaces;
|
||||
using rclcpp::node_interfaces::NodeBaseInterface;
|
||||
using rclcpp::node_interfaces::NodeGraphInterface;
|
||||
auto some_func = [](NodeInterfaces<NodeBaseInterface> ni_with_one) {
|
||||
auto base_interface = ni_with_one.get<NodeBaseInterface>();
|
||||
};
|
||||
|
||||
NodeInterfaces<NodeBaseInterface, NodeGraphInterface> ni_with_two(node);
|
||||
|
||||
some_func(ni_with_two);
|
||||
}
|
||||
|
||||
// Create a NodeInterfaces via aggregation of interfaces in constructor.
|
||||
{
|
||||
using rclcpp::node_interfaces::NodeInterfaces;
|
||||
using rclcpp::node_interfaces::NodeBaseInterface;
|
||||
using rclcpp::node_interfaces::NodeGraphInterface;
|
||||
auto loose_node_base = node->get_node_base_interface();
|
||||
auto loose_node_graph = node->get_node_graph_interface();
|
||||
auto ni = NodeInterfaces<NodeBaseInterface, NodeGraphInterface>(
|
||||
loose_node_base,
|
||||
loose_node_graph);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Test construction with all standard rclcpp::node_interfaces::Node*Interfaces.
|
||||
*/
|
||||
TEST_F(TestNodeInterfaces, node_interfaces_standard_interfaces) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
auto ni = rclcpp::node_interfaces::NodeInterfaces<
|
||||
rclcpp::node_interfaces::NodeBaseInterface,
|
||||
rclcpp::node_interfaces::NodeClockInterface,
|
||||
rclcpp::node_interfaces::NodeGraphInterface,
|
||||
rclcpp::node_interfaces::NodeLoggingInterface,
|
||||
rclcpp::node_interfaces::NodeTimersInterface,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface,
|
||||
rclcpp::node_interfaces::NodeServicesInterface,
|
||||
rclcpp::node_interfaces::NodeWaitablesInterface,
|
||||
rclcpp::node_interfaces::NodeParametersInterface,
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface
|
||||
>(node);
|
||||
}
|
||||
|
||||
/*
|
||||
Testing getters.
|
||||
*/
|
||||
TEST_F(TestNodeInterfaces, ni_init) {
|
||||
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;
|
||||
|
||||
auto ni = NodeInterfaces<
|
||||
NodeBaseInterface,
|
||||
NodeClockInterface,
|
||||
NodeGraphInterface,
|
||||
NodeLoggingInterface,
|
||||
NodeTimersInterface,
|
||||
NodeTopicsInterface,
|
||||
NodeServicesInterface,
|
||||
NodeWaitablesInterface,
|
||||
NodeParametersInterface,
|
||||
NodeTimeSourceInterface
|
||||
>(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();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing macro'ed getters.
|
||||
*/
|
||||
TEST_F(TestNodeInterfaces, ni_all_init_non_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;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,8 @@
|
||||
#include "../../mocking_utils/patch.hpp"
|
||||
#include "../../utils/rclcpp_gtest_macros.hpp"
|
||||
|
||||
#include "rcpputils/filesystem_helper.hpp"
|
||||
|
||||
class TestNodeParameters : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
@@ -47,6 +49,7 @@ public:
|
||||
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
|
||||
node->get_node_parameters_interface().get());
|
||||
ASSERT_NE(nullptr, node_parameters);
|
||||
test_resources_path /= "test_node_parameters";
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
@@ -57,6 +60,8 @@ public:
|
||||
protected:
|
||||
std::shared_ptr<rclcpp::Node> node;
|
||||
rclcpp::node_interfaces::NodeParameters * node_parameters;
|
||||
|
||||
rcpputils::fs::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
};
|
||||
|
||||
TEST_F(TestNodeParameters, construct_destruct_rcl_errors) {
|
||||
@@ -199,3 +204,130 @@ TEST_F(TestNodeParameters, add_remove_parameters_callback) {
|
||||
node_parameters->remove_on_set_parameters_callback(handle.get()),
|
||||
std::runtime_error("Callback doesn't exist"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeParameters, wildcard_with_namespace)
|
||||
{
|
||||
rclcpp::NodeOptions opts;
|
||||
opts.arguments(
|
||||
{
|
||||
"--ros-args",
|
||||
"--params-file", (test_resources_path / "wildcards.yaml").string()
|
||||
});
|
||||
|
||||
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node2", "ns", opts);
|
||||
|
||||
auto * node_parameters =
|
||||
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
|
||||
node->get_node_parameters_interface().get());
|
||||
ASSERT_NE(nullptr, node_parameters);
|
||||
|
||||
const auto & parameter_overrides = node_parameters->get_parameter_overrides();
|
||||
EXPECT_EQ(7u, parameter_overrides.size());
|
||||
EXPECT_EQ(parameter_overrides.at("full_wild").get<std::string>(), "full_wild");
|
||||
EXPECT_EQ(parameter_overrides.at("namespace_wild").get<std::string>(), "namespace_wild");
|
||||
EXPECT_EQ(
|
||||
parameter_overrides.at("namespace_wild_another").get<std::string>(),
|
||||
"namespace_wild_another");
|
||||
EXPECT_EQ(
|
||||
parameter_overrides.at("namespace_wild_one_star").get<std::string>(),
|
||||
"namespace_wild_one_star");
|
||||
EXPECT_EQ(parameter_overrides.at("node_wild_in_ns").get<std::string>(), "node_wild_in_ns");
|
||||
EXPECT_EQ(
|
||||
parameter_overrides.at("node_wild_in_ns_another").get<std::string>(),
|
||||
"node_wild_in_ns_another");
|
||||
EXPECT_EQ(parameter_overrides.at("explicit_in_ns").get<std::string>(), "explicit_in_ns");
|
||||
EXPECT_EQ(parameter_overrides.count("should_not_appear"), 0u);
|
||||
}
|
||||
|
||||
TEST_F(TestNodeParameters, wildcard_no_namespace)
|
||||
{
|
||||
rclcpp::NodeOptions opts;
|
||||
opts.arguments(
|
||||
{
|
||||
"--ros-args",
|
||||
"--params-file", (test_resources_path / "wildcards.yaml").string()
|
||||
});
|
||||
|
||||
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node2", opts);
|
||||
|
||||
auto * node_parameters =
|
||||
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
|
||||
node->get_node_parameters_interface().get());
|
||||
ASSERT_NE(nullptr, node_parameters);
|
||||
|
||||
const auto & parameter_overrides = node_parameters->get_parameter_overrides();
|
||||
EXPECT_EQ(5u, parameter_overrides.size());
|
||||
EXPECT_EQ(parameter_overrides.at("full_wild").get<std::string>(), "full_wild");
|
||||
EXPECT_EQ(parameter_overrides.at("namespace_wild").get<std::string>(), "namespace_wild");
|
||||
EXPECT_EQ(
|
||||
parameter_overrides.at("namespace_wild_another").get<std::string>(),
|
||||
"namespace_wild_another");
|
||||
EXPECT_EQ(parameter_overrides.at("node_wild_no_ns").get<std::string>(), "node_wild_no_ns");
|
||||
EXPECT_EQ(parameter_overrides.at("explicit_no_ns").get<std::string>(), "explicit_no_ns");
|
||||
EXPECT_EQ(parameter_overrides.count("should_not_appear"), 0u);
|
||||
// "/*" match exactly one token, not expect to get `namespace_wild_one_star`
|
||||
EXPECT_EQ(parameter_overrides.count("namespace_wild_one_star"), 0u);
|
||||
}
|
||||
|
||||
TEST_F(TestNodeParameters, params_by_order)
|
||||
{
|
||||
rclcpp::NodeOptions opts;
|
||||
opts.arguments(
|
||||
{
|
||||
"--ros-args",
|
||||
"--params-file", (test_resources_path / "params_by_order.yaml").string()
|
||||
});
|
||||
|
||||
std::shared_ptr<rclcpp::Node> node = std::make_shared<rclcpp::Node>("node2", "ns", opts);
|
||||
|
||||
auto * node_parameters =
|
||||
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
|
||||
node->get_node_parameters_interface().get());
|
||||
ASSERT_NE(nullptr, node_parameters);
|
||||
|
||||
const auto & parameter_overrides = node_parameters->get_parameter_overrides();
|
||||
EXPECT_EQ(3u, parameter_overrides.size());
|
||||
EXPECT_EQ(parameter_overrides.at("a_value").get<std::string>(), "last_one_win");
|
||||
EXPECT_EQ(parameter_overrides.at("foo").get<std::string>(), "foo");
|
||||
EXPECT_EQ(parameter_overrides.at("bar").get<std::string>(), "bar");
|
||||
}
|
||||
|
||||
TEST_F(TestNodeParameters, complicated_wildcards)
|
||||
{
|
||||
rclcpp::NodeOptions opts;
|
||||
opts.arguments(
|
||||
{
|
||||
"--ros-args",
|
||||
"--params-file", (test_resources_path / "complicated_wildcards.yaml").string()
|
||||
});
|
||||
|
||||
{
|
||||
// regex matched: /**/foo/*/bar
|
||||
std::shared_ptr<rclcpp::Node> node =
|
||||
std::make_shared<rclcpp::Node>("node2", "/a/b/c/foo/d/bar", opts);
|
||||
|
||||
auto * node_parameters =
|
||||
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
|
||||
node->get_node_parameters_interface().get());
|
||||
ASSERT_NE(nullptr, node_parameters);
|
||||
|
||||
const auto & parameter_overrides = node_parameters->get_parameter_overrides();
|
||||
EXPECT_EQ(2u, parameter_overrides.size());
|
||||
EXPECT_EQ(parameter_overrides.at("foo").get<std::string>(), "foo");
|
||||
EXPECT_EQ(parameter_overrides.at("bar").get<std::string>(), "bar");
|
||||
}
|
||||
|
||||
{
|
||||
// regex not matched: /**/foo/*/bar
|
||||
std::shared_ptr<rclcpp::Node> node =
|
||||
std::make_shared<rclcpp::Node>("node2", "/a/b/c/foo/bar", opts);
|
||||
|
||||
auto * node_parameters =
|
||||
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
|
||||
node->get_node_parameters_interface().get());
|
||||
ASSERT_NE(nullptr, node_parameters);
|
||||
|
||||
const auto & parameter_overrides = node_parameters->get_parameter_overrides();
|
||||
EXPECT_EQ(0u, parameter_overrides.size());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,6 +276,70 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_e
|
||||
ASSERT_EQ(timer_executor.get_all_callback_groups().size(), 2u);
|
||||
}
|
||||
|
||||
/*
|
||||
* Test callback groups from one node to many executors.
|
||||
* A subscriber on a new executor with a callback group not received a message
|
||||
* because the executor can't be triggered while a subscriber created, see
|
||||
* https://github.com/ros2/rclcpp/issues/1611
|
||||
*/
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, subscriber_triggered_to_receive_message)
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
// create a thread running an executor with a new callback group for a coming subscriber
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive, false);
|
||||
rclcpp::executors::SingleThreadedExecutor cb_grp_executor;
|
||||
|
||||
std::promise<bool> received_message_promise;
|
||||
auto received_message_future = received_message_promise.get_future();
|
||||
rclcpp::FutureReturnCode return_code = rclcpp::FutureReturnCode::TIMEOUT;
|
||||
std::thread cb_grp_thread = std::thread(
|
||||
[&cb_grp, &node, &cb_grp_executor, &received_message_future, &return_code]() {
|
||||
cb_grp_executor.add_callback_group(cb_grp, node->get_node_base_interface());
|
||||
return_code = cb_grp_executor.spin_until_future_complete(received_message_future, 10s);
|
||||
});
|
||||
|
||||
// expect the subscriber to receive a message
|
||||
auto sub_callback = [&received_message_promise](test_msgs::msg::Empty::ConstSharedPtr) {
|
||||
received_message_promise.set_value(true);
|
||||
};
|
||||
|
||||
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
|
||||
rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr publisher;
|
||||
// to create a timer with a callback run on another executor
|
||||
rclcpp::TimerBase::SharedPtr timer = nullptr;
|
||||
std::promise<void> timer_promise;
|
||||
auto timer_callback =
|
||||
[&subscription, &publisher, &timer, &cb_grp, &node, &sub_callback, &timer_promise]() {
|
||||
if (timer) {
|
||||
timer.reset();
|
||||
}
|
||||
|
||||
// create a subscription using the `cb_grp` callback group
|
||||
rclcpp::QoS qos = rclcpp::QoS(1).reliable();
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
options.callback_group = cb_grp;
|
||||
subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>("topic_name", qos, sub_callback, options);
|
||||
// create a publisher to send data
|
||||
publisher =
|
||||
node->create_publisher<test_msgs::msg::Empty>("topic_name", qos);
|
||||
publisher->publish(test_msgs::msg::Empty());
|
||||
timer_promise.set_value();
|
||||
};
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor timer_executor;
|
||||
timer = node->create_wall_timer(100ms, timer_callback);
|
||||
timer_executor.add_node(node);
|
||||
auto future = timer_promise.get_future();
|
||||
timer_executor.spin_until_future_complete(future);
|
||||
cb_grp_thread.join();
|
||||
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
|
||||
EXPECT_TRUE(received_message_future.get());
|
||||
}
|
||||
|
||||
/*
|
||||
* Test removing callback group from executor that its not associated with.
|
||||
*/
|
||||
|
||||
@@ -93,6 +93,111 @@ TEST_F(TestAnySubscriptionCallback, construct_destruct) {
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc2(allocator);
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, is_serialized_message_callback) {
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](const rclcpp::SerializedMessage &) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
std::make_shared<rclcpp::SerializedMessage>(),
|
||||
rclcpp::MessageInfo{}));
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](const rclcpp::SerializedMessage &, const rclcpp::MessageInfo &) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
std::make_shared<rclcpp::SerializedMessage>(),
|
||||
rclcpp::MessageInfo{}));
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](const rclcpp::SerializedMessage &, const rclcpp::MessageInfo &) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
std::make_shared<rclcpp::SerializedMessage>(),
|
||||
rclcpp::MessageInfo{}));
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](std::unique_ptr<rclcpp::SerializedMessage>) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
std::make_shared<rclcpp::SerializedMessage>(),
|
||||
rclcpp::MessageInfo{}));
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](std::unique_ptr<rclcpp::SerializedMessage>, const rclcpp::MessageInfo &) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
std::make_shared<rclcpp::SerializedMessage>(),
|
||||
rclcpp::MessageInfo{}));
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](std::shared_ptr<const rclcpp::SerializedMessage>) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
std::make_shared<rclcpp::SerializedMessage>(),
|
||||
rclcpp::MessageInfo{}));
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](std::shared_ptr<const rclcpp::SerializedMessage>, const rclcpp::MessageInfo &) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
std::make_shared<rclcpp::SerializedMessage>(),
|
||||
rclcpp::MessageInfo{}));
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](const std::shared_ptr<const rclcpp::SerializedMessage> &) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
std::make_shared<rclcpp::SerializedMessage>(),
|
||||
rclcpp::MessageInfo{}));
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set(
|
||||
[](
|
||||
const std::shared_ptr<const rclcpp::SerializedMessage> &,
|
||||
const rclcpp::MessageInfo &) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
std::make_shared<rclcpp::SerializedMessage>(),
|
||||
rclcpp::MessageInfo{}));
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](std::shared_ptr<rclcpp::SerializedMessage>) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
std::make_shared<rclcpp::SerializedMessage>(),
|
||||
rclcpp::MessageInfo{}));
|
||||
}
|
||||
{
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
|
||||
asc.set([](std::shared_ptr<rclcpp::SerializedMessage>, const rclcpp::MessageInfo &) {});
|
||||
EXPECT_TRUE(asc.is_serialized_message_callback());
|
||||
EXPECT_NO_THROW(
|
||||
asc.dispatch(
|
||||
std::make_shared<rclcpp::SerializedMessage>(),
|
||||
rclcpp::MessageInfo{}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, unset_dispatch_throw) {
|
||||
EXPECT_THROW(
|
||||
any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_),
|
||||
|
||||
@@ -282,6 +282,27 @@ TEST_F(TestClientWithServer, test_client_remove_pending_request) {
|
||||
EXPECT_TRUE(client->remove_pending_request(future));
|
||||
}
|
||||
|
||||
TEST_F(TestClientWithServer, prune_requests_older_than_no_pruned) {
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
auto future = client->async_send_request(request);
|
||||
auto time = std::chrono::system_clock::now() + 1s;
|
||||
|
||||
EXPECT_EQ(1u, client->prune_requests_older_than(time));
|
||||
}
|
||||
|
||||
TEST_F(TestClientWithServer, prune_requests_older_than_with_pruned) {
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
auto future = client->async_send_request(request);
|
||||
auto time = std::chrono::system_clock::now() + 1s;
|
||||
|
||||
std::vector<int64_t> pruned_requests;
|
||||
EXPECT_EQ(1u, client->prune_requests_older_than(time, &pruned_requests));
|
||||
ASSERT_EQ(1u, pruned_requests.size());
|
||||
EXPECT_EQ(future.request_id, pruned_requests[0]);
|
||||
}
|
||||
|
||||
TEST_F(TestClientWithServer, async_send_request_rcl_send_request_error) {
|
||||
// Checking rcl_send_request in rclcpp::Client::async_send_request()
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_request, RCL_RET_ERROR);
|
||||
|
||||
@@ -93,3 +93,19 @@ TEST_F(TestCreateSubscription, create_with_statistics) {
|
||||
ASSERT_NE(nullptr, subscription);
|
||||
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
|
||||
}
|
||||
|
||||
TEST_F(TestCreateSubscription, create_with_intra_process_com) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
|
||||
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
subscription = rclcpp::create_subscription<test_msgs::msg::Empty>(
|
||||
node, "topic_name", rclcpp::SystemDefaultsQoS(), callback, options);
|
||||
});
|
||||
ASSERT_NE(nullptr, subscription);
|
||||
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -430,7 +430,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) {
|
||||
@@ -482,9 +482,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) {
|
||||
@@ -589,9 +589,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 published message
|
||||
*/
|
||||
TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
|
||||
|
||||
@@ -253,9 +253,19 @@ bool log_function_const_ref(const rclcpp::Logger & logger)
|
||||
return true;
|
||||
}
|
||||
|
||||
class DerivedLogger : public rclcpp::Logger
|
||||
{
|
||||
public:
|
||||
explicit DerivedLogger(const rclcpp::Logger & logger)
|
||||
: rclcpp::Logger(logger) {}
|
||||
};
|
||||
|
||||
TEST_F(TestLoggingMacros, test_log_from_node) {
|
||||
auto logger = rclcpp::get_logger("test_logging_logger");
|
||||
EXPECT_TRUE(log_function(logger));
|
||||
EXPECT_TRUE(log_function_const(logger));
|
||||
EXPECT_TRUE(log_function_const_ref(logger));
|
||||
|
||||
DerivedLogger derived_logger(logger);
|
||||
RCLCPP_INFO(derived_logger, "successful log from derived logger");
|
||||
}
|
||||
|
||||
@@ -72,6 +72,13 @@ protected:
|
||||
/*
|
||||
Testing filters.
|
||||
*/
|
||||
TEST_F(TestParameterEventFilter, invalide_arguments) {
|
||||
EXPECT_THROW(
|
||||
rclcpp::ParameterEventsFilter(nullptr, {"new"}, {nt}),
|
||||
std::invalid_argument
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(TestParameterEventFilter, full_by_type) {
|
||||
auto res = rclcpp::ParameterEventsFilter(
|
||||
full,
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/parameter_map.hpp"
|
||||
@@ -353,3 +354,132 @@ TEST(Test_parameter_map_from, string_array_param_value)
|
||||
c_params->params[0].parameter_values[0].string_array_value = NULL;
|
||||
rcl_yaml_node_struct_fini(c_params);
|
||||
}
|
||||
|
||||
TEST(Test_parameter_map_from, one_node_one_param_by_node_fqn)
|
||||
{
|
||||
rcl_params_t * c_params = make_params({"foo"});
|
||||
make_node_params(c_params, 0, {"string_param"});
|
||||
|
||||
std::string hello_world = "hello world";
|
||||
char * c_hello_world = new char[hello_world.length() + 1];
|
||||
std::snprintf(c_hello_world, hello_world.size() + 1, "%s", hello_world.c_str());
|
||||
c_params->params[0].parameter_values[0].string_value = c_hello_world;
|
||||
|
||||
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params, "/foo");
|
||||
const std::vector<rclcpp::Parameter> & params = map.at("/foo");
|
||||
EXPECT_STREQ("string_param", params.at(0).get_name().c_str());
|
||||
EXPECT_STREQ(hello_world.c_str(), params.at(0).get_value<std::string>().c_str());
|
||||
|
||||
c_params->params[0].parameter_values[0].string_value = NULL;
|
||||
delete[] c_hello_world;
|
||||
rcl_yaml_node_struct_fini(c_params);
|
||||
}
|
||||
|
||||
TEST(Test_parameter_map_from, multi_nodes_same_param_name_by_node_fqn)
|
||||
{
|
||||
std::vector<std::string> node_names_keys = {
|
||||
"/**", // index: 0
|
||||
"/*", // index: 1
|
||||
"/**/node", // index: 2
|
||||
"/*/node", // index: 3
|
||||
"/ns/node" // index: 4
|
||||
};
|
||||
|
||||
rcl_params_t * c_params = make_params(node_names_keys);
|
||||
|
||||
std::vector<char *> param_values;
|
||||
for (size_t i = 0; i < node_names_keys.size(); ++i) {
|
||||
make_node_params(c_params, i, {"string_param"});
|
||||
std::string hello_world = "hello world" + std::to_string(i);
|
||||
char * c_hello_world = new char[hello_world.length() + 1];
|
||||
std::snprintf(c_hello_world, hello_world.size() + 1, "%s", hello_world.c_str());
|
||||
c_params->params[i].parameter_values[0].string_value = c_hello_world;
|
||||
param_values.push_back(c_hello_world);
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::vector<size_t>> node_fqn_expected = {
|
||||
{"/ns/foo/another_node", {0}},
|
||||
{"/another", {0, 1}},
|
||||
{"/node", {0, 1, 2}},
|
||||
{"/another_ns/node", {0, 2, 3}},
|
||||
{"/ns/node", {0, 2, 3, 4}},
|
||||
};
|
||||
|
||||
for (auto & kv : node_fqn_expected) {
|
||||
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params, kv.first.c_str());
|
||||
const std::vector<rclcpp::Parameter> & params = map.at(kv.first);
|
||||
|
||||
EXPECT_EQ(kv.second.size(), params.size());
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
std::string param_value = "hello world" + std::to_string(kv.second[i]);
|
||||
EXPECT_STREQ("string_param", params.at(i).get_name().c_str());
|
||||
EXPECT_STREQ(param_value.c_str(), params.at(i).get_value<std::string>().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < node_names_keys.size(); ++i) {
|
||||
c_params->params[i].parameter_values[0].string_value = NULL;
|
||||
}
|
||||
for (auto c_hello_world : param_values) {
|
||||
delete[] c_hello_world;
|
||||
}
|
||||
rcl_yaml_node_struct_fini(c_params);
|
||||
}
|
||||
|
||||
TEST(Test_parameter_map_from, multi_nodes_diff_param_name_by_node_fqn)
|
||||
{
|
||||
std::vector<std::string> node_names_keys = {
|
||||
"/**", // index: 0
|
||||
"/*", // index: 1
|
||||
"/**/node", // index: 2
|
||||
"/*/node", // index: 3
|
||||
"/ns/**", // index: 4
|
||||
"/ns/*", // index: 5
|
||||
"/ns/**/node", // index: 6
|
||||
"/ns/*/node", // index: 7
|
||||
"/ns/**/a/*/node", // index: 8
|
||||
"/ns/node" // index: 9
|
||||
};
|
||||
|
||||
rcl_params_t * c_params = make_params(node_names_keys);
|
||||
|
||||
for (size_t i = 0; i < node_names_keys.size(); ++i) {
|
||||
std::string param_name = "string_param" + std::to_string(i);
|
||||
make_node_params(c_params, i, {param_name});
|
||||
}
|
||||
|
||||
std::string hello_world = "hello world";
|
||||
char * c_hello_world = new char[hello_world.length() + 1];
|
||||
std::snprintf(c_hello_world, hello_world.size() + 1, "%s", hello_world.c_str());
|
||||
|
||||
for (size_t i = 0; i < node_names_keys.size(); ++i) {
|
||||
c_params->params[i].parameter_values[0].string_value = c_hello_world;
|
||||
}
|
||||
|
||||
std::unordered_map<std::string, std::vector<size_t>> node_fqn_expected = {
|
||||
{"/ns/node", {0, 2, 3, 4, 5, 6, 9}},
|
||||
{"/node", {0, 1, 2}},
|
||||
{"/ns/foo/node", {0, 2, 4, 6, 7}},
|
||||
{"/ns/foo/a/node", {0, 2, 4, 6}},
|
||||
{"/ns/foo/a/bar/node", {0, 2, 4, 6, 8}},
|
||||
{"/ns/a/bar/node", {0, 2, 4, 6, 8}},
|
||||
{"/ns/foo/zoo/a/bar/node", {0, 2, 4, 6, 8}},
|
||||
};
|
||||
|
||||
for (auto & kv : node_fqn_expected) {
|
||||
rclcpp::ParameterMap map = rclcpp::parameter_map_from(c_params, kv.first.c_str());
|
||||
const std::vector<rclcpp::Parameter> & params = map.at(kv.first);
|
||||
EXPECT_EQ(kv.second.size(), params.size());
|
||||
for (size_t i = 0; i < params.size(); ++i) {
|
||||
std::string param_name = "string_param" + std::to_string(kv.second[i]);
|
||||
EXPECT_STREQ(param_name.c_str(), params.at(i).get_name().c_str());
|
||||
EXPECT_STREQ(hello_world.c_str(), params.at(i).get_value<std::string>().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < node_names_keys.size(); ++i) {
|
||||
c_params->params[i].parameter_values[0].string_value = NULL;
|
||||
}
|
||||
delete[] c_hello_world;
|
||||
rcl_yaml_node_struct_fini(c_params);
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -176,6 +176,23 @@ TEST_F(TestPublisher, various_creation_signatures) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing publisher with intraprocess enabled and SystemDefaultQoS
|
||||
*/
|
||||
TEST_F(TestPublisher, test_publisher_with_system_default_qos) {
|
||||
initialize(rclcpp::NodeOptions().use_intra_process_comms(false));
|
||||
// explicitly enable intra-process comm with publisher option
|
||||
auto options = rclcpp::PublisherOptions();
|
||||
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
|
||||
// intraprocess communication allowed only with volatile durability
|
||||
rclcpp::QoS qos = rclcpp::QoS(10).durability_volatile();
|
||||
using test_msgs::msg::Empty;
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
auto publisher = node->create_publisher<Empty>("topic", qos, options);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
Testing publisher with intraprocess enabled and invalid QoS
|
||||
*/
|
||||
@@ -418,11 +435,10 @@ TEST_F(TestPublisher, intra_process_publish_failures) {
|
||||
publisher->publish(std::move(loaned_msg)),
|
||||
std::runtime_error("loaned message is not valid"));
|
||||
}
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node->create_publisher<test_msgs::msg::Empty>(
|
||||
"topic", rclcpp::QoS(0), options),
|
||||
std::invalid_argument(
|
||||
"intraprocess communication is not allowed with a zero qos history depth value"));
|
||||
// a zero depth with KEEP_LAST doesn't make sense,
|
||||
// this will be interpreted as SystemDefaultQoS by rclcpp.
|
||||
EXPECT_NO_THROW(
|
||||
node->create_publisher<test_msgs::msg::Empty>("topic", rclcpp::QoS(0), options));
|
||||
}
|
||||
|
||||
TEST_F(TestPublisher, inter_process_publish_failures) {
|
||||
|
||||
@@ -152,33 +152,54 @@ TEST_F(TestPublisher, conversion_exception_is_passed_up) {
|
||||
}
|
||||
}
|
||||
|
||||
using UseTakeSharedMethod = bool;
|
||||
class TestPublisherFixture
|
||||
: public TestPublisher,
|
||||
public ::testing::WithParamInterface<UseTakeSharedMethod>
|
||||
{
|
||||
};
|
||||
|
||||
/*
|
||||
* Testing that publisher sends type adapted types and ROS message types with intra proccess communications.
|
||||
*/
|
||||
TEST_F(
|
||||
TestPublisher,
|
||||
TEST_P(
|
||||
TestPublisherFixture,
|
||||
check_type_adapted_message_is_sent_and_received_intra_process) {
|
||||
using StringTypeAdapter = rclcpp::TypeAdapter<std::string, rclcpp::msg::String>;
|
||||
const std::string message_data = "Message Data";
|
||||
const std::string topic_name = "topic_name";
|
||||
bool is_received;
|
||||
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const rclcpp::msg::String::ConstSharedPtr msg,
|
||||
const rclcpp::MessageInfo & message_info
|
||||
) -> void
|
||||
{
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
|
||||
auto node = rclcpp::Node::make_shared(
|
||||
"test_intra_process",
|
||||
rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||
auto pub = node->create_publisher<StringTypeAdapter>(topic_name, 10);
|
||||
auto sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback);
|
||||
rclcpp::Subscription<rclcpp::msg::String>::SharedPtr sub;
|
||||
if (GetParam()) {
|
||||
auto callback =
|
||||
[message_data, &is_received](
|
||||
const rclcpp::msg::String::ConstSharedPtr msg,
|
||||
const rclcpp::MessageInfo & message_info
|
||||
) -> void
|
||||
{
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback);
|
||||
} else {
|
||||
auto callback_unique =
|
||||
[message_data, &is_received](
|
||||
rclcpp::msg::String::UniquePtr msg,
|
||||
const rclcpp::MessageInfo & message_info
|
||||
) -> void
|
||||
{
|
||||
is_received = true;
|
||||
ASSERT_STREQ(message_data.c_str(), msg->data.c_str());
|
||||
ASSERT_TRUE(message_info.get_rmw_message_info().from_intra_process);
|
||||
};
|
||||
sub = node->create_subscription<rclcpp::msg::String>(topic_name, 1, callback_unique);
|
||||
}
|
||||
|
||||
auto wait_for_message_to_be_received = [&is_received, &node]() {
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
@@ -239,6 +260,14 @@ TEST_F(
|
||||
}
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TestPublisherFixtureWithParam,
|
||||
TestPublisherFixture,
|
||||
::testing::Values(
|
||||
true, // use take shared method
|
||||
false // not use take shared method
|
||||
));
|
||||
|
||||
/*
|
||||
* Testing that publisher sends type adapted types and ROS message types with inter proccess communications.
|
||||
*/
|
||||
|
||||
@@ -250,3 +250,16 @@ TEST(TestQoS, qos_check_compatible)
|
||||
EXPECT_FALSE(ret.reason.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestQoS, from_rmw_validity)
|
||||
{
|
||||
rmw_qos_profile_t invalid_qos;
|
||||
memset(&invalid_qos, 0, sizeof(invalid_qos));
|
||||
unsigned int n = 999;
|
||||
memcpy(&invalid_qos.history, &n, sizeof(n));
|
||||
|
||||
EXPECT_THROW(
|
||||
{
|
||||
rclcpp::QoSInitialization::from_rmw(invalid_qos);
|
||||
}, std::invalid_argument);
|
||||
}
|
||||
|
||||
@@ -18,10 +18,24 @@
|
||||
|
||||
#include "rclcpp/rate.hpp"
|
||||
|
||||
class TestRate : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Basic tests for the Rate and WallRate classes.
|
||||
*/
|
||||
TEST(TestRate, rate_basics) {
|
||||
TEST_F(TestRate, rate_basics) {
|
||||
auto period = std::chrono::milliseconds(1000);
|
||||
auto offset = std::chrono::milliseconds(500);
|
||||
auto epsilon = std::chrono::milliseconds(100);
|
||||
@@ -61,7 +75,7 @@ TEST(TestRate, rate_basics) {
|
||||
ASSERT_TRUE(epsilon > delta);
|
||||
}
|
||||
|
||||
TEST(TestRate, wall_rate_basics) {
|
||||
TEST_F(TestRate, wall_rate_basics) {
|
||||
auto period = std::chrono::milliseconds(100);
|
||||
auto offset = std::chrono::milliseconds(50);
|
||||
auto epsilon = std::chrono::milliseconds(1);
|
||||
@@ -101,7 +115,7 @@ TEST(TestRate, wall_rate_basics) {
|
||||
EXPECT_GT(epsilon, delta);
|
||||
}
|
||||
|
||||
TEST(TestRate, from_double) {
|
||||
TEST_F(TestRate, from_double) {
|
||||
{
|
||||
rclcpp::WallRate rate(1.0);
|
||||
EXPECT_EQ(std::chrono::seconds(1), rate.period());
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@@ -310,3 +310,25 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(
|
||||
CLASSNAME(
|
||||
TestContentFilterSubscription,
|
||||
RMW_IMPLEMENTATION), create_two_content_filters_with_same_topic_name_and_destroy) {
|
||||
|
||||
// Create another content filter
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
|
||||
std::string filter_expression = "int32_value > %0";
|
||||
std::vector<std::string> expression_parameters = {"4"};
|
||||
|
||||
options.content_filter_options.filter_expression = filter_expression;
|
||||
options.content_filter_options.expression_parameters = expression_parameters;
|
||||
|
||||
auto callback = [](std::shared_ptr<const test_msgs::msg::BasicTypes>) {};
|
||||
auto sub_2 = node->create_subscription<test_msgs::msg::BasicTypes>(
|
||||
"content_filter_topic", qos, callback, options);
|
||||
|
||||
EXPECT_NE(nullptr, sub_2);
|
||||
sub_2.reset();
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/time.h"
|
||||
@@ -848,3 +849,72 @@ TEST_F(TestClockSleep, sleep_for_basic_ros) {
|
||||
sleep_thread.join();
|
||||
EXPECT_TRUE(sleep_succeeded);
|
||||
}
|
||||
|
||||
class TestClockStarted : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestClockStarted, started) {
|
||||
// rclcpp::Clock ros_clock(RCL_ROS_TIME);
|
||||
// auto ros_clock_handle = ros_clock.get_clock_handle();
|
||||
//
|
||||
// // At this point, the ROS clock is reading system time since the ROS time override isn't on
|
||||
// // So we expect it to be started (it's extremely unlikely that system time is at epoch start)
|
||||
// EXPECT_TRUE(ros_clock.started());
|
||||
// EXPECT_TRUE(ros_clock.wait_until_started());
|
||||
// EXPECT_TRUE(ros_clock.wait_until_started(rclcpp::Duration(0, static_cast<uint32_t>(1e7))));
|
||||
// EXPECT_EQ(RCL_RET_OK, rcl_enable_ros_time_override(ros_clock_handle));
|
||||
// EXPECT_TRUE(ros_clock.ros_time_is_active());
|
||||
// EXPECT_FALSE(ros_clock.started());
|
||||
// EXPECT_EQ(RCL_RET_OK, rcl_set_ros_time_override(ros_clock_handle, 1));
|
||||
// EXPECT_TRUE(ros_clock.started());
|
||||
//
|
||||
// rclcpp::Clock system_clock(RCL_SYSTEM_TIME);
|
||||
// EXPECT_TRUE(system_clock.started());
|
||||
// EXPECT_TRUE(system_clock.wait_until_started());
|
||||
// EXPECT_TRUE(system_clock.wait_until_started(rclcpp::Duration(0, static_cast<uint32_t>(1e7))));
|
||||
//
|
||||
// rclcpp::Clock steady_clock(RCL_STEADY_TIME);
|
||||
// EXPECT_TRUE(steady_clock.started());
|
||||
// EXPECT_TRUE(steady_clock.wait_until_started());
|
||||
// EXPECT_TRUE(steady_clock.wait_until_started(rclcpp::Duration(0, static_cast<uint32_t>(1e7))));
|
||||
//
|
||||
// rclcpp::Clock uninit_clock(RCL_CLOCK_UNINITIALIZED);
|
||||
// RCLCPP_EXPECT_THROW_EQ(
|
||||
// uninit_clock.started(), std::runtime_error("clock is not rcl_clock_valid"));
|
||||
// RCLCPP_EXPECT_THROW_EQ(
|
||||
// uninit_clock.wait_until_started(rclcpp::Duration(0, static_cast<uint32_t>(1e7))),
|
||||
// std::runtime_error("clock cannot be waited on as it is not rcl_clock_valid"));
|
||||
}
|
||||
|
||||
TEST_F(TestClockStarted, started_timeout) {
|
||||
rclcpp::Clock ros_clock(RCL_ROS_TIME);
|
||||
auto ros_clock_handle = ros_clock.get_clock_handle();
|
||||
|
||||
EXPECT_EQ(RCL_RET_OK, rcl_enable_ros_time_override(ros_clock_handle));
|
||||
EXPECT_TRUE(ros_clock.ros_time_is_active());
|
||||
|
||||
EXPECT_EQ(RCL_RET_OK, rcl_set_ros_time_override(ros_clock_handle, 0));
|
||||
|
||||
EXPECT_FALSE(ros_clock.started());
|
||||
EXPECT_FALSE(ros_clock.wait_until_started(rclcpp::Duration(0, static_cast<uint32_t>(1e7))));
|
||||
|
||||
std::thread t([]() {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
rclcpp::shutdown();
|
||||
});
|
||||
|
||||
// Test rclcpp shutdown escape hatch (otherwise this waits indefinitely)
|
||||
EXPECT_FALSE(ros_clock.wait_until_started());
|
||||
t.join();
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -107,3 +108,58 @@ TEST(TestUtilities, wait_for_message_twice_one_sub) {
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestUtilities, wait_for_last_message) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("wait_for_last_message_node");
|
||||
auto qos = rclcpp::QoS(1).reliable().transient_local();
|
||||
|
||||
using MsgT = test_msgs::msg::Strings;
|
||||
auto pub = node->create_publisher<MsgT>("wait_for_last_message_topic", qos);
|
||||
pub->publish(*get_messages_strings()[0]);
|
||||
|
||||
MsgT out;
|
||||
auto received = false;
|
||||
auto wait = std::async(
|
||||
[&]() {
|
||||
auto ret = rclcpp::wait_for_message(out, node, "wait_for_last_message_topic", 5s, qos);
|
||||
EXPECT_TRUE(ret);
|
||||
received = true;
|
||||
});
|
||||
|
||||
ASSERT_NO_THROW(wait.get());
|
||||
ASSERT_TRUE(received);
|
||||
EXPECT_EQ(out, *get_messages_strings()[0]);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
/**/foo/*/bar:
|
||||
node2:
|
||||
ros__parameters:
|
||||
foo: "foo"
|
||||
bar: "bar"
|
||||
@@ -0,0 +1,16 @@
|
||||
/**:
|
||||
node2:
|
||||
ros__parameters:
|
||||
a_value: "first"
|
||||
foo: "foo"
|
||||
|
||||
/ns:
|
||||
node2:
|
||||
ros__parameters:
|
||||
a_value: "second"
|
||||
bar: "bar"
|
||||
|
||||
/*:
|
||||
node2:
|
||||
ros__parameters:
|
||||
a_value: "last_one_win"
|
||||
57
rclcpp/test/resources/test_node_parameters/wildcards.yaml
Normal file
57
rclcpp/test/resources/test_node_parameters/wildcards.yaml
Normal file
@@ -0,0 +1,57 @@
|
||||
/**:
|
||||
ros__parameters:
|
||||
full_wild: "full_wild"
|
||||
|
||||
/**:
|
||||
node2:
|
||||
ros__parameters:
|
||||
namespace_wild: "namespace_wild"
|
||||
|
||||
/**/node2:
|
||||
ros__parameters:
|
||||
namespace_wild_another: "namespace_wild_another"
|
||||
|
||||
/*:
|
||||
node2:
|
||||
ros__parameters:
|
||||
namespace_wild_one_star: "namespace_wild_one_star"
|
||||
|
||||
ns:
|
||||
"*":
|
||||
ros__parameters:
|
||||
node_wild_in_ns: "node_wild_in_ns"
|
||||
|
||||
/ns/*:
|
||||
ros__parameters:
|
||||
node_wild_in_ns_another: "node_wild_in_ns_another"
|
||||
|
||||
ns:
|
||||
node2:
|
||||
ros__parameters:
|
||||
explicit_in_ns: "explicit_in_ns"
|
||||
|
||||
"*":
|
||||
ros__parameters:
|
||||
node_wild_no_ns: "node_wild_no_ns"
|
||||
|
||||
node2:
|
||||
ros__parameters:
|
||||
explicit_no_ns: "explicit_no_ns"
|
||||
|
||||
ns:
|
||||
nodeX:
|
||||
ros__parameters:
|
||||
should_not_appear: "incorrect_node_name"
|
||||
|
||||
/**/nodeX:
|
||||
ros__parameters:
|
||||
should_not_appear: "incorrect_node_name"
|
||||
|
||||
nsX:
|
||||
node2:
|
||||
ros__parameters:
|
||||
should_not_appear: "incorrect_namespace"
|
||||
|
||||
/nsX/*:
|
||||
ros__parameters:
|
||||
should_not_appear: "incorrect_namespace"
|
||||
@@ -3,6 +3,73 @@ Changelog for package rclcpp_action
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
16.0.18 (2026-02-09)
|
||||
--------------------
|
||||
* Update exception documentation for goal cancellation in ServerGoalHandle (`#3019 <https://github.com/ros2/rclcpp/issues/3019>`_) (`#3024 <https://github.com/ros2/rclcpp/issues/3024>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.17 (2025-12-23)
|
||||
--------------------
|
||||
|
||||
16.0.16 (2025-11-18)
|
||||
--------------------
|
||||
* Fix REP url locations (backport `#2987 <https://github.com/ros2/rclcpp/issues/2987>`_) (`#2991 <https://github.com/ros2/rclcpp/issues/2991>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.15 (2025-09-11)
|
||||
--------------------
|
||||
|
||||
16.0.14 (2025-07-16)
|
||||
--------------------
|
||||
|
||||
16.0.13 (2025-06-23)
|
||||
--------------------
|
||||
* Replace std::default_random_engine with std::mt19937 (humble) (`#2847 <https://github.com/ros2/rclcpp/issues/2847>`_)
|
||||
* Added missing chrono includes (backport `#2854 <https://github.com/ros2/rclcpp/issues/2854>`_) (`#2857 <https://github.com/ros2/rclcpp/issues/2857>`_)
|
||||
* Harden rclcpp_action::convert(). (backport `#2786 <https://github.com/ros2/rclcpp/issues/2786>`_) (`#2788 <https://github.com/ros2/rclcpp/issues/2788>`_)
|
||||
* Contributors: keeponoiro, mergify[bot]
|
||||
|
||||
16.0.12 (2025-03-25)
|
||||
--------------------
|
||||
|
||||
16.0.11 (2024-11-25)
|
||||
--------------------
|
||||
* fix: Fixed race condition in action server between is_ready and take. Backport from iron `#2531 <https://github.com/ros2/rclcpp/issues/2531>`_ (`#2635 <https://github.com/ros2/rclcpp/issues/2635>`_)
|
||||
* Contributors: Camilo Camacho
|
||||
|
||||
16.0.10 (2024-07-26)
|
||||
--------------------
|
||||
|
||||
16.0.9 (2024-05-15)
|
||||
-------------------
|
||||
* Do not generate the exception when action service response timeout. (`#2464 <https://github.com/ros2/rclcpp/issues/2464>`_) (`#2518 <https://github.com/ros2/rclcpp/issues/2518>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.8 (2024-01-24)
|
||||
-------------------
|
||||
|
||||
16.0.7 (2023-11-13)
|
||||
-------------------
|
||||
|
||||
16.0.6 (2023-09-19)
|
||||
-------------------
|
||||
|
||||
16.0.5 (2023-07-17)
|
||||
-------------------
|
||||
|
||||
16.0.4 (2023-04-25)
|
||||
-------------------
|
||||
* Revert "Revert "extract the result response before the callback is issued. (`#2133 <https://github.com/ros2/rclcpp/issues/2133>`_)" (`#2148 <https://github.com/ros2/rclcpp/issues/2148>`_)" (`#2152 <https://github.com/ros2/rclcpp/issues/2152>`_)
|
||||
* Revert "extract the result response before the callback is issued. (`#2133 <https://github.com/ros2/rclcpp/issues/2133>`_)" (`#2148 <https://github.com/ros2/rclcpp/issues/2148>`_)
|
||||
* extract the result response before the callback is issued. (`#2133 <https://github.com/ros2/rclcpp/issues/2133>`_)
|
||||
* Contributors: Tomoya Fujita
|
||||
|
||||
16.0.3 (2023-01-10)
|
||||
-------------------
|
||||
|
||||
16.0.2 (2022-11-07)
|
||||
-------------------
|
||||
|
||||
16.0.1 (2022-04-13)
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -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 Quality Categories in REP-2004](https://www.ros.org/reps/rep-2004.html) of the ROS2 developer guide.
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://reps.openrobotics.org/rep-2004/) of the ROS2 developer guide.
|
||||
|
||||
## 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/).
|
||||
|
||||
@@ -200,7 +200,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.
|
||||
*/
|
||||
|
||||
@@ -39,12 +39,22 @@ RCLCPP_ACTION_PUBLIC
|
||||
std::string
|
||||
to_string(const GoalUUID & goal_id);
|
||||
|
||||
// Convert C++ GoalID to rcl_action_goal_info_t
|
||||
/// Convert C++ GoalID to rcl_action_goal_info_t
|
||||
/**
|
||||
* \param[in] goal_id C++ GoalUUID reference to be converted.
|
||||
* \param[inout] info rcl_action_goal_info_t structure to be filled.
|
||||
* \throws std::runtime_error if info is null.
|
||||
*/
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
void
|
||||
convert(const GoalUUID & goal_id, rcl_action_goal_info_t * info);
|
||||
|
||||
// Convert rcl_action_goal_info_t to C++ GoalID
|
||||
/// Convert rcl_action_goal_info_t to C++ GoalID
|
||||
/**
|
||||
* \param[in] info rcl_action_goal_info_t reference to be converted.
|
||||
* \param[inout] goal_id C++ GoalUUID structure to be filled.
|
||||
* \throws std::runtime_error if goal_id is null.
|
||||
*/
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
void
|
||||
convert(const rcl_action_goal_info_t & info, GoalUUID * goal_id);
|
||||
|
||||
@@ -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>16.0.1</version>
|
||||
<version>16.0.18</version>
|
||||
<description>Adds action APIs for C++.</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="jacob@openrobotics.org">Jacob Perron</maintainer>
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "rcl_action/action_client.h"
|
||||
#include "rcl_action/wait.h"
|
||||
@@ -31,6 +32,67 @@
|
||||
namespace rclcpp_action
|
||||
{
|
||||
|
||||
struct ClientBaseData
|
||||
{
|
||||
struct FeedbackReadyData
|
||||
{
|
||||
FeedbackReadyData(rcl_ret_t retIn, std::shared_ptr<void> msg)
|
||||
: ret(retIn), feedback_message(msg) {}
|
||||
rcl_ret_t ret;
|
||||
std::shared_ptr<void> feedback_message;
|
||||
};
|
||||
struct StatusReadyData
|
||||
{
|
||||
StatusReadyData(rcl_ret_t retIn, std::shared_ptr<void> msg)
|
||||
: ret(retIn), status_message(msg) {}
|
||||
rcl_ret_t ret;
|
||||
std::shared_ptr<void> status_message;
|
||||
};
|
||||
struct GoalResponseData
|
||||
{
|
||||
GoalResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr<void> response)
|
||||
: ret(retIn), response_header(header), goal_response(response) {}
|
||||
rcl_ret_t ret;
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> goal_response;
|
||||
};
|
||||
struct CancelResponseData
|
||||
{
|
||||
CancelResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr<void> response)
|
||||
: ret(retIn), response_header(header), cancel_response(response) {}
|
||||
rcl_ret_t ret;
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> cancel_response;
|
||||
};
|
||||
struct ResultResponseData
|
||||
{
|
||||
ResultResponseData(rcl_ret_t retIn, rmw_request_id_t header, std::shared_ptr<void> response)
|
||||
: ret(retIn), response_header(header), result_response(response) {}
|
||||
rcl_ret_t ret;
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> result_response;
|
||||
};
|
||||
|
||||
std::variant<
|
||||
FeedbackReadyData,
|
||||
StatusReadyData,
|
||||
GoalResponseData,
|
||||
CancelResponseData,
|
||||
ResultResponseData
|
||||
> data;
|
||||
|
||||
explicit ClientBaseData(FeedbackReadyData && data_in)
|
||||
: data(std::move(data_in)) {}
|
||||
explicit ClientBaseData(StatusReadyData && data_in)
|
||||
: data(std::move(data_in)) {}
|
||||
explicit ClientBaseData(GoalResponseData && data_in)
|
||||
: data(std::move(data_in)) {}
|
||||
explicit ClientBaseData(CancelResponseData && data_in)
|
||||
: data(std::move(data_in)) {}
|
||||
explicit ClientBaseData(ResultResponseData && data_in)
|
||||
: data(std::move(data_in)) {}
|
||||
};
|
||||
|
||||
class ClientBaseImpl
|
||||
{
|
||||
public:
|
||||
@@ -94,11 +156,13 @@ public:
|
||||
size_t num_clients{0u};
|
||||
size_t num_services{0u};
|
||||
|
||||
bool is_feedback_ready{false};
|
||||
bool is_status_ready{false};
|
||||
bool is_goal_response_ready{false};
|
||||
bool is_cancel_response_ready{false};
|
||||
bool is_result_response_ready{false};
|
||||
// Lock for action_client_
|
||||
std::recursive_mutex action_client_mutex_;
|
||||
|
||||
// next ready event for taking, will be set by is_ready and will be processed by take_data
|
||||
std::atomic<size_t> next_ready_event;
|
||||
// used to indicate that next_ready_event has no ready event for processing
|
||||
static constexpr size_t NO_EVENT_READY = std::numeric_limits<size_t>::max();
|
||||
|
||||
rclcpp::Context::SharedPtr context_;
|
||||
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
|
||||
@@ -119,7 +183,7 @@ public:
|
||||
std::mutex cancel_requests_mutex;
|
||||
|
||||
std::independent_bits_engine<
|
||||
std::default_random_engine, 8, unsigned int> random_bytes_generator;
|
||||
std::mt19937, 8, unsigned int> random_bytes_generator;
|
||||
};
|
||||
|
||||
ClientBase::ClientBase(
|
||||
@@ -143,6 +207,7 @@ bool
|
||||
ClientBase::action_server_is_ready() const
|
||||
{
|
||||
bool is_ready;
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
|
||||
rcl_ret_t ret = rcl_action_server_is_available(
|
||||
this->pimpl_->node_handle.get(),
|
||||
this->pimpl_->client_handle.get(),
|
||||
@@ -256,6 +321,7 @@ ClientBase::get_number_of_ready_services()
|
||||
void
|
||||
ClientBase::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
|
||||
rcl_ret_t ret = rcl_action_wait_set_add_action_client(
|
||||
wait_set, pimpl_->client_handle.get(), nullptr, nullptr);
|
||||
if (RCL_RET_OK != ret) {
|
||||
@@ -266,23 +332,56 @@ ClientBase::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
bool
|
||||
ClientBase::is_ready(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
rcl_ret_t ret = rcl_action_client_wait_set_get_entities_ready(
|
||||
wait_set, pimpl_->client_handle.get(),
|
||||
&pimpl_->is_feedback_ready,
|
||||
&pimpl_->is_status_ready,
|
||||
&pimpl_->is_goal_response_ready,
|
||||
&pimpl_->is_cancel_response_ready,
|
||||
&pimpl_->is_result_response_ready);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "failed to check for any ready entities");
|
||||
bool is_feedback_ready{false};
|
||||
bool is_status_ready{false};
|
||||
bool is_goal_response_ready{false};
|
||||
bool is_cancel_response_ready{false};
|
||||
bool is_result_response_ready{false};
|
||||
|
||||
rcl_ret_t ret;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
|
||||
ret = rcl_action_client_wait_set_get_entities_ready(
|
||||
wait_set, pimpl_->client_handle.get(),
|
||||
&is_feedback_ready,
|
||||
&is_status_ready,
|
||||
&is_goal_response_ready,
|
||||
&is_cancel_response_ready,
|
||||
&is_result_response_ready);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "failed to check for any ready entities");
|
||||
}
|
||||
}
|
||||
return
|
||||
pimpl_->is_feedback_ready ||
|
||||
pimpl_->is_status_ready ||
|
||||
pimpl_->is_goal_response_ready ||
|
||||
pimpl_->is_cancel_response_ready ||
|
||||
pimpl_->is_result_response_ready;
|
||||
|
||||
pimpl_->next_ready_event = ClientBaseImpl::NO_EVENT_READY;
|
||||
|
||||
if (is_feedback_ready) {
|
||||
pimpl_->next_ready_event = static_cast<size_t>(EntityType::FeedbackSubscription);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_status_ready) {
|
||||
pimpl_->next_ready_event = static_cast<size_t>(EntityType::StatusSubscription);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_goal_response_ready) {
|
||||
pimpl_->next_ready_event = static_cast<size_t>(EntityType::GoalClient);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_result_response_ready) {
|
||||
pimpl_->next_ready_event = static_cast<size_t>(EntityType::ResultClient);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (is_cancel_response_ready) {
|
||||
pimpl_->next_ready_event = static_cast<size_t>(EntityType::CancelClient);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -319,14 +418,18 @@ ClientBase::handle_result_response(
|
||||
const rmw_request_id_t & response_header,
|
||||
std::shared_ptr<void> response)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(pimpl_->result_requests_mutex);
|
||||
const int64_t & sequence_number = response_header.sequence_number;
|
||||
if (pimpl_->pending_result_responses.count(sequence_number) == 0) {
|
||||
RCLCPP_ERROR(pimpl_->logger, "unknown result response, ignoring...");
|
||||
return;
|
||||
ResponseCallback response_callback;
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(pimpl_->result_requests_mutex);
|
||||
const int64_t & sequence_number = response_header.sequence_number;
|
||||
if (pimpl_->pending_result_responses.count(sequence_number) == 0) {
|
||||
RCLCPP_ERROR(pimpl_->logger, "unknown result response, ignoring...");
|
||||
return;
|
||||
}
|
||||
response_callback = std::move(pimpl_->pending_result_responses[sequence_number]);
|
||||
pimpl_->pending_result_responses.erase(sequence_number);
|
||||
}
|
||||
pimpl_->pending_result_responses[sequence_number](response);
|
||||
pimpl_->pending_result_responses.erase(sequence_number);
|
||||
response_callback(response);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -428,7 +531,6 @@ ClientBase::set_callback_to_entity(
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 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
|
||||
// been replaced but the middleware hasn't been told about the new one yet.
|
||||
@@ -546,140 +648,155 @@ ClientBase::clear_on_ready_callback()
|
||||
std::shared_ptr<void>
|
||||
ClientBase::take_data()
|
||||
{
|
||||
if (pimpl_->is_feedback_ready) {
|
||||
std::shared_ptr<void> feedback_message = this->create_feedback_message();
|
||||
rcl_ret_t ret = rcl_action_take_feedback(
|
||||
pimpl_->client_handle.get(), feedback_message.get());
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(
|
||||
ret, feedback_message));
|
||||
} else if (pimpl_->is_status_ready) {
|
||||
std::shared_ptr<void> status_message = this->create_status_message();
|
||||
rcl_ret_t ret = rcl_action_take_status(
|
||||
pimpl_->client_handle.get(), status_message.get());
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(
|
||||
ret, status_message));
|
||||
} else if (pimpl_->is_goal_response_ready) {
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> goal_response = this->create_goal_response();
|
||||
rcl_ret_t ret = rcl_action_take_goal_response(
|
||||
pimpl_->client_handle.get(), &response_header, goal_response.get());
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
|
||||
ret, response_header, goal_response));
|
||||
} else if (pimpl_->is_result_response_ready) {
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> result_response = this->create_result_response();
|
||||
rcl_ret_t ret = rcl_action_take_result_response(
|
||||
pimpl_->client_handle.get(), &response_header, result_response.get());
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
|
||||
ret, response_header, result_response));
|
||||
} else if (pimpl_->is_cancel_response_ready) {
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> cancel_response = this->create_cancel_response();
|
||||
rcl_ret_t ret = rcl_action_take_cancel_response(
|
||||
pimpl_->client_handle.get(), &response_header, cancel_response.get());
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
|
||||
ret, response_header, cancel_response));
|
||||
} else {
|
||||
throw std::runtime_error("Taking data from action client but nothing is ready");
|
||||
// next_ready_event is an atomic, caching localy
|
||||
size_t next_ready_event = pimpl_->next_ready_event.exchange(ClientBaseImpl::NO_EVENT_READY);
|
||||
|
||||
if (next_ready_event == ClientBaseImpl::NO_EVENT_READY) {
|
||||
throw std::runtime_error("Taking data from action client but no ready event");
|
||||
}
|
||||
|
||||
return take_data_by_entity_id(next_ready_event);
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
ClientBase::take_data_by_entity_id(size_t id)
|
||||
{
|
||||
std::shared_ptr<ClientBaseData> data_ptr;
|
||||
rcl_ret_t ret;
|
||||
|
||||
// Mark as ready the entity from which we want to take data
|
||||
switch (static_cast<EntityType>(id)) {
|
||||
case EntityType::GoalClient:
|
||||
pimpl_->is_goal_response_ready = true;
|
||||
{
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> goal_response;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
|
||||
|
||||
goal_response = this->create_goal_response();
|
||||
ret = rcl_action_take_goal_response(
|
||||
pimpl_->client_handle.get(), &response_header, goal_response.get());
|
||||
}
|
||||
data_ptr = std::make_shared<ClientBaseData>(
|
||||
ClientBaseData::GoalResponseData(
|
||||
ret, response_header, goal_response));
|
||||
}
|
||||
break;
|
||||
case EntityType::ResultClient:
|
||||
pimpl_->is_result_response_ready = true;
|
||||
{
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> result_response;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
|
||||
result_response = this->create_result_response();
|
||||
ret = rcl_action_take_result_response(
|
||||
pimpl_->client_handle.get(), &response_header, result_response.get());
|
||||
}
|
||||
data_ptr =
|
||||
std::make_shared<ClientBaseData>(
|
||||
ClientBaseData::ResultResponseData(
|
||||
ret, response_header, result_response));
|
||||
}
|
||||
break;
|
||||
case EntityType::CancelClient:
|
||||
pimpl_->is_cancel_response_ready = true;
|
||||
{
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> cancel_response;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
|
||||
cancel_response = this->create_cancel_response();
|
||||
ret = rcl_action_take_cancel_response(
|
||||
pimpl_->client_handle.get(), &response_header, cancel_response.get());
|
||||
}
|
||||
data_ptr =
|
||||
std::make_shared<ClientBaseData>(
|
||||
ClientBaseData::CancelResponseData(
|
||||
ret, response_header, cancel_response));
|
||||
}
|
||||
break;
|
||||
case EntityType::FeedbackSubscription:
|
||||
pimpl_->is_feedback_ready = true;
|
||||
{
|
||||
std::shared_ptr<void> feedback_message;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
|
||||
feedback_message = this->create_feedback_message();
|
||||
ret = rcl_action_take_feedback(
|
||||
pimpl_->client_handle.get(), feedback_message.get());
|
||||
}
|
||||
data_ptr =
|
||||
std::make_shared<ClientBaseData>(
|
||||
ClientBaseData::FeedbackReadyData(
|
||||
ret, feedback_message));
|
||||
}
|
||||
break;
|
||||
case EntityType::StatusSubscription:
|
||||
pimpl_->is_status_ready = true;
|
||||
{
|
||||
std::shared_ptr<void> status_message;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_client_mutex_);
|
||||
status_message = this->create_status_message();
|
||||
ret = rcl_action_take_status(
|
||||
pimpl_->client_handle.get(), status_message.get());
|
||||
}
|
||||
data_ptr =
|
||||
std::make_shared<ClientBaseData>(
|
||||
ClientBaseData::StatusReadyData(
|
||||
ret, status_message));
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return take_data();
|
||||
return std::static_pointer_cast<void>(data_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
ClientBase::execute(std::shared_ptr<void> & data)
|
||||
ClientBase::execute(std::shared_ptr<void> & data_in)
|
||||
{
|
||||
if (!data) {
|
||||
throw std::runtime_error("'data' is empty");
|
||||
if (!data_in) {
|
||||
throw std::runtime_error("Executing action client but 'data' is empty");
|
||||
}
|
||||
|
||||
if (pimpl_->is_feedback_ready) {
|
||||
auto shared_ptr = std::static_pointer_cast<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
pimpl_->is_feedback_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
auto feedback_message = std::get<1>(*shared_ptr);
|
||||
this->handle_feedback_message(feedback_message);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking feedback");
|
||||
}
|
||||
} else if (pimpl_->is_status_ready) {
|
||||
auto shared_ptr = std::static_pointer_cast<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
pimpl_->is_status_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
auto status_message = std::get<1>(*shared_ptr);
|
||||
this->handle_status_message(status_message);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking status");
|
||||
}
|
||||
} else if (pimpl_->is_goal_response_ready) {
|
||||
auto shared_ptr = std::static_pointer_cast<
|
||||
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
pimpl_->is_goal_response_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
auto response_header = std::get<1>(*shared_ptr);
|
||||
auto goal_response = std::get<2>(*shared_ptr);
|
||||
this->handle_goal_response(response_header, goal_response);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking goal response");
|
||||
}
|
||||
} else if (pimpl_->is_result_response_ready) {
|
||||
auto shared_ptr = std::static_pointer_cast<
|
||||
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
pimpl_->is_result_response_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
auto response_header = std::get<1>(*shared_ptr);
|
||||
auto result_response = std::get<2>(*shared_ptr);
|
||||
this->handle_result_response(response_header, result_response);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking result response");
|
||||
}
|
||||
} else if (pimpl_->is_cancel_response_ready) {
|
||||
auto shared_ptr = std::static_pointer_cast<
|
||||
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
pimpl_->is_cancel_response_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
auto response_header = std::get<1>(*shared_ptr);
|
||||
auto cancel_response = std::get<2>(*shared_ptr);
|
||||
this->handle_cancel_response(response_header, cancel_response);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking cancel response");
|
||||
}
|
||||
} else {
|
||||
throw std::runtime_error("Executing action client but nothing is ready");
|
||||
}
|
||||
std::shared_ptr<ClientBaseData> data_ptr = std::static_pointer_cast<ClientBaseData>(data_in);
|
||||
|
||||
std::visit(
|
||||
[&](auto && data) -> void {
|
||||
using T = std::decay_t<decltype(data)>;
|
||||
if constexpr (std::is_same_v<T, ClientBaseData::FeedbackReadyData>) {
|
||||
if (RCL_RET_OK == data.ret) {
|
||||
this->handle_feedback_message(data.feedback_message);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking feedback");
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_same_v<T, ClientBaseData::StatusReadyData>) {
|
||||
if (RCL_RET_OK == data.ret) {
|
||||
this->handle_status_message(data.status_message);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking status");
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_same_v<T, ClientBaseData::GoalResponseData>) {
|
||||
if (RCL_RET_OK == data.ret) {
|
||||
this->handle_goal_response(data.response_header, data.goal_response);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking goal response");
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_same_v<T, ClientBaseData::ResultResponseData>) {
|
||||
if (RCL_RET_OK == data.ret) {
|
||||
this->handle_result_response(data.response_header, data.result_response);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking result response");
|
||||
}
|
||||
}
|
||||
if constexpr (std::is_same_v<T, ClientBaseData::CancelResponseData>) {
|
||||
if (RCL_RET_OK == data.ret) {
|
||||
this->handle_cancel_response(data.response_header, data.cancel_response);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != data.ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(data.ret, "error taking cancel response");
|
||||
}
|
||||
}
|
||||
}, data_ptr->data);
|
||||
}
|
||||
|
||||
} // namespace rclcpp_action
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl_action/action_server.h"
|
||||
@@ -33,8 +34,50 @@
|
||||
using rclcpp_action::ServerBase;
|
||||
using rclcpp_action::GoalUUID;
|
||||
|
||||
struct ServerBaseData;
|
||||
|
||||
namespace rclcpp_action
|
||||
{
|
||||
|
||||
struct ServerBaseData
|
||||
{
|
||||
using GoalRequestData = std::tuple<
|
||||
rcl_ret_t,
|
||||
const rcl_action_goal_info_t,
|
||||
rmw_request_id_t,
|
||||
std::shared_ptr<void>
|
||||
>;
|
||||
|
||||
using CancelRequestData = std::tuple<
|
||||
rcl_ret_t,
|
||||
std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
|
||||
rmw_request_id_t
|
||||
>;
|
||||
|
||||
using ResultRequestData = std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>;
|
||||
|
||||
using GoalExpiredData = struct Empty {};
|
||||
|
||||
std::variant<GoalRequestData, CancelRequestData, ResultRequestData, GoalExpiredData> data;
|
||||
|
||||
explicit ServerBaseData(GoalRequestData && data_in)
|
||||
: data(std::move(data_in)) {}
|
||||
explicit ServerBaseData(CancelRequestData && data_in)
|
||||
: data(std::move(data_in)) {}
|
||||
explicit ServerBaseData(ResultRequestData && data_in)
|
||||
: data(std::move(data_in)) {}
|
||||
explicit ServerBaseData(GoalExpiredData && data_in)
|
||||
: data(std::move(data_in)) {}
|
||||
};
|
||||
|
||||
enum class ActionEventType : std::size_t
|
||||
{
|
||||
GoalService,
|
||||
ResultService,
|
||||
CancelService,
|
||||
Expired,
|
||||
};
|
||||
|
||||
class ServerBaseImpl
|
||||
{
|
||||
public:
|
||||
@@ -60,11 +103,6 @@ public:
|
||||
size_t num_services_ = 0;
|
||||
size_t num_guard_conditions_ = 0;
|
||||
|
||||
std::atomic<bool> goal_request_ready_{false};
|
||||
std::atomic<bool> cancel_request_ready_{false};
|
||||
std::atomic<bool> result_request_ready_{false};
|
||||
std::atomic<bool> goal_expired_{false};
|
||||
|
||||
// Lock for unordered_maps
|
||||
std::recursive_mutex unordered_map_mutex_;
|
||||
|
||||
@@ -75,8 +113,15 @@ public:
|
||||
// rcl goal handles are kept so api to send result doesn't try to access freed memory
|
||||
std::unordered_map<GoalUUID, std::shared_ptr<rcl_action_goal_handle_t>> goal_handles_;
|
||||
|
||||
|
||||
// next ready event for taking, will be set by is_ready and will be processed by take_data
|
||||
std::atomic<size_t> next_ready_event;
|
||||
// used to indicate that next_ready_event has no ready event for processing
|
||||
static constexpr size_t NO_EVENT_READY = std::numeric_limits<size_t>::max();
|
||||
|
||||
rclcpp::Logger logger_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp_action
|
||||
|
||||
ServerBase::ServerBase(
|
||||
@@ -195,124 +240,166 @@ ServerBase::is_ready(rcl_wait_set_t * wait_set)
|
||||
&goal_expired);
|
||||
}
|
||||
|
||||
pimpl_->goal_request_ready_ = goal_request_ready;
|
||||
pimpl_->cancel_request_ready_ = cancel_request_ready;
|
||||
pimpl_->result_request_ready_ = result_request_ready;
|
||||
pimpl_->goal_expired_ = goal_expired;
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
|
||||
return pimpl_->goal_request_ready_.load() ||
|
||||
pimpl_->cancel_request_ready_.load() ||
|
||||
pimpl_->result_request_ready_.load() ||
|
||||
pimpl_->goal_expired_.load();
|
||||
pimpl_->next_ready_event = ServerBaseImpl::NO_EVENT_READY;
|
||||
|
||||
if (goal_request_ready) {
|
||||
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::GoalService);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (cancel_request_ready) {
|
||||
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::CancelService);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (result_request_ready) {
|
||||
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::ResultService);
|
||||
return true;
|
||||
}
|
||||
|
||||
if (goal_expired) {
|
||||
pimpl_->next_ready_event = static_cast<uint32_t>(ActionEventType::Expired);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
ServerBase::take_data()
|
||||
{
|
||||
if (pimpl_->goal_request_ready_.load()) {
|
||||
rcl_ret_t ret;
|
||||
rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info();
|
||||
rmw_request_id_t request_header;
|
||||
size_t next_ready_event = pimpl_->next_ready_event.exchange(ServerBaseImpl::NO_EVENT_READY);
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
|
||||
std::shared_ptr<void> message = create_goal_request();
|
||||
ret = rcl_action_take_goal_request(
|
||||
pimpl_->action_server_.get(),
|
||||
&request_header,
|
||||
message.get());
|
||||
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared
|
||||
<std::tuple<rcl_ret_t, rcl_action_goal_info_t, rmw_request_id_t, std::shared_ptr<void>>>(
|
||||
ret,
|
||||
goal_info,
|
||||
request_header, message));
|
||||
} else if (pimpl_->cancel_request_ready_.load()) {
|
||||
rcl_ret_t ret;
|
||||
rmw_request_id_t request_header;
|
||||
|
||||
// Initialize cancel request
|
||||
auto request = std::make_shared<action_msgs::srv::CancelGoal::Request>();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
ret = rcl_action_take_cancel_request(
|
||||
pimpl_->action_server_.get(),
|
||||
&request_header,
|
||||
request.get());
|
||||
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared
|
||||
<std::tuple<rcl_ret_t, std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
|
||||
rmw_request_id_t>>(ret, request, request_header));
|
||||
} else if (pimpl_->result_request_ready_.load()) {
|
||||
rcl_ret_t ret;
|
||||
// Get the result request message
|
||||
rmw_request_id_t request_header;
|
||||
std::shared_ptr<void> result_request = create_result_request();
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
ret = rcl_action_take_result_request(
|
||||
pimpl_->action_server_.get(), &request_header, result_request.get());
|
||||
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>>(
|
||||
ret, result_request, request_header));
|
||||
} else if (pimpl_->goal_expired_.load()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
throw std::runtime_error("Taking data from action server but nothing is ready");
|
||||
if (next_ready_event == ServerBaseImpl::NO_EVENT_READY) {
|
||||
throw std::runtime_error("Taking data from action server but no ready event");
|
||||
}
|
||||
|
||||
return take_data_by_entity_id(next_ready_event);
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
ServerBase::take_data_by_entity_id(size_t id)
|
||||
{
|
||||
static_assert(
|
||||
static_cast<size_t>(EntityType::GoalService) ==
|
||||
static_cast<size_t>(ActionEventType::GoalService));
|
||||
static_assert(
|
||||
static_cast<size_t>(EntityType::ResultService) ==
|
||||
static_cast<size_t>(ActionEventType::ResultService));
|
||||
static_assert(
|
||||
static_cast<size_t>(EntityType::CancelService) ==
|
||||
static_cast<size_t>(ActionEventType::CancelService));
|
||||
|
||||
std::shared_ptr<ServerBaseData> data_ptr;
|
||||
// Mark as ready the entity from which we want to take data
|
||||
switch (static_cast<EntityType>(id)) {
|
||||
case EntityType::GoalService:
|
||||
pimpl_->goal_request_ready_ = true;
|
||||
switch (static_cast<ActionEventType>(id)) {
|
||||
case ActionEventType::GoalService:
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info();
|
||||
rmw_request_id_t request_header;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
|
||||
std::shared_ptr<void> message = create_goal_request();
|
||||
ret = rcl_action_take_goal_request(
|
||||
pimpl_->action_server_.get(),
|
||||
&request_header,
|
||||
message.get());
|
||||
|
||||
data_ptr = std::make_shared<ServerBaseData>(
|
||||
ServerBaseData::GoalRequestData(ret, goal_info, request_header, message));
|
||||
}
|
||||
break;
|
||||
case EntityType::ResultService:
|
||||
pimpl_->result_request_ready_ = true;
|
||||
case ActionEventType::ResultService:
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
// Get the result request message
|
||||
rmw_request_id_t request_header;
|
||||
std::shared_ptr<void> result_request = create_result_request();
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
ret = rcl_action_take_result_request(
|
||||
pimpl_->action_server_.get(), &request_header, result_request.get());
|
||||
|
||||
data_ptr =
|
||||
std::make_shared<ServerBaseData>(
|
||||
ServerBaseData::ResultRequestData(ret, result_request, request_header));
|
||||
}
|
||||
break;
|
||||
case EntityType::CancelService:
|
||||
pimpl_->cancel_request_ready_ = true;
|
||||
case ActionEventType::CancelService:
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rmw_request_id_t request_header;
|
||||
|
||||
// Initialize cancel request
|
||||
auto request = std::make_shared<action_msgs::srv::CancelGoal::Request>();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
ret = rcl_action_take_cancel_request(
|
||||
pimpl_->action_server_.get(),
|
||||
&request_header,
|
||||
request.get());
|
||||
|
||||
data_ptr =
|
||||
std::make_shared<ServerBaseData>(
|
||||
ServerBaseData::CancelRequestData(ret, request, request_header));
|
||||
}
|
||||
break;
|
||||
case ActionEventType::Expired:
|
||||
{
|
||||
data_ptr =
|
||||
std::make_shared<ServerBaseData>(ServerBaseData::GoalExpiredData());
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return take_data();
|
||||
return std::static_pointer_cast<void>(data_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
ServerBase::execute(std::shared_ptr<void> & data)
|
||||
ServerBase::execute(std::shared_ptr<void> & data_in)
|
||||
{
|
||||
if (!data && !pimpl_->goal_expired_.load()) {
|
||||
throw std::runtime_error("'data' is empty");
|
||||
if (!data_in) {
|
||||
throw std::runtime_error("Executing action server but 'data' is empty");
|
||||
}
|
||||
|
||||
if (pimpl_->goal_request_ready_.load()) {
|
||||
execute_goal_request_received(data);
|
||||
} else if (pimpl_->cancel_request_ready_.load()) {
|
||||
execute_cancel_request_received(data);
|
||||
} else if (pimpl_->result_request_ready_.load()) {
|
||||
execute_result_request_received(data);
|
||||
} else if (pimpl_->goal_expired_.load()) {
|
||||
execute_check_expired_goals();
|
||||
} else {
|
||||
throw std::runtime_error("Executing action server but nothing is ready");
|
||||
}
|
||||
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data_in);
|
||||
|
||||
std::visit(
|
||||
[&](auto && data) -> void {
|
||||
using T = std::decay_t<decltype(data)>;
|
||||
if constexpr (std::is_same_v<T, ServerBaseData::GoalRequestData>) {
|
||||
execute_goal_request_received(data_in);
|
||||
}
|
||||
if constexpr (std::is_same_v<T, ServerBaseData::CancelRequestData>) {
|
||||
execute_cancel_request_received(data_in);
|
||||
}
|
||||
if constexpr (std::is_same_v<T, ServerBaseData::ResultRequestData>) {
|
||||
execute_result_request_received(data_in);
|
||||
}
|
||||
if constexpr (std::is_same_v<T, ServerBaseData::GoalExpiredData>) {
|
||||
execute_check_expired_goals();
|
||||
}
|
||||
},
|
||||
data_ptr->data);
|
||||
}
|
||||
|
||||
void
|
||||
ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
|
||||
{
|
||||
auto shared_ptr = std::static_pointer_cast
|
||||
<std::tuple<rcl_ret_t, rcl_action_goal_info_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
|
||||
rcl_ret_t ret = std::get<0>(*shared_ptr);
|
||||
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data);
|
||||
const ServerBaseData::GoalRequestData & gData(
|
||||
std::get<ServerBaseData::GoalRequestData>(data_ptr->data));
|
||||
|
||||
rcl_ret_t ret = std::get<0>(gData);
|
||||
rcl_action_goal_info_t goal_info = std::get<1>(gData);
|
||||
rmw_request_id_t request_header = std::get<2>(gData);
|
||||
const std::shared_ptr<void> message = std::get<3>(gData);
|
||||
|
||||
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
|
||||
// Ignore take failure because connext fails if it receives a sample without valid data.
|
||||
// This happens when a client shuts down and connext receives a sample saying the client is
|
||||
@@ -321,14 +408,6 @@ ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
|
||||
} else if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
rcl_action_goal_info_t goal_info = std::get<1>(*shared_ptr);
|
||||
rmw_request_id_t request_header = std::get<2>(*shared_ptr);
|
||||
std::shared_ptr<void> message = std::get<3>(*shared_ptr);
|
||||
|
||||
bool expected = true;
|
||||
if (!pimpl_->goal_request_ready_.compare_exchange_strong(expected, false)) {
|
||||
return;
|
||||
}
|
||||
|
||||
GoalUUID uuid = get_goal_id_from_goal_request(message.get());
|
||||
convert(uuid, &goal_info);
|
||||
@@ -345,7 +424,16 @@ ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
|
||||
}
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
if (ret == RCL_RET_TIMEOUT) {
|
||||
RCLCPP_WARN(
|
||||
pimpl_->logger_,
|
||||
"Failed to send goal response %s (timeout): %s",
|
||||
to_string(uuid).c_str(), rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
return;
|
||||
} else {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
}
|
||||
|
||||
const auto status = response_pair.first;
|
||||
@@ -403,10 +491,15 @@ ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
|
||||
void
|
||||
ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
|
||||
{
|
||||
auto shared_ptr = std::static_pointer_cast
|
||||
<std::tuple<rcl_ret_t, std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
|
||||
rmw_request_id_t>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data);
|
||||
const ServerBaseData::CancelRequestData & gData(
|
||||
std::get<ServerBaseData::CancelRequestData>(data_ptr->data));
|
||||
|
||||
rcl_ret_t ret = std::get<0>(gData);
|
||||
std::shared_ptr<action_msgs::srv::CancelGoal::Request> request = std::get<1>(gData);
|
||||
rmw_request_id_t request_header = std::get<2>(gData);
|
||||
|
||||
|
||||
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
|
||||
// Ignore take failure because connext fails if it receives a sample without valid data.
|
||||
// This happens when a client shuts down and connext receives a sample saying the client is
|
||||
@@ -415,9 +508,6 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
|
||||
} else if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
auto request = std::get<1>(*shared_ptr);
|
||||
auto request_header = std::get<2>(*shared_ptr);
|
||||
pimpl_->cancel_request_ready_ = false;
|
||||
|
||||
// Convert c++ message to C message
|
||||
rcl_action_cancel_request_t cancel_request = rcl_action_get_zero_initialized_cancel_request();
|
||||
@@ -484,6 +574,15 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
|
||||
pimpl_->action_server_.get(), &request_header, response.get());
|
||||
}
|
||||
|
||||
if (ret == RCL_RET_TIMEOUT) {
|
||||
GoalUUID uuid = request->goal_info.goal_id.uuid;
|
||||
RCLCPP_WARN(
|
||||
pimpl_->logger_,
|
||||
"Failed to send cancel response %s (timeout): %s",
|
||||
to_string(uuid).c_str(), rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
return;
|
||||
}
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
@@ -493,9 +592,14 @@ ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
|
||||
void
|
||||
ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
|
||||
{
|
||||
auto shared_ptr = std::static_pointer_cast
|
||||
<std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
std::shared_ptr<ServerBaseData> data_ptr = std::static_pointer_cast<ServerBaseData>(data);
|
||||
const ServerBaseData::ResultRequestData & gData(
|
||||
std::get<ServerBaseData::ResultRequestData>(data_ptr->data));
|
||||
|
||||
rcl_ret_t ret = std::get<0>(gData);
|
||||
std::shared_ptr<void> result_request = std::get<1>(gData);
|
||||
rmw_request_id_t request_header = std::get<2>(gData);
|
||||
|
||||
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
|
||||
// Ignore take failure because connext fails if it receives a sample without valid data.
|
||||
// This happens when a client shuts down and connext receives a sample saying the client is
|
||||
@@ -504,10 +608,7 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
|
||||
} else if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
auto result_request = std::get<1>(*shared_ptr);
|
||||
auto request_header = std::get<2>(*shared_ptr);
|
||||
|
||||
pimpl_->result_request_ready_ = false;
|
||||
std::shared_ptr<void> result_response;
|
||||
|
||||
// check if the goal exists
|
||||
@@ -539,6 +640,14 @@ ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
rcl_ret_t rcl_ret = rcl_action_send_result_response(
|
||||
pimpl_->action_server_.get(), &request_header, result_response.get());
|
||||
if (rcl_ret == RCL_RET_TIMEOUT) {
|
||||
RCLCPP_WARN(
|
||||
pimpl_->logger_,
|
||||
"Failed to send result response %s (timeout): %s",
|
||||
to_string(uuid).c_str(), rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
return;
|
||||
}
|
||||
if (RCL_RET_OK != rcl_ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(rcl_ret);
|
||||
}
|
||||
@@ -672,7 +781,13 @@ ServerBase::publish_result(const GoalUUID & uuid, std::shared_ptr<void> result_m
|
||||
for (auto & request_header : iter->second) {
|
||||
rcl_ret_t ret = rcl_action_send_result_response(
|
||||
pimpl_->action_server_.get(), &request_header, result_msg.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
if (ret == RCL_RET_TIMEOUT) {
|
||||
RCLCPP_WARN(
|
||||
pimpl_->logger_,
|
||||
"Failed to send result response %s (timeout): %s",
|
||||
to_string(uuid).c_str(), rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
} else if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,6 +33,9 @@ to_string(const GoalUUID & goal_id)
|
||||
void
|
||||
convert(const GoalUUID & goal_id, rcl_action_goal_info_t * info)
|
||||
{
|
||||
if (info == nullptr) {
|
||||
throw std::invalid_argument("info is nullptr");
|
||||
}
|
||||
for (size_t i = 0; i < 16; ++i) {
|
||||
info->goal_id.uuid[i] = goal_id[i];
|
||||
}
|
||||
@@ -41,6 +44,9 @@ convert(const GoalUUID & goal_id, rcl_action_goal_info_t * info)
|
||||
void
|
||||
convert(const rcl_action_goal_info_t & info, GoalUUID * goal_id)
|
||||
{
|
||||
if (goal_id == nullptr) {
|
||||
throw std::invalid_argument("goal_id is nullptr");
|
||||
}
|
||||
for (size_t i = 0; i < 16; ++i) {
|
||||
(*goal_id)[i] = info.goal_id.uuid[i];
|
||||
}
|
||||
|
||||
@@ -12,13 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <thread>
|
||||
#include <chrono>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
|
||||
@@ -40,6 +40,7 @@ TEST(TestActionTypes, goal_uuid_to_rcl_action_goal_info) {
|
||||
for (uint8_t i = 0; i < UUID_SIZE; ++i) {
|
||||
goal_id[i] = i;
|
||||
}
|
||||
ASSERT_THROW(rclcpp_action::convert(goal_id, nullptr), std::invalid_argument);
|
||||
rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info();
|
||||
rclcpp_action::convert(goal_id, &goal_info);
|
||||
for (uint8_t i = 0; i < UUID_SIZE; ++i) {
|
||||
@@ -53,6 +54,7 @@ TEST(TestActionTypes, rcl_action_goal_info_to_goal_uuid) {
|
||||
goal_info.goal_id.uuid[i] = i;
|
||||
}
|
||||
|
||||
ASSERT_THROW(rclcpp_action::convert(goal_info, nullptr), std::invalid_argument);
|
||||
rclcpp_action::GoalUUID goal_id;
|
||||
rclcpp_action::convert(goal_id, &goal_info);
|
||||
for (uint8_t i = 0; i < UUID_SIZE; ++i) {
|
||||
|
||||
@@ -2,6 +2,66 @@
|
||||
Changelog for package rclcpp_components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
16.0.18 (2026-02-09)
|
||||
--------------------
|
||||
|
||||
16.0.17 (2025-12-23)
|
||||
--------------------
|
||||
|
||||
16.0.16 (2025-11-18)
|
||||
--------------------
|
||||
* Fix REP url locations (backport `#2987 <https://github.com/ros2/rclcpp/issues/2987>`_) (`#2991 <https://github.com/ros2/rclcpp/issues/2991>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.15 (2025-09-11)
|
||||
--------------------
|
||||
|
||||
16.0.14 (2025-07-16)
|
||||
--------------------
|
||||
|
||||
16.0.13 (2025-06-23)
|
||||
--------------------
|
||||
* Added missing chrono includes (backport `#2854 <https://github.com/ros2/rclcpp/issues/2854>`_) (`#2857 <https://github.com/ros2/rclcpp/issues/2857>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.12 (2025-03-25)
|
||||
--------------------
|
||||
* Redundant .c_str() usage in rclcpp_components triggers ament_clang_tidy warning (`#2718 <https://github.com/ros2/rclcpp/issues/2718>`_)
|
||||
* Contributors: LihanChen2004
|
||||
|
||||
16.0.11 (2024-11-25)
|
||||
--------------------
|
||||
|
||||
16.0.10 (2024-07-26)
|
||||
--------------------
|
||||
|
||||
16.0.9 (2024-05-15)
|
||||
-------------------
|
||||
|
||||
16.0.8 (2024-01-24)
|
||||
-------------------
|
||||
* Add missing header required by the rclcpp::NodeOptions type (`#2324 <https://github.com/ros2/rclcpp/issues/2324>`_) (`#2407 <https://github.com/ros2/rclcpp/issues/2407>`_)
|
||||
* fix(rclcpp_components): increase the service queue sizes in component_container (backport `#2363 <https://github.com/ros2/rclcpp/issues/2363>`_) (`#2380 <https://github.com/ros2/rclcpp/issues/2380>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
16.0.7 (2023-11-13)
|
||||
-------------------
|
||||
|
||||
16.0.6 (2023-09-19)
|
||||
-------------------
|
||||
|
||||
16.0.5 (2023-07-17)
|
||||
-------------------
|
||||
|
||||
16.0.4 (2023-04-25)
|
||||
-------------------
|
||||
|
||||
16.0.3 (2023-01-10)
|
||||
-------------------
|
||||
|
||||
16.0.2 (2022-11-07)
|
||||
-------------------
|
||||
|
||||
16.0.1 (2022-04-13)
|
||||
-------------------
|
||||
|
||||
|
||||
@@ -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 Quality Categories in REP-2004](https://www.ros.org/reps/rep-2004.html) of the ROS2 developer guide.
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://reps.openrobotics.org/rep-2004/) of the ROS2 developer guide.
|
||||
|
||||
## 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/).
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
|
||||
#define RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
|
||||
|
||||
#include "rclcpp/node_options.hpp"
|
||||
#include "rclcpp_components/node_instance_wrapper.hpp"
|
||||
|
||||
namespace rclcpp_components
|
||||
|
||||
@@ -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>16.0.1</version>
|
||||
<version>16.0.18</version>
|
||||
<description>Package containing tools for dynamically loadable components</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="jacob@openrobotics.org">Jacob Perron</maintainer>
|
||||
|
||||
@@ -37,12 +37,16 @@ ComponentManager::ComponentManager(
|
||||
: Node(std::move(node_name), node_options),
|
||||
executor_(executor)
|
||||
{
|
||||
rmw_qos_profile_t service_qos = rmw_qos_profile_services_default;
|
||||
service_qos.depth = 200;
|
||||
loadNode_srv_ = create_service<LoadNode>(
|
||||
"~/_container/load_node",
|
||||
std::bind(&ComponentManager::on_load_node, this, _1, _2, _3));
|
||||
std::bind(&ComponentManager::on_load_node, this, _1, _2, _3),
|
||||
service_qos);
|
||||
unloadNode_srv_ = create_service<UnloadNode>(
|
||||
"~/_container/unload_node",
|
||||
std::bind(&ComponentManager::on_unload_node, this, _1, _2, _3));
|
||||
std::bind(&ComponentManager::on_unload_node, this, _1, _2, _3),
|
||||
service_qos);
|
||||
listNodes_srv_ = create_service<ListNodes>(
|
||||
"~/_container/list_nodes",
|
||||
std::bind(&ComponentManager::on_list_nodes, this, _1, _2, _3));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user