Compare commits
62 Commits
mjcarroll/
...
23.0.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea31f94eb4 | ||
|
|
f496291e81 | ||
|
|
dd6fad6d42 | ||
|
|
38734d769a | ||
|
|
e103b8d37e | ||
|
|
253d395d4e | ||
|
|
d5e5141b3d | ||
|
|
a0148dfd5d | ||
|
|
5e152d77d8 | ||
|
|
fa732b9ee8 | ||
|
|
bc435776a2 | ||
|
|
43cf0be15c | ||
|
|
fd58bddb05 | ||
|
|
e7f06398db | ||
|
|
ba175922d3 | ||
|
|
77db1ed25b | ||
|
|
403f305b15 | ||
|
|
fd229d72ff | ||
|
|
89f0afe9bc | ||
|
|
a4db4c57a6 | ||
|
|
fbe8f28cd1 | ||
|
|
65f0b70d4a | ||
|
|
9b4b3da3d4 | ||
|
|
cd0440f1a5 | ||
|
|
a17d26b20a | ||
|
|
e2965831d5 | ||
|
|
ea29c142af | ||
|
|
5d6e5fa766 | ||
|
|
22a954e1b0 | ||
|
|
c0d72c3ee0 | ||
|
|
6e97990a32 | ||
|
|
4ebc5f61d8 | ||
|
|
a7a9b78fee | ||
|
|
945d254e32 | ||
|
|
deebbc3ad6 | ||
|
|
588dba7a70 | ||
|
|
2e355e4849 | ||
|
|
fe2e0e4c64 | ||
|
|
005f6aefe9 | ||
|
|
3a64349aec | ||
|
|
3530b0959c | ||
|
|
4d12bcbca0 | ||
|
|
1fff79089a | ||
|
|
b3518d12ca | ||
|
|
4efc05266b | ||
|
|
dab9c8acdc | ||
|
|
867ad62da2 | ||
|
|
f8072f2fa2 | ||
|
|
fce021b149 | ||
|
|
c4f57a7998 | ||
|
|
d7fdb6184c | ||
|
|
58bcd3b822 | ||
|
|
26426adda9 | ||
|
|
6e1fea14e1 | ||
|
|
86c77143c9 | ||
|
|
b812790ee3 | ||
|
|
6ca1023ef7 | ||
|
|
77ede02251 | ||
|
|
a431256383 | ||
|
|
9d2849cb0a | ||
|
|
3610b68348 | ||
|
|
9c03a463c1 |
@@ -2,6 +2,83 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
23.0.0 (2023-09-08)
|
||||
-------------------
|
||||
* Fix the return type of Rate::period. (`#2301 <https://github.com/ros2/rclcpp/issues/2301>`_)
|
||||
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
|
||||
* Cleanup flaky timers_manager tests. (`#2299 <https://github.com/ros2/rclcpp/issues/2299>`_)
|
||||
* Contributors: Chris Lalancette, Christophe Bedard
|
||||
|
||||
22.2.0 (2023-09-07)
|
||||
-------------------
|
||||
* Topic correct typeadapter deduction (`#2294 <https://github.com/ros2/rclcpp/issues/2294>`_)
|
||||
* Fix C++20 allocator construct deprecation (`#2292 <https://github.com/ros2/rclcpp/issues/2292>`_)
|
||||
* Make Rate to select the clock to work with (`#2123 <https://github.com/ros2/rclcpp/issues/2123>`_)
|
||||
* Correct the position of a comment. (`#2290 <https://github.com/ros2/rclcpp/issues/2290>`_)
|
||||
* Remove unnecessary lambda captures in the tests. (`#2289 <https://github.com/ros2/rclcpp/issues/2289>`_)
|
||||
* Add rcl_logging_interface as an explicit dependency. (`#2284 <https://github.com/ros2/rclcpp/issues/2284>`_)
|
||||
* Revamp list_parameters to be more efficient and easier to read. (`#2282 <https://github.com/ros2/rclcpp/issues/2282>`_)
|
||||
* Contributors: AiVerisimilitude, Alexey Merzlyakov, Chen Lihui, Chris Lalancette, Jiaqi Li
|
||||
|
||||
22.1.0 (2023-08-21)
|
||||
-------------------
|
||||
* Do not crash Executor when send_response fails due to client failure. (`#2276 <https://github.com/ros2/rclcpp/issues/2276>`_)
|
||||
* Adding Custom Unknown Type Error (`#2272 <https://github.com/ros2/rclcpp/issues/2272>`_)
|
||||
* Add a pimpl inside rclcpp::Node for future distro backports (`#2228 <https://github.com/ros2/rclcpp/issues/2228>`_)
|
||||
* Remove an unused variable from the events executor tests. (`#2270 <https://github.com/ros2/rclcpp/issues/2270>`_)
|
||||
* Add spin_all shortcut (`#2246 <https://github.com/ros2/rclcpp/issues/2246>`_)
|
||||
* Adding Missing Group Exceptions (`#2256 <https://github.com/ros2/rclcpp/issues/2256>`_)
|
||||
* Change associated clocks storage to unordered_set (`#2257 <https://github.com/ros2/rclcpp/issues/2257>`_)
|
||||
* associated clocks should be protected by mutex. (`#2255 <https://github.com/ros2/rclcpp/issues/2255>`_)
|
||||
* Instrument loaned message publication code path (`#2240 <https://github.com/ros2/rclcpp/issues/2240>`_)
|
||||
* Contributors: Chris Lalancette, Christophe Bedard, Emerson Knapp, Luca Della Vedova, Lucas Wendland, Tomoya Fujita, Tony Najjar
|
||||
|
||||
22.0.0 (2023-07-11)
|
||||
-------------------
|
||||
* Implement get_node_type_descriptions_interface for lifecyclenode and add smoke test for it (`#2237 <https://github.com/ros2/rclcpp/issues/2237>`_)
|
||||
* Add new node interface TypeDescriptionsInterface to provide GetTypeDescription service (`#2224 <https://github.com/ros2/rclcpp/issues/2224>`_)
|
||||
* Move always_false_v to detail namespace (`#2232 <https://github.com/ros2/rclcpp/issues/2232>`_)
|
||||
* Revamp the test_subscription.cpp tests. (`#2227 <https://github.com/ros2/rclcpp/issues/2227>`_)
|
||||
* warning: comparison of integer expressions of different signedness (`#2219 <https://github.com/ros2/rclcpp/issues/2219>`_)
|
||||
* Modifies timers API to select autostart state (`#2005 <https://github.com/ros2/rclcpp/issues/2005>`_)
|
||||
* Enable callback group tests for connextdds (`#2182 <https://github.com/ros2/rclcpp/issues/2182>`_)
|
||||
* Contributors: Chris Lalancette, Christopher Wecht, Eloy Briceno, Emerson Knapp, Nathan Wiebe Neufeldt, Tomoya Fujita
|
||||
|
||||
21.3.0 (2023-06-12)
|
||||
-------------------
|
||||
* Fix up misspellings of "receive". (`#2208 <https://github.com/ros2/rclcpp/issues/2208>`_)
|
||||
* Remove flaky stressAddRemoveNode test (`#2206 <https://github.com/ros2/rclcpp/issues/2206>`_)
|
||||
* Use TRACETOOLS\_ prefix for tracepoint-related macros (`#2162 <https://github.com/ros2/rclcpp/issues/2162>`_)
|
||||
* Contributors: Chris Lalancette, Christophe Bedard, Michael Carroll
|
||||
|
||||
21.2.0 (2023-06-07)
|
||||
-------------------
|
||||
* remove nolint since ament_cpplint updated for the c++17 header (`#2198 <https://github.com/ros2/rclcpp/issues/2198>`_)
|
||||
* Feature/available capacity of ipm (`#2173 <https://github.com/ros2/rclcpp/issues/2173>`_)
|
||||
* add mutex to protect events_executor current entity collection (`#2187 <https://github.com/ros2/rclcpp/issues/2187>`_)
|
||||
* Declare rclcpp callbacks before the rcl entities (`#2024 <https://github.com/ros2/rclcpp/issues/2024>`_)
|
||||
* Contributors: Alberto Soragna, Chen Lihui, DensoADAS, mauropasse
|
||||
|
||||
21.1.1 (2023-05-11)
|
||||
-------------------
|
||||
* Fix race condition in events-executor (`#2177 <https://github.com/ros2/rclcpp/issues/2177>`_)
|
||||
* Add missing stdexcept include (`#2186 <https://github.com/ros2/rclcpp/issues/2186>`_)
|
||||
* Fix a format-security warning when building with clang (`#2171 <https://github.com/ros2/rclcpp/issues/2171>`_)
|
||||
* Fix delivered message kind (`#2175 <https://github.com/ros2/rclcpp/issues/2175>`_)
|
||||
* Contributors: Alberto Soragna, Chris Lalancette, methylDragon, Øystein Sture
|
||||
|
||||
21.1.0 (2023-04-27)
|
||||
-------------------
|
||||
|
||||
21.0.0 (2023-04-18)
|
||||
-------------------
|
||||
* Add support for logging service. (`#2122 <https://github.com/ros2/rclcpp/issues/2122>`_)
|
||||
* Picking ABI-incompatible executor changes (`#2170 <https://github.com/ros2/rclcpp/issues/2170>`_)
|
||||
* add events-executor and timers-manager in rclcpp (`#2155 <https://github.com/ros2/rclcpp/issues/2155>`_)
|
||||
* Create common structures for executors to use (`#2143 <https://github.com/ros2/rclcpp/issues/2143>`_)
|
||||
* Implement deliver message kind (`#2168 <https://github.com/ros2/rclcpp/issues/2168>`_)
|
||||
* Contributors: Alberto Soragna, Lei Liu, Michael Carroll, methylDragon
|
||||
|
||||
20.0.0 (2023-04-13)
|
||||
-------------------
|
||||
* applied tracepoints for ring_buffer (`#2091 <https://github.com/ros2/rclcpp/issues/2091>`_)
|
||||
|
||||
@@ -10,6 +10,7 @@ find_package(builtin_interfaces REQUIRED)
|
||||
find_package(libstatistics_collector REQUIRED)
|
||||
find_package(rcl REQUIRED)
|
||||
find_package(rcl_interfaces REQUIRED)
|
||||
find_package(rcl_logging_interface REQUIRED)
|
||||
find_package(rcl_yaml_param_parser REQUIRED)
|
||||
find_package(rcpputils REQUIRED)
|
||||
find_package(rcutils REQUIRED)
|
||||
@@ -92,6 +93,7 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/node_interfaces/node_time_source.cpp
|
||||
src/rclcpp/node_interfaces/node_timers.cpp
|
||||
src/rclcpp/node_interfaces/node_topics.cpp
|
||||
src/rclcpp/node_interfaces/node_type_descriptions.cpp
|
||||
src/rclcpp/node_interfaces/node_waitables.cpp
|
||||
src/rclcpp/node_options.cpp
|
||||
src/rclcpp/parameter.cpp
|
||||
@@ -105,6 +107,7 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/qos.cpp
|
||||
src/rclcpp/event_handler.cpp
|
||||
src/rclcpp/qos_overriding_options.cpp
|
||||
src/rclcpp/rate.cpp
|
||||
src/rclcpp/serialization.cpp
|
||||
src/rclcpp/serialized_message.cpp
|
||||
src/rclcpp/service.cpp
|
||||
@@ -206,6 +209,7 @@ ament_target_dependencies(${PROJECT_NAME}
|
||||
"libstatistics_collector"
|
||||
"rcl"
|
||||
"rcl_interfaces"
|
||||
"rcl_logging_interface"
|
||||
"rcl_yaml_param_parser"
|
||||
"rcpputils"
|
||||
"rcutils"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
The ROS client library in C++.
|
||||
|
||||
Visit the [rclcpp API documentation](http://docs.ros2.org/latest/api/rclcpp/) for a complete list of its main components and features.
|
||||
The link to the latest rclcpp API documentation, which includes a complete list of its main components and features, can be found on the rclcpp package info page, at the [ROS Index](https://index.ros.org/p/rclcpp/).
|
||||
|
||||
## Quality Declaration
|
||||
|
||||
|
||||
@@ -156,7 +156,7 @@ public:
|
||||
const std::shared_ptr<rmw_request_id_t> & request_header,
|
||||
std::shared_ptr<typename ServiceT::Request> request)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
if (std::holds_alternative<std::monostate>(callback_)) {
|
||||
// TODO(ivanpauno): Remove the set method, and force the users of this class
|
||||
// to pass a callback at construnciton.
|
||||
@@ -182,7 +182,7 @@ public:
|
||||
const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
|
||||
cb(request_header, std::move(request), response);
|
||||
}
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
return response;
|
||||
}
|
||||
|
||||
@@ -191,9 +191,9 @@ public:
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
std::visit(
|
||||
[this](auto && arg) {
|
||||
if (TRACEPOINT_ENABLED(rclcpp_callback_register)) {
|
||||
if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
|
||||
char * symbol = tracetools::get_symbol(arg);
|
||||
DO_TRACEPOINT(
|
||||
TRACETOOLS_DO_TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
symbol);
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant> // NOLINT[build/include_order]
|
||||
#include <variant>
|
||||
|
||||
#include "rosidl_runtime_cpp/traits.hpp"
|
||||
#include "tracetools/tracetools.h"
|
||||
@@ -34,15 +34,15 @@
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
|
||||
|
||||
template<class>
|
||||
inline constexpr bool always_false_v = false;
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<class>
|
||||
inline constexpr bool always_false_v = false;
|
||||
|
||||
template<typename MessageT, typename AllocatorT>
|
||||
struct MessageDeleterHelper
|
||||
{
|
||||
@@ -492,7 +492,7 @@ public:
|
||||
std::shared_ptr<ROSMessageType> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
// Check if the variant is "unset", throw if it is.
|
||||
if (callback_variant_.index() == 0) {
|
||||
if (std::get<0>(callback_variant_) == nullptr) {
|
||||
@@ -580,10 +580,10 @@ public:
|
||||
}
|
||||
// condition to catch unhandled callback types
|
||||
else { // NOLINT[readability/braces]
|
||||
static_assert(always_false_v<T>, "unhandled callback type");
|
||||
static_assert(detail::always_false_v<T>, "unhandled callback type");
|
||||
}
|
||||
}, callback_variant_);
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
}
|
||||
|
||||
// Dispatch when input is a serialized message and the output could be anything.
|
||||
@@ -592,7 +592,7 @@ public:
|
||||
std::shared_ptr<rclcpp::SerializedMessage> serialized_message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
// Check if the variant is "unset", throw if it is.
|
||||
if (callback_variant_.index() == 0) {
|
||||
if (std::get<0>(callback_variant_) == nullptr) {
|
||||
@@ -660,10 +660,10 @@ public:
|
||||
}
|
||||
// condition to catch unhandled callback types
|
||||
else { // NOLINT[readability/braces]
|
||||
static_assert(always_false_v<T>, "unhandled callback type");
|
||||
static_assert(detail::always_false_v<T>, "unhandled callback type");
|
||||
}
|
||||
}, callback_variant_);
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -671,7 +671,7 @@ public:
|
||||
std::shared_ptr<const SubscribedType> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
|
||||
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), true);
|
||||
// Check if the variant is "unset", throw if it is.
|
||||
if (callback_variant_.index() == 0) {
|
||||
if (std::get<0>(callback_variant_) == nullptr) {
|
||||
@@ -790,10 +790,10 @@ public:
|
||||
}
|
||||
// condition to catch unhandled callback types
|
||||
else { // NOLINT[readability/braces]
|
||||
static_assert(always_false_v<T>, "unhandled callback type");
|
||||
static_assert(detail::always_false_v<T>, "unhandled callback type");
|
||||
}
|
||||
}, callback_variant_);
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
}
|
||||
|
||||
void
|
||||
@@ -801,7 +801,7 @@ public:
|
||||
std::unique_ptr<SubscribedType, SubscribedTypeDeleter> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
|
||||
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), true);
|
||||
// Check if the variant is "unset", throw if it is.
|
||||
if (callback_variant_.index() == 0) {
|
||||
if (std::get<0>(callback_variant_) == nullptr) {
|
||||
@@ -924,10 +924,10 @@ public:
|
||||
}
|
||||
// condition to catch unhandled callback types
|
||||
else { // NOLINT[readability/braces]
|
||||
static_assert(always_false_v<T>, "unhandled callback type");
|
||||
static_assert(detail::always_false_v<T>, "unhandled callback type");
|
||||
}
|
||||
}, callback_variant_);
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
}
|
||||
|
||||
constexpr
|
||||
@@ -965,9 +965,9 @@ public:
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
std::visit(
|
||||
[this](auto && callback) {
|
||||
if (TRACEPOINT_ENABLED(rclcpp_callback_register)) {
|
||||
if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
|
||||
char * symbol = tracetools::get_symbol(callback);
|
||||
DO_TRACEPOINT(
|
||||
TRACETOOLS_DO_TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
symbol);
|
||||
|
||||
@@ -180,6 +180,13 @@ public:
|
||||
return _find_ptrs_if_impl<rclcpp::Waitable, Function>(func, waitable_ptrs_);
|
||||
}
|
||||
|
||||
/// Get the total number of entities in this callback group.
|
||||
/**
|
||||
* \return the number of entities in the callback group.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
size_t size() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::atomic_bool &
|
||||
can_be_taken_from();
|
||||
|
||||
@@ -20,13 +20,13 @@
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <optional> // NOLINT, cpplint doesn't think this is a cpp std header
|
||||
#include <optional>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <variant> // NOLINT
|
||||
#include <variant>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/client.h"
|
||||
@@ -363,12 +363,16 @@ protected:
|
||||
std::shared_ptr<rclcpp::Context> context_;
|
||||
rclcpp::Logger node_logger_;
|
||||
|
||||
std::recursive_mutex callback_mutex_;
|
||||
// It is important to declare on_new_response_callback_ before
|
||||
// client_handle_, so on destruction the client is
|
||||
// destroyed first. Otherwise, the rmw client callback
|
||||
// would point briefly to a destroyed function.
|
||||
std::function<void(size_t)> on_new_response_callback_{nullptr};
|
||||
// Declare client_handle_ after callback
|
||||
std::shared_ptr<rcl_client_t> client_handle_;
|
||||
|
||||
std::atomic<bool> in_use_by_wait_set_{false};
|
||||
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::function<void(size_t)> on_new_response_callback_{nullptr};
|
||||
};
|
||||
|
||||
template<typename ServiceT>
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "rcl/context.h"
|
||||
#include "rcl/guard_condition.h"
|
||||
|
||||
@@ -90,7 +90,8 @@ create_timer(
|
||||
rclcpp::Clock::SharedPtr clock,
|
||||
rclcpp::Duration period,
|
||||
CallbackT && callback,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr)
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr,
|
||||
bool autostart = true)
|
||||
{
|
||||
return create_timer(
|
||||
clock,
|
||||
@@ -98,7 +99,8 @@ create_timer(
|
||||
std::forward<CallbackT>(callback),
|
||||
group,
|
||||
node_base.get(),
|
||||
node_timers.get());
|
||||
node_timers.get(),
|
||||
autostart);
|
||||
}
|
||||
|
||||
/// Create a timer with a given clock
|
||||
@@ -109,7 +111,8 @@ create_timer(
|
||||
rclcpp::Clock::SharedPtr clock,
|
||||
rclcpp::Duration period,
|
||||
CallbackT && callback,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr)
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr,
|
||||
bool autostart = true)
|
||||
{
|
||||
return create_timer(
|
||||
clock,
|
||||
@@ -117,7 +120,8 @@ create_timer(
|
||||
std::forward<CallbackT>(callback),
|
||||
group,
|
||||
rclcpp::node_interfaces::get_node_base_interface(node).get(),
|
||||
rclcpp::node_interfaces::get_node_timers_interface(node).get());
|
||||
rclcpp::node_interfaces::get_node_timers_interface(node).get(),
|
||||
autostart);
|
||||
}
|
||||
|
||||
/// Convenience method to create a general timer with node resources.
|
||||
@@ -132,6 +136,7 @@ create_timer(
|
||||
* \param group callback group
|
||||
* \param node_base node base interface
|
||||
* \param node_timers node timer interface
|
||||
* \param autostart defines if the timer should start it's countdown on initialization or not.
|
||||
* \return shared pointer to a generic timer
|
||||
* \throws std::invalid_argument if either clock, node_base or node_timers
|
||||
* are nullptr, or period is negative or too large
|
||||
@@ -144,7 +149,8 @@ create_timer(
|
||||
CallbackT callback,
|
||||
rclcpp::CallbackGroup::SharedPtr group,
|
||||
node_interfaces::NodeBaseInterface * node_base,
|
||||
node_interfaces::NodeTimersInterface * node_timers)
|
||||
node_interfaces::NodeTimersInterface * node_timers,
|
||||
bool autostart = true)
|
||||
{
|
||||
if (clock == nullptr) {
|
||||
throw std::invalid_argument{"clock cannot be null"};
|
||||
@@ -160,7 +166,7 @@ create_timer(
|
||||
|
||||
// Add a new generic timer.
|
||||
auto timer = rclcpp::GenericTimer<CallbackT>::make_shared(
|
||||
std::move(clock), period_ns, std::move(callback), node_base->get_context());
|
||||
std::move(clock), period_ns, std::move(callback), node_base->get_context(), autostart);
|
||||
node_timers->add_timer(timer, group);
|
||||
return timer;
|
||||
}
|
||||
@@ -187,7 +193,8 @@ create_wall_timer(
|
||||
CallbackT callback,
|
||||
rclcpp::CallbackGroup::SharedPtr group,
|
||||
node_interfaces::NodeBaseInterface * node_base,
|
||||
node_interfaces::NodeTimersInterface * node_timers)
|
||||
node_interfaces::NodeTimersInterface * node_timers,
|
||||
bool autostart = true)
|
||||
{
|
||||
if (node_base == nullptr) {
|
||||
throw std::invalid_argument{"input node_base cannot be null"};
|
||||
@@ -201,7 +208,7 @@ create_wall_timer(
|
||||
|
||||
// Add a new wall timer.
|
||||
auto timer = rclcpp::WallTimer<CallbackT>::make_shared(
|
||||
period_ns, std::move(callback), node_base->get_context());
|
||||
period_ns, std::move(callback), node_base->get_context(), autostart);
|
||||
node_timers->add_timer(timer, group);
|
||||
return timer;
|
||||
}
|
||||
|
||||
@@ -206,6 +206,14 @@ public:
|
||||
const std::vector<std::string> unknown_ros_args;
|
||||
};
|
||||
|
||||
/// Thrown when an unknown type is passed
|
||||
class UnknownTypeError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit UnknownTypeError(const std::string & type)
|
||||
: std::runtime_error("Unknown type: " + type) {}
|
||||
};
|
||||
|
||||
/// Thrown when an invalid rclcpp::Event object or SharedPtr is encountered.
|
||||
class InvalidEventError : public std::runtime_error
|
||||
{
|
||||
@@ -222,6 +230,14 @@ public:
|
||||
: std::runtime_error("event already registered") {}
|
||||
};
|
||||
|
||||
/// Thrown when a callback group is missing from the node, when it wants to utilize the group.
|
||||
class MissingGroupNodeException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
explicit MissingGroupNodeException(const std::string & obj_type)
|
||||
: std::runtime_error("cannot create: " + obj_type + " , callback group not in node") {}
|
||||
};
|
||||
|
||||
/// Thrown if passed parameters are inconsistent or invalid
|
||||
class InvalidParametersException : public std::runtime_error
|
||||
{
|
||||
|
||||
@@ -51,6 +51,7 @@ typedef std::map<rclcpp::CallbackGroup::WeakPtr,
|
||||
|
||||
// Forward declaration is used in convenience method signature.
|
||||
class Node;
|
||||
class ExecutorImplementation;
|
||||
|
||||
/// Coordinate the order and timing of available communication tasks.
|
||||
/**
|
||||
@@ -296,6 +297,21 @@ public:
|
||||
virtual void
|
||||
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0));
|
||||
|
||||
/// Add a node, complete all immediately available work exhaustively, and remove the node.
|
||||
/**
|
||||
* \param[in] node Shared pointer to the node to add.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_node_all(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
|
||||
std::chrono::nanoseconds max_duration);
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_node_all(std::shared_ptr<rclcpp::Node> node, std::chrono::nanoseconds max_duration);
|
||||
|
||||
/// Collect and execute work repeatedly within a duration or until no more work is available.
|
||||
/**
|
||||
* This function can be overridden. The default implementation is suitable for a
|
||||
@@ -697,6 +713,9 @@ protected:
|
||||
|
||||
/// shutdown callback handle registered to Context
|
||||
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
|
||||
|
||||
/// Pointer to implementation
|
||||
std::unique_ptr<ExecutorImplementation> impl_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -29,6 +29,18 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Create a default single-threaded executor and execute all available work exhaustively.
|
||||
/** \param[in] node_ptr Shared pointer to the node to spin. */
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_all(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
std::chrono::nanoseconds max_duration);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_all(rclcpp::Node::SharedPtr node_ptr, std::chrono::nanoseconds max_duration);
|
||||
|
||||
/// Create a default single-threaded executor and execute any immediately available work.
|
||||
/** \param[in] node_ptr Shared pointer to the node to spin. */
|
||||
RCLCPP_PUBLIC
|
||||
|
||||
@@ -198,14 +198,15 @@ build_entities_collection(
|
||||
*
|
||||
* \param[in] collection Collection of entities corresponding to the current wait set.
|
||||
* \param[in] wait_result Result of rclcpp::WaitSet::wait corresponding to the collection.
|
||||
* \return A queue of executables that have been marked ready by the waitset.
|
||||
* \param[inout] queue of executables to append new ready executables to
|
||||
* \return number of new ready executables
|
||||
*/
|
||||
std::deque<rclcpp::AnyExecutable>
|
||||
size_t
|
||||
ready_executables(
|
||||
const ExecutorEntitiesCollection & collection,
|
||||
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result
|
||||
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
|
||||
std::deque<rclcpp::AnyExecutable> & executables
|
||||
);
|
||||
|
||||
} // namespace executors
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -33,6 +33,7 @@ public:
|
||||
|
||||
virtual void clear() = 0;
|
||||
virtual bool has_data() const = 0;
|
||||
virtual size_t available_capacity() const = 0;
|
||||
};
|
||||
|
||||
} // namespace buffers
|
||||
|
||||
@@ -44,6 +44,7 @@ public:
|
||||
|
||||
virtual bool has_data() const = 0;
|
||||
virtual bool use_take_shared_method() const = 0;
|
||||
virtual size_t available_capacity() const = 0;
|
||||
};
|
||||
|
||||
template<
|
||||
@@ -95,7 +96,7 @@ public:
|
||||
|
||||
buffer_ = std::move(buffer_impl);
|
||||
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_buffer_to_ipb,
|
||||
static_cast<const void *>(buffer_.get()),
|
||||
static_cast<const void *>(this));
|
||||
@@ -143,6 +144,11 @@ public:
|
||||
return std::is_same<BufferT, MessageSharedPtr>::value;
|
||||
}
|
||||
|
||||
size_t available_capacity() const override
|
||||
{
|
||||
return buffer_->available_capacity();
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<BufferImplementationBase<BufferT>> buffer_;
|
||||
|
||||
|
||||
@@ -52,7 +52,10 @@ public:
|
||||
if (capacity == 0) {
|
||||
throw std::invalid_argument("capacity must be a positive, non-zero value");
|
||||
}
|
||||
TRACEPOINT(rclcpp_construct_ring_buffer, static_cast<const void *>(this), capacity_);
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_construct_ring_buffer,
|
||||
static_cast<const void *>(this),
|
||||
capacity_);
|
||||
}
|
||||
|
||||
virtual ~RingBufferImplementation() {}
|
||||
@@ -69,7 +72,7 @@ public:
|
||||
|
||||
write_index_ = next_(write_index_);
|
||||
ring_buffer_[write_index_] = std::move(request);
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_ring_buffer_enqueue,
|
||||
static_cast<const void *>(this),
|
||||
write_index_,
|
||||
@@ -98,7 +101,7 @@ public:
|
||||
}
|
||||
|
||||
auto request = std::move(ring_buffer_[read_index_]);
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_ring_buffer_dequeue,
|
||||
static_cast<const void *>(this),
|
||||
read_index_,
|
||||
@@ -148,9 +151,21 @@ public:
|
||||
return is_full_();
|
||||
}
|
||||
|
||||
/// Get the remaining capacity to store messages
|
||||
/**
|
||||
* This member function is thread-safe.
|
||||
*
|
||||
* \return the number of free capacity for new messages
|
||||
*/
|
||||
size_t available_capacity() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return available_capacity_();
|
||||
}
|
||||
|
||||
void clear()
|
||||
{
|
||||
TRACEPOINT(rclcpp_ring_buffer_clear, static_cast<const void *>(this));
|
||||
TRACETOOLS_TRACEPOINT(rclcpp_ring_buffer_clear, static_cast<const void *>(this));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -189,6 +204,17 @@ private:
|
||||
return size_ == capacity_;
|
||||
}
|
||||
|
||||
/// Get the remaining capacity to store messages
|
||||
/**
|
||||
* This member function is not thread-safe.
|
||||
*
|
||||
* \return the number of free capacity for new messages
|
||||
*/
|
||||
inline size_t available_capacity_() const
|
||||
{
|
||||
return capacity_ - size_;
|
||||
}
|
||||
|
||||
size_t capacity_;
|
||||
|
||||
std::vector<BufferT> ring_buffer_;
|
||||
|
||||
@@ -243,6 +243,11 @@ private:
|
||||
std::function<void(size_t, int)>
|
||||
create_waitable_callback(const rclcpp::Waitable * waitable_id);
|
||||
|
||||
/// Utility to add the notify waitable to an entities collection
|
||||
void
|
||||
add_notify_waitable_to_collection(
|
||||
rclcpp::executors::ExecutorEntitiesCollection::WaitableCollection & collection);
|
||||
|
||||
/// Searches for the provided entity_id in the collection and returns the entity if valid
|
||||
template<typename CollectionType>
|
||||
typename CollectionType::EntitySharedPtr
|
||||
@@ -269,9 +274,12 @@ private:
|
||||
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue_;
|
||||
|
||||
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollector> entities_collector_;
|
||||
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollection> current_entities_collection_;
|
||||
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> notify_waitable_;
|
||||
|
||||
/// Mutex to protect the current_entities_collection_
|
||||
std::recursive_mutex collection_mutex_;
|
||||
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollection> current_entities_collection_;
|
||||
|
||||
/// Flag used to reduce the number of unnecessary waitable events
|
||||
std::atomic<bool> notify_waitable_event_pushed_ {false};
|
||||
|
||||
|
||||
@@ -306,6 +306,11 @@ public:
|
||||
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr
|
||||
get_subscription_intra_process(uint64_t intra_process_subscription_id);
|
||||
|
||||
/// Return the lowest available capacity for all subscription buffers for a publisher id.
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
lowest_available_capacity(const uint64_t intra_process_publisher_id) const;
|
||||
|
||||
private:
|
||||
struct SplittedSubscriptions
|
||||
{
|
||||
@@ -481,13 +486,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 {
|
||||
|
||||
@@ -87,7 +87,7 @@ public:
|
||||
buffer_type),
|
||||
any_callback_(callback)
|
||||
{
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_subscription_callback_added,
|
||||
static_cast<const void *>(this),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
|
||||
@@ -62,6 +62,11 @@ public:
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
available_capacity() const = 0;
|
||||
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set) override = 0;
|
||||
|
||||
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
buffer_type,
|
||||
qos_profile,
|
||||
std::make_shared<Alloc>(subscribed_type_allocator_));
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_ipb_to_subscription,
|
||||
static_cast<const void *>(buffer_.get()),
|
||||
static_cast<const void *>(this));
|
||||
@@ -169,6 +169,11 @@ public:
|
||||
return buffer_->use_take_shared_method();
|
||||
}
|
||||
|
||||
size_t available_capacity() const override
|
||||
{
|
||||
return buffer_->available_capacity();
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
trigger_guard_condition() override
|
||||
|
||||
@@ -527,7 +527,7 @@ private:
|
||||
void execute_ready_timers_unsafe();
|
||||
|
||||
// Callback to be called when timer is ready
|
||||
std::function<void(const rclcpp::TimerBase *)> on_ready_callback_ = nullptr;
|
||||
std::function<void(const rclcpp::TimerBase *)> on_ready_callback_;
|
||||
|
||||
// Thread used to run the timers execution task
|
||||
std::thread timers_thread_;
|
||||
|
||||
@@ -56,6 +56,7 @@
|
||||
#include "rclcpp/node_interfaces/node_time_source_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_type_descriptions_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_waitables_interface.hpp"
|
||||
#include "rclcpp/node_options.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
@@ -232,13 +233,15 @@ public:
|
||||
* \param[in] period Time interval between triggers of the callback.
|
||||
* \param[in] callback User-defined callback function.
|
||||
* \param[in] group Callback group to execute this timer's callback in.
|
||||
* \param[in] autostart The state of the clock on initialization.
|
||||
*/
|
||||
template<typename DurationRepT = int64_t, typename DurationT = std::milli, typename CallbackT>
|
||||
typename rclcpp::WallTimer<CallbackT>::SharedPtr
|
||||
create_wall_timer(
|
||||
std::chrono::duration<DurationRepT, DurationT> period,
|
||||
CallbackT callback,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr,
|
||||
bool autostart = true);
|
||||
|
||||
/// Create a timer that uses the node clock to drive the callback.
|
||||
/**
|
||||
@@ -1454,6 +1457,11 @@ public:
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr
|
||||
get_node_time_source_interface();
|
||||
|
||||
/// Return the Node's internal NodeTypeDescriptionsInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr
|
||||
get_node_type_descriptions_interface();
|
||||
|
||||
/// Return the sub-namespace, if this is a sub-node, otherwise an empty string.
|
||||
/**
|
||||
* The returned sub-namespace is either the accumulated sub-namespaces which
|
||||
@@ -1586,11 +1594,18 @@ private:
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr node_time_source_;
|
||||
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr node_type_descriptions_;
|
||||
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables_;
|
||||
|
||||
const rclcpp::NodeOptions node_options_;
|
||||
const std::string sub_namespace_;
|
||||
const std::string effective_namespace_;
|
||||
|
||||
class NodeImpl;
|
||||
// This member is meant to be a place to backport features into stable distributions,
|
||||
// and new features targeting Rolling should not use this.
|
||||
// See the comment in node.cpp for more information.
|
||||
std::shared_ptr<NodeImpl> hidden_impl_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -110,14 +110,16 @@ typename rclcpp::WallTimer<CallbackT>::SharedPtr
|
||||
Node::create_wall_timer(
|
||||
std::chrono::duration<DurationRepT, DurationT> period,
|
||||
CallbackT callback,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
rclcpp::CallbackGroup::SharedPtr group,
|
||||
bool autostart)
|
||||
{
|
||||
return rclcpp::create_wall_timer(
|
||||
period,
|
||||
std::move(callback),
|
||||
group,
|
||||
this->node_base_.get(),
|
||||
this->node_timers_.get());
|
||||
this->node_timers_.get(),
|
||||
autostart);
|
||||
}
|
||||
|
||||
template<typename DurationRepT, typename DurationT, typename CallbackT>
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface, \
|
||||
rclcpp::node_interfaces::NodeTimersInterface, \
|
||||
rclcpp::node_interfaces::NodeTopicsInterface, \
|
||||
rclcpp::node_interfaces::NodeTypeDescriptionsInterface, \
|
||||
rclcpp::node_interfaces::NodeWaitablesInterface
|
||||
|
||||
|
||||
@@ -118,6 +119,7 @@ public:
|
||||
* - rclcpp::node_interfaces::NodeTimeSourceInterface
|
||||
* - rclcpp::node_interfaces::NodeTimersInterface
|
||||
* - rclcpp::node_interfaces::NodeTopicsInterface
|
||||
* - rclcpp::node_interfaces::NodeTypeDescriptionsInterface
|
||||
* - rclcpp::node_interfaces::NodeWaitablesInterface
|
||||
*
|
||||
* Or you use custom interfaces as long as you make a template specialization
|
||||
|
||||
@@ -23,6 +23,9 @@
|
||||
#include "rclcpp/node_interfaces/node_logging_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rcl_interfaces/srv/get_logger_levels.hpp"
|
||||
#include "rcl_interfaces/srv/set_logger_levels.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
@@ -35,7 +38,7 @@ public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeLoggingInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit NodeLogging(rclcpp::node_interfaces::NodeBaseInterface * node_base);
|
||||
explicit NodeLogging(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -49,13 +52,21 @@ public:
|
||||
const char *
|
||||
get_logger_name() const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
create_logger_services(
|
||||
node_interfaces::NodeServicesInterface::SharedPtr node_services) override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeLogging)
|
||||
|
||||
/// Handle to the NodeBaseInterface given in the constructor.
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
|
||||
|
||||
rclcpp::Logger logger_;
|
||||
|
||||
rclcpp::Service<rcl_interfaces::srv::GetLoggerLevels>::SharedPtr get_loggers_service_;
|
||||
rclcpp::Service<rcl_interfaces::srv::SetLoggerLevels>::SharedPtr set_loggers_service_;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "rclcpp/logger.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_services_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -54,6 +55,13 @@ public:
|
||||
virtual
|
||||
const char *
|
||||
get_logger_name() const = 0;
|
||||
|
||||
/// create logger services
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
create_logger_services(
|
||||
node_interfaces::NodeServicesInterface::SharedPtr node_services) = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2023 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__NODE_INTERFACES__NODE_TYPE_DESCRIPTIONS_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_TYPE_DESCRIPTIONS_HPP_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_logging_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_services_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_type_descriptions_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
/// Implementation of the NodeTypeDescriptions part of the Node API.
|
||||
class NodeTypeDescriptions : public NodeTypeDescriptionsInterface
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTypeDescriptions)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit NodeTypeDescriptions(
|
||||
NodeBaseInterface::SharedPtr node_base,
|
||||
NodeLoggingInterface::SharedPtr node_logging,
|
||||
NodeParametersInterface::SharedPtr node_parameters,
|
||||
NodeServicesInterface::SharedPtr node_services);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeTypeDescriptions();
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeTypeDescriptions)
|
||||
|
||||
// Pimpl hides helper types and functions used for wrapping a C service, which would be
|
||||
// awkward to expose in this header.
|
||||
class NodeTypeDescriptionsImpl;
|
||||
std::unique_ptr<NodeTypeDescriptionsImpl> impl_;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_TYPE_DESCRIPTIONS_HPP_
|
||||
@@ -0,0 +1,44 @@
|
||||
// Copyright 2023 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__NODE_INTERFACES__NODE_TYPE_DESCRIPTIONS_INTERFACE_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_TYPE_DESCRIPTIONS_INTERFACE_HPP_
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
/// Pure virtual interface class for the NodeTypeDescriptions part of the Node API.
|
||||
class NodeTypeDescriptionsInterface
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTypeDescriptionsInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeTypeDescriptionsInterface() = default;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(
|
||||
rclcpp::node_interfaces::NodeTypeDescriptionsInterface, type_descriptions)
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_TYPE_DESCRIPTIONS_INTERFACE_HPP_
|
||||
@@ -50,6 +50,7 @@ public:
|
||||
* - clock_type = RCL_ROS_TIME
|
||||
* - clock_qos = rclcpp::ClockQoS()
|
||||
* - use_clock_thread = true
|
||||
* - enable_logger_service = false
|
||||
* - rosout_qos = rclcpp::RosoutQoS()
|
||||
* - parameter_event_qos = rclcpp::ParameterEventQoS
|
||||
* - with history setting and depth from rmw_qos_profile_parameter_events
|
||||
@@ -232,6 +233,24 @@ public:
|
||||
NodeOptions &
|
||||
start_parameter_services(bool start_parameter_services);
|
||||
|
||||
/// Return the enable_logger_service flag.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
enable_logger_service() const;
|
||||
|
||||
/// Set the enable_logger_service flag, return this for logger idiom.
|
||||
/**
|
||||
* If true, ROS services are created to allow external nodes to get
|
||||
* and set logger levels of this node.
|
||||
*
|
||||
* If false, loggers will still be configured and set logger levels locally,
|
||||
* but logger levels cannot be changed remotely .
|
||||
*
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
enable_logger_service(bool enable_log_service);
|
||||
|
||||
/// Return the start_parameter_event_publisher flag.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
@@ -421,6 +440,8 @@ private:
|
||||
|
||||
bool use_clock_thread_ {true};
|
||||
|
||||
bool enable_logger_service_ {false};
|
||||
|
||||
rclcpp::QoS parameter_event_qos_ = rclcpp::ParameterEventsQoS(
|
||||
rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)
|
||||
);
|
||||
|
||||
@@ -48,7 +48,7 @@ public:
|
||||
*
|
||||
* Example Usage:
|
||||
*
|
||||
* If you have recieved a parameter event and are only interested in parameters foo and
|
||||
* If you have received a parameter event and are only interested in parameters foo and
|
||||
* bar being added or changed but don't care about deletion.
|
||||
*
|
||||
* ```cpp
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#include "rcl_interfaces/msg/parameter_type.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_value.hpp"
|
||||
#include "rclcpp/exceptions/exceptions.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
|
||||
@@ -421,7 +421,7 @@ protected:
|
||||
void
|
||||
do_inter_process_publish(const ROSMessageType & msg)
|
||||
{
|
||||
TRACEPOINT(rclcpp_publish, nullptr, static_cast<const void *>(&msg));
|
||||
TRACETOOLS_TRACEPOINT(rclcpp_publish, nullptr, static_cast<const void *>(&msg));
|
||||
auto status = rcl_publish(publisher_handle_.get(), &msg, nullptr);
|
||||
|
||||
if (RCL_RET_PUBLISHER_INVALID == status) {
|
||||
@@ -456,6 +456,7 @@ protected:
|
||||
do_loaned_message_publish(
|
||||
std::unique_ptr<ROSMessageType, std::function<void(ROSMessageType *)>> msg)
|
||||
{
|
||||
TRACETOOLS_TRACEPOINT(rclcpp_publish, nullptr, static_cast<const void *>(msg.get()));
|
||||
auto status = rcl_publish_loaned_message(publisher_handle_.get(), msg.get(), nullptr);
|
||||
|
||||
if (RCL_RET_PUBLISHER_INVALID == status) {
|
||||
@@ -484,7 +485,7 @@ protected:
|
||||
if (!msg) {
|
||||
throw std::runtime_error("cannot publish msg which is a null pointer");
|
||||
}
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_intra_publish,
|
||||
static_cast<const void *>(publisher_handle_.get()),
|
||||
msg.get());
|
||||
@@ -506,7 +507,7 @@ protected:
|
||||
if (!msg) {
|
||||
throw std::runtime_error("cannot publish msg which is a null pointer");
|
||||
}
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_intra_publish,
|
||||
static_cast<const void *>(publisher_handle_.get()),
|
||||
msg.get());
|
||||
@@ -529,7 +530,7 @@ protected:
|
||||
if (!msg) {
|
||||
throw std::runtime_error("cannot publish msg which is a null pointer");
|
||||
}
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_intra_publish,
|
||||
static_cast<const void *>(publisher_handle_.get()),
|
||||
msg.get());
|
||||
|
||||
@@ -215,6 +215,17 @@ public:
|
||||
std::vector<rclcpp::NetworkFlowEndpoint>
|
||||
get_network_flow_endpoints() const;
|
||||
|
||||
/// Return the lowest available capacity for all subscription buffers.
|
||||
/**
|
||||
* For intraprocess communication return the lowest buffer capacity for all subscriptions.
|
||||
* If intraprocess is disabled or no intraprocess subscriptions present, return maximum of size_t.
|
||||
* On failure return 0.
|
||||
* \return lowest buffer capacity for all subscriptions
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
lowest_available_ipm_capacity() const;
|
||||
|
||||
/// Wait until all published messages are acknowledged or until the specified timeout elapses.
|
||||
/**
|
||||
* This method waits until all published messages are acknowledged by all matching
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "rclcpp/clock.hpp"
|
||||
#include "rclcpp/duration.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -31,9 +33,20 @@ class RateBase
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(RateBase)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~RateBase() {}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool sleep() = 0;
|
||||
|
||||
[[deprecated("use get_type() instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool is_steady() const = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual rcl_clock_type_t get_type() const = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual void reset() = 0;
|
||||
};
|
||||
|
||||
@@ -42,14 +55,13 @@ using std::chrono::duration_cast;
|
||||
using std::chrono::nanoseconds;
|
||||
|
||||
template<class Clock = std::chrono::high_resolution_clock>
|
||||
class GenericRate : public RateBase
|
||||
class [[deprecated("use rclcpp::Rate class instead of GenericRate")]] GenericRate : public RateBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate)
|
||||
|
||||
explicit GenericRate(double rate)
|
||||
: GenericRate<Clock>(
|
||||
duration_cast<nanoseconds>(duration<double>(1.0 / rate)))
|
||||
: period_(duration_cast<nanoseconds>(duration<double>(1.0 / rate))), last_interval_(Clock::now())
|
||||
{}
|
||||
explicit GenericRate(std::chrono::nanoseconds period)
|
||||
: period_(period), last_interval_(Clock::now())
|
||||
@@ -87,12 +99,18 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
[[deprecated("use get_type() instead")]]
|
||||
virtual bool
|
||||
is_steady() const
|
||||
{
|
||||
return Clock::is_steady;
|
||||
}
|
||||
|
||||
virtual rcl_clock_type_t get_type() const
|
||||
{
|
||||
return Clock::is_steady ? RCL_STEADY_TIME : RCL_SYSTEM_TIME;
|
||||
}
|
||||
|
||||
virtual void
|
||||
reset()
|
||||
{
|
||||
@@ -112,8 +130,69 @@ private:
|
||||
std::chrono::time_point<Clock, ClockDurationNano> last_interval_;
|
||||
};
|
||||
|
||||
using Rate = GenericRate<std::chrono::system_clock>;
|
||||
using WallRate = GenericRate<std::chrono::steady_clock>;
|
||||
class Rate : public RateBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Rate)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit Rate(
|
||||
const double rate,
|
||||
Clock::SharedPtr clock = std::make_shared<Clock>(RCL_SYSTEM_TIME));
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit Rate(
|
||||
const Duration & period,
|
||||
Clock::SharedPtr clock = std::make_shared<Clock>(RCL_SYSTEM_TIME));
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool
|
||||
sleep();
|
||||
|
||||
[[deprecated("use get_type() instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool
|
||||
is_steady() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual rcl_clock_type_t
|
||||
get_type() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
reset();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::chrono::nanoseconds
|
||||
period() const;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Rate)
|
||||
|
||||
Clock::SharedPtr clock_;
|
||||
Duration period_;
|
||||
Time last_interval_;
|
||||
};
|
||||
|
||||
class WallRate : public Rate
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
explicit WallRate(const double rate);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit WallRate(const Duration & period);
|
||||
};
|
||||
|
||||
class ROSRate : public Rate
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
explicit ROSRate(const double rate);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit ROSRate(const Duration & period);
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -265,15 +265,19 @@ protected:
|
||||
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
|
||||
std::recursive_mutex callback_mutex_;
|
||||
// It is important to declare on_new_request_callback_ before
|
||||
// service_handle_, so on destruction the service is
|
||||
// destroyed first. Otherwise, the rmw service callback
|
||||
// would point briefly to a destroyed function.
|
||||
std::function<void(size_t)> on_new_request_callback_{nullptr};
|
||||
// Declare service_handle_ after callback
|
||||
std::shared_ptr<rcl_service_t> service_handle_;
|
||||
bool owns_rcl_handle_ = true;
|
||||
|
||||
rclcpp::Logger node_logger_;
|
||||
|
||||
std::atomic<bool> in_use_by_wait_set_{false};
|
||||
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::function<void(size_t)> on_new_request_callback_{nullptr};
|
||||
};
|
||||
|
||||
template<typename ServiceT>
|
||||
@@ -348,7 +352,7 @@ public:
|
||||
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
|
||||
}
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_service_callback_added,
|
||||
static_cast<const void *>(get_service_handle().get()),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
@@ -383,7 +387,7 @@ public:
|
||||
}
|
||||
|
||||
service_handle_ = service_handle;
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_service_callback_added,
|
||||
static_cast<const void *>(get_service_handle().get()),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
@@ -420,7 +424,7 @@ public:
|
||||
// In this case, rcl owns the service handle memory
|
||||
service_handle_ = std::shared_ptr<rcl_service_t>(new rcl_service_t);
|
||||
service_handle_->impl = service_handle->impl;
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_service_callback_added,
|
||||
static_cast<const void *>(get_service_handle().get()),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
@@ -482,6 +486,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");
|
||||
}
|
||||
|
||||
@@ -185,7 +185,7 @@ public:
|
||||
this->get_topic_name(), // important to get like this, as it has the fully-qualified name
|
||||
qos_profile,
|
||||
resolve_intra_process_buffer_type(options_.intra_process_buffer_type, callback));
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_subscription_init,
|
||||
static_cast<const void *>(get_subscription_handle().get()),
|
||||
static_cast<const void *>(subscription_intra_process_.get()));
|
||||
@@ -201,11 +201,11 @@ public:
|
||||
this->subscription_topic_statistics_ = std::move(subscription_topic_statistics);
|
||||
}
|
||||
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_subscription_init,
|
||||
static_cast<const void *>(get_subscription_handle().get()),
|
||||
static_cast<const void *>(this));
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_subscription_callback_added,
|
||||
static_cast<const void *>(this),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
|
||||
@@ -260,13 +260,13 @@ public:
|
||||
bool
|
||||
is_serialized() const;
|
||||
|
||||
/// Return the type of the subscription.
|
||||
/// Return the delivered message kind.
|
||||
/**
|
||||
* \return `DeliveredMessageKind`, which adjusts how messages are received and delivered.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
DeliveredMessageKind
|
||||
get_subscription_type() const;
|
||||
get_delivered_message_kind() const;
|
||||
|
||||
/// Get matching publisher count.
|
||||
/** \return The number of publishers on this topic. */
|
||||
@@ -645,6 +645,14 @@ protected:
|
||||
rclcpp::node_interfaces::NodeBaseInterface * const node_base_;
|
||||
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
|
||||
std::recursive_mutex callback_mutex_;
|
||||
// It is important to declare on_new_message_callback_ before
|
||||
// subscription_handle_, so on destruction the subscription is
|
||||
// destroyed first. Otherwise, the rmw subscription callback
|
||||
// would point briefly to a destroyed function.
|
||||
std::function<void(size_t)> on_new_message_callback_{nullptr};
|
||||
// Declare subscription_handle_ after callback
|
||||
std::shared_ptr<rcl_subscription_t> subscription_handle_;
|
||||
std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;
|
||||
rclcpp::Logger node_logger_;
|
||||
@@ -663,15 +671,12 @@ private:
|
||||
RCLCPP_DISABLE_COPY(SubscriptionBase)
|
||||
|
||||
rosidl_message_type_support_t type_support_;
|
||||
DeliveredMessageKind delivered_message_type_;
|
||||
DeliveredMessageKind delivered_message_kind_;
|
||||
|
||||
std::atomic<bool> subscription_in_use_by_wait_set_{false};
|
||||
std::atomic<bool> intra_process_subscription_waitable_in_use_by_wait_set_{false};
|
||||
std::unordered_map<rclcpp::EventHandlerBase *,
|
||||
std::atomic<bool>> qos_events_in_use_by_wait_set_;
|
||||
|
||||
std::recursive_mutex callback_mutex_;
|
||||
std::function<void(size_t)> on_new_message_callback_{nullptr};
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -53,12 +53,17 @@ public:
|
||||
* \param clock A clock to use for time and sleeping
|
||||
* \param period The interval at which the timer fires
|
||||
* \param context node context
|
||||
* \param autostart timer state on initialization
|
||||
*
|
||||
* In order to activate a timer that is not started on initialization,
|
||||
* user should call the reset() method.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit TimerBase(
|
||||
Clock::SharedPtr clock,
|
||||
std::chrono::nanoseconds period,
|
||||
rclcpp::Context::SharedPtr context);
|
||||
rclcpp::Context::SharedPtr context,
|
||||
bool autostart = true);
|
||||
|
||||
/// TimerBase destructor
|
||||
RCLCPP_PUBLIC
|
||||
@@ -216,21 +221,22 @@ public:
|
||||
* \param[in] period The interval at which the timer fires.
|
||||
* \param[in] callback User-specified callback function.
|
||||
* \param[in] context custom context to be used.
|
||||
* \param autostart timer state on initialization
|
||||
*/
|
||||
explicit GenericTimer(
|
||||
Clock::SharedPtr clock, std::chrono::nanoseconds period, FunctorT && callback,
|
||||
rclcpp::Context::SharedPtr context
|
||||
rclcpp::Context::SharedPtr context, bool autostart = true
|
||||
)
|
||||
: TimerBase(clock, period, context), callback_(std::forward<FunctorT>(callback))
|
||||
: TimerBase(clock, period, context, autostart), callback_(std::forward<FunctorT>(callback))
|
||||
{
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_timer_callback_added,
|
||||
static_cast<const void *>(get_timer_handle().get()),
|
||||
reinterpret_cast<const void *>(&callback_));
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
if (TRACEPOINT_ENABLED(rclcpp_callback_register)) {
|
||||
if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
|
||||
char * symbol = tracetools::get_symbol(callback_);
|
||||
DO_TRACEPOINT(
|
||||
TRACETOOLS_DO_TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
reinterpret_cast<const void *>(&callback_),
|
||||
symbol);
|
||||
@@ -269,9 +275,9 @@ public:
|
||||
void
|
||||
execute_callback() override
|
||||
{
|
||||
TRACEPOINT(callback_start, reinterpret_cast<const void *>(&callback_), false);
|
||||
TRACETOOLS_TRACEPOINT(callback_start, reinterpret_cast<const void *>(&callback_), false);
|
||||
execute_callback_delegate<>();
|
||||
TRACEPOINT(callback_end, reinterpret_cast<const void *>(&callback_));
|
||||
TRACETOOLS_TRACEPOINT(callback_end, reinterpret_cast<const void *>(&callback_));
|
||||
}
|
||||
|
||||
// void specialization
|
||||
@@ -330,13 +336,15 @@ public:
|
||||
* \param period The interval at which the timer fires
|
||||
* \param callback The callback function to execute every interval
|
||||
* \param context node context
|
||||
* \param autostart timer state on initialization
|
||||
*/
|
||||
WallTimer(
|
||||
std::chrono::nanoseconds period,
|
||||
FunctorT && callback,
|
||||
rclcpp::Context::SharedPtr context)
|
||||
rclcpp::Context::SharedPtr context,
|
||||
bool autostart = true)
|
||||
: GenericTimer<FunctorT>(
|
||||
std::make_shared<Clock>(RCL_STEADY_TIME), period, std::move(callback), context)
|
||||
std::make_shared<Clock>(RCL_STEADY_TIME), period, std::move(callback), context, autostart)
|
||||
{}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -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>20.0.0</version>
|
||||
<version>23.0.0</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
@@ -35,6 +35,7 @@
|
||||
|
||||
<depend>libstatistics_collector</depend>
|
||||
<depend>rcl</depend>
|
||||
<depend>rcl_logging_interface</depend>
|
||||
<depend>rcl_yaml_param_parser</depend>
|
||||
<depend>rcpputils</depend>
|
||||
<depend>rcutils</depend>
|
||||
|
||||
@@ -56,6 +56,16 @@ CallbackGroup::type() const
|
||||
return type_;
|
||||
}
|
||||
|
||||
size_t
|
||||
CallbackGroup::size() const
|
||||
{
|
||||
return
|
||||
subscription_ptrs_.size() +
|
||||
service_ptrs_.size() +
|
||||
client_ptrs_.size() +
|
||||
timer_ptrs_.size() +
|
||||
waitable_ptrs_.size();
|
||||
}
|
||||
void CallbackGroup::collect_all_ptrs(
|
||||
std::function<void(const rclcpp::SubscriptionBase::SharedPtr &)> sub_func,
|
||||
std::function<void(const rclcpp::ServiceBase::SharedPtr &)> service_func,
|
||||
|
||||
@@ -125,7 +125,6 @@ bool
|
||||
ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
// make an event to reuse, rather than create a new one each time
|
||||
auto node_ptr = node_graph_.lock();
|
||||
if (!node_ptr) {
|
||||
throw InvalidNodeError();
|
||||
@@ -138,6 +137,7 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
// check was non-blocking, return immediately
|
||||
return false;
|
||||
}
|
||||
// make an event to reuse, rather than create a new one each time
|
||||
auto event = node_ptr->get_graph_event();
|
||||
// update the time even on the first loop to account for time spent in the first call
|
||||
// to this->server_is_ready()
|
||||
|
||||
@@ -41,11 +41,14 @@ using namespace std::chrono_literals;
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
using rclcpp::Executor;
|
||||
|
||||
class rclcpp::ExecutorImplementation {};
|
||||
|
||||
Executor::Executor(const rclcpp::ExecutorOptions & options)
|
||||
: spinning(false),
|
||||
interrupt_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
|
||||
shutdown_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
|
||||
memory_strategy_(options.memory_strategy)
|
||||
memory_strategy_(options.memory_strategy),
|
||||
impl_(std::make_unique<rclcpp::ExecutorImplementation>())
|
||||
{
|
||||
// Store the context for later use.
|
||||
context_ = options.context;
|
||||
@@ -428,6 +431,22 @@ void Executor::spin_some(std::chrono::nanoseconds max_duration)
|
||||
return this->spin_some_impl(max_duration, false);
|
||||
}
|
||||
|
||||
void
|
||||
Executor::spin_node_all(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
|
||||
std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
this->add_node(node, false);
|
||||
spin_all(max_duration);
|
||||
this->remove_node(node, false);
|
||||
}
|
||||
|
||||
void
|
||||
Executor::spin_node_all(std::shared_ptr<rclcpp::Node> node, std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
this->spin_node_all(node->get_node_base_interface(), max_duration);
|
||||
}
|
||||
|
||||
void Executor::spin_all(std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
if (max_duration < 0ns) {
|
||||
@@ -522,13 +541,13 @@ Executor::execute_any_executable(AnyExecutable & any_exec)
|
||||
return;
|
||||
}
|
||||
if (any_exec.timer) {
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_executor_execute,
|
||||
static_cast<const void *>(any_exec.timer->get_timer_handle().get()));
|
||||
execute_timer(any_exec.timer);
|
||||
}
|
||||
if (any_exec.subscription) {
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_executor_execute,
|
||||
static_cast<const void *>(any_exec.subscription->get_subscription_handle().get()));
|
||||
execute_subscription(any_exec.subscription);
|
||||
@@ -600,7 +619,7 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
|
||||
rclcpp::MessageInfo message_info;
|
||||
message_info.get_rmw_message_info().from_intra_process = false;
|
||||
|
||||
switch (subscription->get_subscription_type()) {
|
||||
switch (subscription->get_delivered_message_kind()) {
|
||||
// Deliver ROS message
|
||||
case rclcpp::DeliveredMessageKind::ROS_MESSAGE:
|
||||
{
|
||||
@@ -723,7 +742,7 @@ Executor::execute_client(
|
||||
void
|
||||
Executor::wait_for_work(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
TRACEPOINT(rclcpp_executor_wait_for_work, timeout.count());
|
||||
TRACETOOLS_TRACEPOINT(rclcpp_executor_wait_for_work, timeout.count());
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
|
||||
@@ -879,7 +898,7 @@ Executor::get_next_ready_executable_from_map(
|
||||
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
|
||||
weak_groups_to_nodes)
|
||||
{
|
||||
TRACEPOINT(rclcpp_executor_get_next_ready);
|
||||
TRACETOOLS_TRACEPOINT(rclcpp_executor_get_next_ready);
|
||||
bool success = false;
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
// Check the timers to see if there are any that are ready
|
||||
|
||||
@@ -14,6 +14,21 @@
|
||||
|
||||
#include "rclcpp/executors.hpp"
|
||||
|
||||
void
|
||||
rclcpp::spin_all(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
exec.spin_node_all(node_ptr, max_duration);
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::spin_all(rclcpp::Node::SharedPtr node_ptr, std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
rclcpp::spin_all(node_ptr->get_node_base_interface(), max_duration);
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
||||
{
|
||||
|
||||
@@ -20,12 +20,13 @@ namespace executors
|
||||
{
|
||||
bool ExecutorEntitiesCollection::empty() const
|
||||
{
|
||||
return subscriptions.empty() &&
|
||||
timers.empty() &&
|
||||
guard_conditions.empty() &&
|
||||
clients.empty() &&
|
||||
services.empty() &&
|
||||
waitables.empty();
|
||||
return
|
||||
subscriptions.empty() &&
|
||||
timers.empty() &&
|
||||
guard_conditions.empty() &&
|
||||
clients.empty() &&
|
||||
services.empty() &&
|
||||
waitables.empty();
|
||||
}
|
||||
|
||||
void ExecutorEntitiesCollection::clear()
|
||||
@@ -38,7 +39,6 @@ void ExecutorEntitiesCollection::clear()
|
||||
waitables.clear();
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
build_entities_collection(
|
||||
const std::vector<rclcpp::CallbackGroup::WeakPtr> & callback_groups,
|
||||
@@ -94,109 +94,136 @@ build_entities_collection(
|
||||
}
|
||||
}
|
||||
|
||||
template<typename EntityCollectionType>
|
||||
void check_ready(
|
||||
EntityCollectionType & collection,
|
||||
std::deque<rclcpp::AnyExecutable> & executables,
|
||||
size_t size_of_waited_entities,
|
||||
typename EntityCollectionType::Key * waited_entities,
|
||||
std::function<bool(rclcpp::AnyExecutable &,
|
||||
typename EntityCollectionType::EntitySharedPtr &)> fill_executable)
|
||||
size_t
|
||||
ready_executables(
|
||||
const ExecutorEntitiesCollection & collection,
|
||||
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
|
||||
std::deque<rclcpp::AnyExecutable> & executables
|
||||
)
|
||||
{
|
||||
for (size_t ii = 0; ii < size_of_waited_entities; ++ii) {
|
||||
if (!waited_entities[ii]) {continue;}
|
||||
auto entity_iter = collection.find(waited_entities[ii]);
|
||||
if (entity_iter != collection.end()) {
|
||||
size_t added = 0;
|
||||
if (wait_result.kind() != rclcpp::WaitResultKind::Ready) {
|
||||
return added;
|
||||
}
|
||||
auto rcl_wait_set = wait_result.get_wait_set().get_rcl_wait_set();
|
||||
|
||||
// Cache shared pointers to groups to avoid extra work re-locking them
|
||||
std::map<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::CallbackGroup::SharedPtr,
|
||||
std::owner_less<rclcpp::CallbackGroup::WeakPtr>> group_map;
|
||||
|
||||
auto group_cache = [&group_map](const rclcpp::CallbackGroup::WeakPtr & weak_cbg_ptr)
|
||||
{
|
||||
if (group_map.count(weak_cbg_ptr) == 0) {
|
||||
group_map.insert({weak_cbg_ptr, weak_cbg_ptr.lock()});
|
||||
}
|
||||
return group_map.find(weak_cbg_ptr)->second;
|
||||
};
|
||||
|
||||
for (size_t ii = 0; ii < rcl_wait_set.size_of_timers; ++ii) {
|
||||
if (nullptr == rcl_wait_set.timers[ii]) {continue;}
|
||||
auto entity_iter = collection.timers.find(rcl_wait_set.timers[ii]);
|
||||
if (entity_iter != collection.timers.end()) {
|
||||
auto entity = entity_iter->second.entity.lock();
|
||||
if (!entity) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto callback_group = entity_iter->second.callback_group.lock();
|
||||
if (callback_group && !callback_group->can_be_taken_from().load()) {
|
||||
auto group_info = group_cache(entity_iter->second.callback_group);
|
||||
if (group_info && !group_info->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
if (!entity->call()) {
|
||||
continue;
|
||||
}
|
||||
rclcpp::AnyExecutable exec;
|
||||
|
||||
exec.callback_group = callback_group;
|
||||
if (fill_executable(exec, entity)) {
|
||||
executables.push_back(exec);
|
||||
}
|
||||
exec.timer = entity;
|
||||
exec.callback_group = group_info;
|
||||
executables.push_back(exec);
|
||||
added++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::deque<rclcpp::AnyExecutable>
|
||||
ready_executables(
|
||||
const ExecutorEntitiesCollection & collection,
|
||||
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result
|
||||
)
|
||||
{
|
||||
std::deque<rclcpp::AnyExecutable> ret;
|
||||
|
||||
if (wait_result.kind() != rclcpp::WaitResultKind::Ready) {
|
||||
return ret;
|
||||
}
|
||||
auto rcl_wait_set = wait_result.get_wait_set().get_rcl_wait_set();
|
||||
check_ready(
|
||||
collection.timers,
|
||||
ret,
|
||||
rcl_wait_set.size_of_timers,
|
||||
rcl_wait_set.timers,
|
||||
[](rclcpp::AnyExecutable & exec, auto timer) {
|
||||
if (!timer->call()) {
|
||||
return false;
|
||||
for (size_t ii = 0; ii < rcl_wait_set.size_of_subscriptions; ++ii) {
|
||||
if (nullptr == rcl_wait_set.subscriptions[ii]) {continue;}
|
||||
auto entity_iter = collection.subscriptions.find(rcl_wait_set.subscriptions[ii]);
|
||||
if (entity_iter != collection.subscriptions.end()) {
|
||||
auto entity = entity_iter->second.entity.lock();
|
||||
if (!entity) {
|
||||
continue;
|
||||
}
|
||||
exec.timer = timer;
|
||||
return true;
|
||||
});
|
||||
auto group_info = group_cache(entity_iter->second.callback_group);
|
||||
if (group_info && !group_info->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
rclcpp::AnyExecutable exec;
|
||||
exec.subscription = entity;
|
||||
exec.callback_group = group_info;
|
||||
executables.push_back(exec);
|
||||
added++;
|
||||
}
|
||||
}
|
||||
|
||||
check_ready(
|
||||
collection.subscriptions,
|
||||
ret,
|
||||
rcl_wait_set.size_of_subscriptions,
|
||||
rcl_wait_set.subscriptions,
|
||||
[](rclcpp::AnyExecutable & exec, auto subscription) {
|
||||
exec.subscription = subscription;
|
||||
return true;
|
||||
});
|
||||
for (size_t ii = 0; ii < rcl_wait_set.size_of_services; ++ii) {
|
||||
if (nullptr == rcl_wait_set.services[ii]) {continue;}
|
||||
auto entity_iter = collection.services.find(rcl_wait_set.services[ii]);
|
||||
if (entity_iter != collection.services.end()) {
|
||||
auto entity = entity_iter->second.entity.lock();
|
||||
if (!entity) {
|
||||
continue;
|
||||
}
|
||||
auto group_info = group_cache(entity_iter->second.callback_group);
|
||||
if (group_info && !group_info->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
rclcpp::AnyExecutable exec;
|
||||
exec.service = entity;
|
||||
exec.callback_group = group_info;
|
||||
executables.push_back(exec);
|
||||
added++;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
check_ready(
|
||||
collection.services,
|
||||
ret,
|
||||
rcl_wait_set.size_of_services,
|
||||
rcl_wait_set.services,
|
||||
[](rclcpp::AnyExecutable & exec, auto service) {
|
||||
exec.service = service;
|
||||
return true;
|
||||
});
|
||||
|
||||
check_ready(
|
||||
collection.clients,
|
||||
ret,
|
||||
rcl_wait_set.size_of_clients,
|
||||
rcl_wait_set.clients,
|
||||
[](rclcpp::AnyExecutable & exec, auto client) {
|
||||
exec.client = client;
|
||||
return true;
|
||||
});
|
||||
for (size_t ii = 0; ii < rcl_wait_set.size_of_clients; ++ii) {
|
||||
if (nullptr == rcl_wait_set.clients[ii]) {continue;}
|
||||
auto entity_iter = collection.clients.find(rcl_wait_set.clients[ii]);
|
||||
if (entity_iter != collection.clients.end()) {
|
||||
auto entity = entity_iter->second.entity.lock();
|
||||
if (!entity) {
|
||||
continue;
|
||||
}
|
||||
auto group_info = group_cache(entity_iter->second.callback_group);
|
||||
if (group_info && !group_info->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
rclcpp::AnyExecutable exec;
|
||||
exec.client = entity;
|
||||
exec.callback_group = group_info;
|
||||
executables.push_back(exec);
|
||||
added++;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto & [handle, entry] : collection.waitables) {
|
||||
auto waitable = entry.entity.lock();
|
||||
if (waitable && waitable->is_ready(&rcl_wait_set)) {
|
||||
auto group = entry.callback_group.lock();
|
||||
if (group && !group->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
rclcpp::AnyExecutable exec;
|
||||
exec.waitable = waitable;
|
||||
exec.callback_group = group;
|
||||
ret.push_back(exec);
|
||||
if (!waitable) {
|
||||
continue;
|
||||
}
|
||||
if (!waitable->is_ready(&rcl_wait_set)) {
|
||||
continue;
|
||||
}
|
||||
auto group_info = group_cache(entry.callback_group);
|
||||
if (group_info && !group_info->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
rclcpp::AnyExecutable exec;
|
||||
exec.waitable = waitable;
|
||||
exec.callback_group = group_info;
|
||||
exec.data = waitable->take_data();
|
||||
executables.push_back(exec);
|
||||
added++;
|
||||
}
|
||||
return ret;
|
||||
|
||||
return added;
|
||||
}
|
||||
|
||||
} // namespace executors
|
||||
|
||||
@@ -50,6 +50,9 @@ EventsExecutor::EventsExecutor(
|
||||
timers_manager_ =
|
||||
std::make_shared<rclcpp::experimental::TimersManager>(context_, timer_on_ready_cb);
|
||||
|
||||
this->current_entities_collection_ =
|
||||
std::make_shared<rclcpp::executors::ExecutorEntitiesCollection>();
|
||||
|
||||
notify_waitable_ = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(
|
||||
[this]() {
|
||||
// This callback is invoked when:
|
||||
@@ -61,6 +64,10 @@ EventsExecutor::EventsExecutor(
|
||||
this->refresh_current_collection_from_callback_groups();
|
||||
});
|
||||
|
||||
// Make sure that the notify waitable is immediately added to the collection
|
||||
// to avoid missing events
|
||||
this->add_notify_waitable_to_collection(current_entities_collection_->waitables);
|
||||
|
||||
notify_waitable_->add_guard_condition(interrupt_guard_condition_);
|
||||
notify_waitable_->add_guard_condition(shutdown_guard_condition_);
|
||||
|
||||
@@ -87,9 +94,6 @@ EventsExecutor::EventsExecutor(
|
||||
|
||||
this->entities_collector_ =
|
||||
std::make_shared<rclcpp::executors::ExecutorEntitiesCollector>(notify_waitable_);
|
||||
|
||||
this->current_entities_collection_ =
|
||||
std::make_shared<rclcpp::executors::ExecutorEntitiesCollection>();
|
||||
}
|
||||
|
||||
EventsExecutor::~EventsExecutor()
|
||||
@@ -269,10 +273,13 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
switch (event.type) {
|
||||
case ExecutorEventType::CLIENT_EVENT:
|
||||
{
|
||||
auto client = this->retrieve_entity(
|
||||
static_cast<const rcl_client_t *>(event.entity_key),
|
||||
current_entities_collection_->clients);
|
||||
|
||||
rclcpp::ClientBase::SharedPtr client;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
client = this->retrieve_entity(
|
||||
static_cast<const rcl_client_t *>(event.entity_key),
|
||||
current_entities_collection_->clients);
|
||||
}
|
||||
if (client) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
execute_client(client);
|
||||
@@ -283,9 +290,13 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
}
|
||||
case ExecutorEventType::SUBSCRIPTION_EVENT:
|
||||
{
|
||||
auto subscription = this->retrieve_entity(
|
||||
static_cast<const rcl_subscription_t *>(event.entity_key),
|
||||
current_entities_collection_->subscriptions);
|
||||
rclcpp::SubscriptionBase::SharedPtr subscription;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
subscription = this->retrieve_entity(
|
||||
static_cast<const rcl_subscription_t *>(event.entity_key),
|
||||
current_entities_collection_->subscriptions);
|
||||
}
|
||||
if (subscription) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
execute_subscription(subscription);
|
||||
@@ -295,10 +306,13 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
}
|
||||
case ExecutorEventType::SERVICE_EVENT:
|
||||
{
|
||||
auto service = this->retrieve_entity(
|
||||
static_cast<const rcl_service_t *>(event.entity_key),
|
||||
current_entities_collection_->services);
|
||||
|
||||
rclcpp::ServiceBase::SharedPtr service;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
service = this->retrieve_entity(
|
||||
static_cast<const rcl_service_t *>(event.entity_key),
|
||||
current_entities_collection_->services);
|
||||
}
|
||||
if (service) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
execute_service(service);
|
||||
@@ -315,9 +329,13 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
}
|
||||
case ExecutorEventType::WAITABLE_EVENT:
|
||||
{
|
||||
auto waitable = this->retrieve_entity(
|
||||
static_cast<const rclcpp::Waitable *>(event.entity_key),
|
||||
current_entities_collection_->waitables);
|
||||
rclcpp::Waitable::SharedPtr waitable;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
waitable = this->retrieve_entity(
|
||||
static_cast<const rclcpp::Waitable *>(event.entity_key),
|
||||
current_entities_collection_->waitables);
|
||||
}
|
||||
if (waitable) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
auto data = waitable->take_data_by_entity_id(event.waitable_data);
|
||||
@@ -382,6 +400,7 @@ EventsExecutor::get_automatically_added_callback_groups_from_nodes()
|
||||
void
|
||||
EventsExecutor::refresh_current_collection_from_callback_groups()
|
||||
{
|
||||
// Build the new collection
|
||||
this->entities_collector_->update_collections();
|
||||
auto callback_groups = this->entities_collector_->get_all_callback_groups();
|
||||
rclcpp::executors::ExecutorEntitiesCollection new_collection;
|
||||
@@ -395,18 +414,11 @@ EventsExecutor::refresh_current_collection_from_callback_groups()
|
||||
// retrieved in the "standard" way.
|
||||
// To do it, we need to add the notify waitable as an entry in both the new and
|
||||
// current collections such that it's neither added or removed.
|
||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr;
|
||||
new_collection.waitables.insert(
|
||||
{
|
||||
this->notify_waitable_.get(),
|
||||
{this->notify_waitable_, weak_group_ptr}
|
||||
});
|
||||
this->add_notify_waitable_to_collection(new_collection.waitables);
|
||||
|
||||
this->current_entities_collection_->waitables.insert(
|
||||
{
|
||||
this->notify_waitable_.get(),
|
||||
{this->notify_waitable_, weak_group_ptr}
|
||||
});
|
||||
// Acquire lock before modifying the current collection
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
this->add_notify_waitable_to_collection(current_entities_collection_->waitables);
|
||||
|
||||
this->refresh_current_collection(new_collection);
|
||||
}
|
||||
@@ -415,6 +427,9 @@ void
|
||||
EventsExecutor::refresh_current_collection(
|
||||
const rclcpp::executors::ExecutorEntitiesCollection & new_collection)
|
||||
{
|
||||
// Acquire lock before modifying the current collection
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
|
||||
current_entities_collection_->timers.update(
|
||||
new_collection.timers,
|
||||
[this](rclcpp::TimerBase::SharedPtr timer) {timers_manager_->add_timer(timer);},
|
||||
@@ -486,3 +501,16 @@ EventsExecutor::create_waitable_callback(const rclcpp::Waitable * entity_key)
|
||||
};
|
||||
return callback;
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::add_notify_waitable_to_collection(
|
||||
rclcpp::executors::ExecutorEntitiesCollection::WaitableCollection & collection)
|
||||
{
|
||||
// The notify waitable is not associated to any group, so use an invalid one
|
||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr;
|
||||
collection.insert(
|
||||
{
|
||||
this->notify_waitable_.get(),
|
||||
{this->notify_waitable_, weak_group_ptr}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -28,9 +28,9 @@ using rclcpp::experimental::TimersManager;
|
||||
TimersManager::TimersManager(
|
||||
std::shared_ptr<rclcpp::Context> context,
|
||||
std::function<void(const rclcpp::TimerBase *)> on_ready_callback)
|
||||
: on_ready_callback_(on_ready_callback),
|
||||
context_(context)
|
||||
{
|
||||
context_ = context;
|
||||
on_ready_callback_ = on_ready_callback;
|
||||
}
|
||||
|
||||
TimersManager::~TimersManager()
|
||||
|
||||
@@ -225,5 +225,52 @@ IntraProcessManager::can_communicate(
|
||||
return true;
|
||||
}
|
||||
|
||||
size_t
|
||||
IntraProcessManager::lowest_available_capacity(const uint64_t intra_process_publisher_id) const
|
||||
{
|
||||
size_t capacity = std::numeric_limits<size_t>::max();
|
||||
|
||||
auto publisher_it = pub_to_subs_.find(intra_process_publisher_id);
|
||||
if (publisher_it == pub_to_subs_.end()) {
|
||||
// Publisher is either invalid or no longer exists.
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Calling lowest_available_capacity for invalid or no longer existing publisher id");
|
||||
return 0u;
|
||||
}
|
||||
|
||||
if (publisher_it->second.take_shared_subscriptions.empty() &&
|
||||
publisher_it->second.take_ownership_subscriptions.empty())
|
||||
{
|
||||
// no subscriptions available
|
||||
return 0u;
|
||||
}
|
||||
|
||||
auto available_capacity = [this, &capacity](const uint64_t intra_process_subscription_id)
|
||||
{
|
||||
auto subscription_it = subscriptions_.find(intra_process_subscription_id);
|
||||
if (subscription_it != subscriptions_.end()) {
|
||||
auto subscription = subscription_it->second.lock();
|
||||
if (subscription) {
|
||||
capacity = std::min(capacity, subscription->available_capacity());
|
||||
}
|
||||
} else {
|
||||
// Subscription is either invalid or no longer exists.
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Calling available_capacity for invalid or no longer existing subscription id");
|
||||
}
|
||||
};
|
||||
|
||||
for (const auto sub_id : publisher_it->second.take_shared_subscriptions) {
|
||||
available_capacity(sub_id);
|
||||
}
|
||||
|
||||
for (const auto sub_id : publisher_it->second.take_ownership_subscriptions) {
|
||||
available_capacity(sub_id);
|
||||
}
|
||||
|
||||
return capacity;
|
||||
}
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "rcutils/macros.h"
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include "rclcpp/node_interfaces/node_time_source.hpp"
|
||||
#include "rclcpp/node_interfaces/node_timers.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics.hpp"
|
||||
#include "rclcpp/node_interfaces/node_type_descriptions.hpp"
|
||||
#include "rclcpp/node_interfaces/node_waitables.hpp"
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
|
||||
@@ -109,6 +110,22 @@ create_effective_namespace(const std::string & node_namespace, const std::string
|
||||
|
||||
} // namespace
|
||||
|
||||
/// Internal implementation to provide hidden and API/ABI stable changes to the Node.
|
||||
/**
|
||||
* This class is intended to be an "escape hatch" within a stable distribution, so that certain
|
||||
* smaller features and bugfixes can be backported, having a place to put new members, while
|
||||
* maintaining the ABI.
|
||||
*
|
||||
* This is not intended to be a parking place for new features, it should be used for backports
|
||||
* only, left empty and unallocated in Rolling.
|
||||
*/
|
||||
class Node::NodeImpl
|
||||
{
|
||||
public:
|
||||
NodeImpl() = default;
|
||||
~NodeImpl() = default;
|
||||
};
|
||||
|
||||
Node::Node(
|
||||
const std::string & node_name,
|
||||
const NodeOptions & options)
|
||||
@@ -167,7 +184,7 @@ Node::Node(
|
||||
options.use_intra_process_comms(),
|
||||
options.enable_topic_statistics())),
|
||||
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
|
||||
node_logging_(new rclcpp::node_interfaces::NodeLogging(node_base_.get())),
|
||||
node_logging_(new rclcpp::node_interfaces::NodeLogging(node_base_)),
|
||||
node_timers_(new rclcpp::node_interfaces::NodeTimers(node_base_.get())),
|
||||
node_topics_(new rclcpp::node_interfaces::NodeTopics(node_base_.get(), node_timers_.get())),
|
||||
node_services_(new rclcpp::node_interfaces::NodeServices(node_base_.get())),
|
||||
@@ -206,6 +223,12 @@ Node::Node(
|
||||
options.clock_qos(),
|
||||
options.use_clock_thread()
|
||||
)),
|
||||
node_type_descriptions_(new rclcpp::node_interfaces::NodeTypeDescriptions(
|
||||
node_base_,
|
||||
node_logging_,
|
||||
node_parameters_,
|
||||
node_services_
|
||||
)),
|
||||
node_waitables_(new rclcpp::node_interfaces::NodeWaitables(node_base_.get())),
|
||||
node_options_(options),
|
||||
sub_namespace_(""),
|
||||
@@ -225,6 +248,10 @@ Node::Node(
|
||||
node_topics_->resolve_topic_name("/parameter_events"),
|
||||
options.parameter_event_qos(),
|
||||
rclcpp::detail::PublisherQosParametersTraits{});
|
||||
|
||||
if (options.enable_logger_service()) {
|
||||
node_logging_->create_logger_services(node_services_);
|
||||
}
|
||||
}
|
||||
|
||||
Node::Node(
|
||||
@@ -242,7 +269,8 @@ Node::Node(
|
||||
node_waitables_(other.node_waitables_),
|
||||
node_options_(other.node_options_),
|
||||
sub_namespace_(extend_sub_namespace(other.get_sub_namespace(), sub_namespace)),
|
||||
effective_namespace_(create_effective_namespace(other.get_namespace(), sub_namespace_))
|
||||
effective_namespace_(create_effective_namespace(other.get_namespace(), sub_namespace_)),
|
||||
hidden_impl_(other.hidden_impl_)
|
||||
{
|
||||
// Validate new effective namespace.
|
||||
int validation_result;
|
||||
@@ -587,6 +615,12 @@ Node::get_node_topics_interface()
|
||||
return node_topics_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeTypeDescriptionsInterface::SharedPtr
|
||||
Node::get_node_type_descriptions_interface()
|
||||
{
|
||||
return node_type_descriptions_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
|
||||
Node::get_node_services_interface()
|
||||
{
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "rclcpp/node_interfaces/node_base.hpp"
|
||||
|
||||
#include "rcl/arguments.h"
|
||||
#include "rcl/node_type_cache.h"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rcutils/logging_macros.h"
|
||||
#include "rmw/validate_namespace.h"
|
||||
|
||||
@@ -12,11 +12,13 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/node_impl.hpp"
|
||||
#include "rclcpp/node_interfaces/node_logging.hpp"
|
||||
#include "rclcpp/node_interfaces/node_services_interface.hpp"
|
||||
|
||||
using rclcpp::node_interfaces::NodeLogging;
|
||||
|
||||
NodeLogging::NodeLogging(rclcpp::node_interfaces::NodeBaseInterface * node_base)
|
||||
NodeLogging::NodeLogging(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base)
|
||||
: node_base_(node_base)
|
||||
{
|
||||
logger_ = rclcpp::get_logger(NodeLogging::get_logger_name());
|
||||
@@ -37,3 +39,55 @@ NodeLogging::get_logger_name() const
|
||||
{
|
||||
return rcl_node_get_logger_name(node_base_->get_rcl_node_handle());
|
||||
}
|
||||
|
||||
void NodeLogging::create_logger_services(
|
||||
node_interfaces::NodeServicesInterface::SharedPtr node_services)
|
||||
{
|
||||
rclcpp::ServicesQoS qos_profile;
|
||||
const std::string node_name = node_base_->get_name();
|
||||
auto callback_group = node_base_->get_default_callback_group();
|
||||
|
||||
get_loggers_service_ = rclcpp::create_service<rcl_interfaces::srv::GetLoggerLevels>(
|
||||
node_base_, node_services,
|
||||
node_name + "/get_logger_levels",
|
||||
[](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::GetLoggerLevels::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::GetLoggerLevels::Response> response)
|
||||
{
|
||||
for (auto & name : request->names) {
|
||||
rcl_interfaces::msg::LoggerLevel logger_level;
|
||||
logger_level.name = name;
|
||||
auto ret = rcutils_logging_get_logger_level(name.c_str());
|
||||
if (ret < 0) {
|
||||
logger_level.level = 0;
|
||||
} else {
|
||||
logger_level.level = static_cast<uint8_t>(ret);
|
||||
}
|
||||
response->levels.push_back(std::move(logger_level));
|
||||
}
|
||||
},
|
||||
qos_profile, callback_group);
|
||||
|
||||
set_loggers_service_ = rclcpp::create_service<rcl_interfaces::srv::SetLoggerLevels>(
|
||||
node_base_, node_services,
|
||||
node_name + "/set_logger_levels",
|
||||
[](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::SetLoggerLevels::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::SetLoggerLevels::Response> response)
|
||||
{
|
||||
rcl_interfaces::msg::SetLoggerLevelsResult result;
|
||||
for (auto & level : request->levels) {
|
||||
auto ret = rcutils_logging_set_logger_level(level.name.c_str(), level.level);
|
||||
if (ret != RCUTILS_RET_OK) {
|
||||
result.successful = false;
|
||||
result.reason = rcutils_get_error_string().str;
|
||||
} else {
|
||||
result.successful = true;
|
||||
}
|
||||
response->results.push_back(std::move(result));
|
||||
}
|
||||
},
|
||||
qos_profile, callback_group);
|
||||
}
|
||||
|
||||
@@ -1038,37 +1038,50 @@ NodeParameters::list_parameters(const std::vector<std::string> & prefixes, uint6
|
||||
// TODO(mikaelarguedas) define parameter separator different from "/" to avoid ambiguity
|
||||
// using "." for now
|
||||
const char * separator = ".";
|
||||
for (auto & kv : parameters_) {
|
||||
bool get_all = (prefixes.size() == 0) &&
|
||||
((depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
|
||||
(static_cast<uint64_t>(std::count(kv.first.begin(), kv.first.end(), *separator)) < depth));
|
||||
bool prefix_matches = std::any_of(
|
||||
prefixes.cbegin(), prefixes.cend(),
|
||||
[&kv, &depth, &separator](const std::string & prefix) {
|
||||
if (kv.first == prefix) {
|
||||
return true;
|
||||
} else if (kv.first.find(prefix + separator) == 0) {
|
||||
size_t length = prefix.length();
|
||||
std::string substr = kv.first.substr(length);
|
||||
// Cast as unsigned integer to avoid warning
|
||||
return (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
|
||||
(static_cast<uint64_t>(std::count(substr.begin(), substr.end(), *separator)) < depth);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
if (get_all || prefix_matches) {
|
||||
result.names.push_back(kv.first);
|
||||
size_t last_separator = kv.first.find_last_of(separator);
|
||||
if (std::string::npos != last_separator) {
|
||||
std::string prefix = kv.first.substr(0, last_separator);
|
||||
if (
|
||||
std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
|
||||
result.prefixes.cend())
|
||||
{
|
||||
result.prefixes.push_back(prefix);
|
||||
|
||||
auto separators_less_than_depth = [&depth, &separator](const std::string & str) -> bool {
|
||||
return static_cast<uint64_t>(std::count(str.begin(), str.end(), *separator)) < depth;
|
||||
};
|
||||
|
||||
bool recursive = (prefixes.size() == 0) &&
|
||||
(depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE);
|
||||
|
||||
for (const std::pair<const std::string, ParameterInfo> & kv : parameters_) {
|
||||
if (!recursive) {
|
||||
bool get_all = (prefixes.size() == 0) && separators_less_than_depth(kv.first);
|
||||
if (!get_all) {
|
||||
bool prefix_matches = std::any_of(
|
||||
prefixes.cbegin(), prefixes.cend(),
|
||||
[&kv, &depth, &separator, &separators_less_than_depth](const std::string & prefix) {
|
||||
if (kv.first == prefix) {
|
||||
return true;
|
||||
} else if (kv.first.find(prefix + separator) == 0) {
|
||||
if (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) {
|
||||
return true;
|
||||
}
|
||||
std::string substr = kv.first.substr(prefix.length());
|
||||
return separators_less_than_depth(substr);
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
if (!prefix_matches) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.names.push_back(kv.first);
|
||||
size_t last_separator = kv.first.find_last_of(separator);
|
||||
if (std::string::npos != last_separator) {
|
||||
std::string prefix = kv.first.substr(0, last_separator);
|
||||
if (
|
||||
std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
|
||||
result.prefixes.cend())
|
||||
{
|
||||
result.prefixes.push_back(prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -32,8 +32,7 @@ NodeServices::add_service(
|
||||
{
|
||||
if (group) {
|
||||
if (!node_base_->callback_group_in_node(group)) {
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create service, group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("service");
|
||||
}
|
||||
} else {
|
||||
group = node_base_->get_default_callback_group();
|
||||
@@ -58,8 +57,7 @@ NodeServices::add_client(
|
||||
{
|
||||
if (group) {
|
||||
if (!node_base_->callback_group_in_node(group)) {
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create client, group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("client");
|
||||
}
|
||||
} else {
|
||||
group = node_base_->get_default_callback_group();
|
||||
|
||||
@@ -34,8 +34,7 @@ NodeTimers::add_timer(
|
||||
{
|
||||
if (callback_group) {
|
||||
if (!node_base_->callback_group_in_node(callback_group)) {
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create timer, group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("timer");
|
||||
}
|
||||
} else {
|
||||
callback_group = node_base_->get_default_callback_group();
|
||||
@@ -50,7 +49,7 @@ NodeTimers::add_timer(
|
||||
std::string("failed to notify wait set on timer creation: ") + ex.what());
|
||||
}
|
||||
|
||||
TRACEPOINT(
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_timer_link_node,
|
||||
static_cast<const void *>(timer->get_timer_handle().get()),
|
||||
static_cast<const void *>(node_base_->get_rcl_node_handle()));
|
||||
|
||||
@@ -58,7 +58,7 @@ NodeTopics::add_publisher(
|
||||
// Assign to a group.
|
||||
if (callback_group) {
|
||||
if (!node_base_->callback_group_in_node(callback_group)) {
|
||||
throw std::runtime_error("Cannot create publisher, callback group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("publisher");
|
||||
}
|
||||
} else {
|
||||
callback_group = node_base_->get_default_callback_group();
|
||||
@@ -97,8 +97,7 @@ NodeTopics::add_subscription(
|
||||
// Assign to a group.
|
||||
if (callback_group) {
|
||||
if (!node_base_->callback_group_in_node(callback_group)) {
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create subscription, callback group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("subscription");
|
||||
}
|
||||
} else {
|
||||
callback_group = node_base_->get_default_callback_group();
|
||||
|
||||
157
rclcpp/src/rclcpp/node_interfaces/node_type_descriptions.cpp
Normal file
157
rclcpp/src/rclcpp/node_interfaces/node_type_descriptions.cpp
Normal file
@@ -0,0 +1,157 @@
|
||||
// Copyright 2023 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
#include "rclcpp/node_interfaces/node_type_descriptions.hpp"
|
||||
#include "rclcpp/parameter_client.hpp"
|
||||
|
||||
#include "type_description_interfaces/srv/get_type_description.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
// Helper wrapper for rclcpp::Service to access ::Request and ::Response types for allocation.
|
||||
struct GetTypeDescription__C
|
||||
{
|
||||
using Request = type_description_interfaces__srv__GetTypeDescription_Request;
|
||||
using Response = type_description_interfaces__srv__GetTypeDescription_Response;
|
||||
using Event = type_description_interfaces__srv__GetTypeDescription_Event;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
// Helper function for C typesupport.
|
||||
namespace rosidl_typesupport_cpp
|
||||
{
|
||||
template<>
|
||||
rosidl_service_type_support_t const *
|
||||
get_service_type_support_handle<GetTypeDescription__C>()
|
||||
{
|
||||
return ROSIDL_GET_SRV_TYPE_SUPPORT(type_description_interfaces, srv, GetTypeDescription);
|
||||
}
|
||||
} // namespace rosidl_typesupport_cpp
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
class NodeTypeDescriptions::NodeTypeDescriptionsImpl
|
||||
{
|
||||
public:
|
||||
using ServiceT = GetTypeDescription__C;
|
||||
|
||||
rclcpp::Logger logger_;
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
|
||||
rclcpp::Service<ServiceT>::SharedPtr type_description_srv_;
|
||||
|
||||
NodeTypeDescriptionsImpl(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters,
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services)
|
||||
: logger_(node_logging->get_logger()),
|
||||
node_base_(node_base)
|
||||
{
|
||||
const std::string enable_param_name = "start_type_description_service";
|
||||
|
||||
bool enabled = false;
|
||||
try {
|
||||
auto enable_param = node_parameters->declare_parameter(
|
||||
enable_param_name,
|
||||
rclcpp::ParameterValue(true),
|
||||
rcl_interfaces::msg::ParameterDescriptor()
|
||||
.set__name(enable_param_name)
|
||||
.set__type(rclcpp::PARAMETER_BOOL)
|
||||
.set__description("Start the ~/get_type_description service for this node.")
|
||||
.set__read_only(true));
|
||||
enabled = enable_param.get<bool>();
|
||||
} catch (const rclcpp::exceptions::InvalidParameterTypeException & exc) {
|
||||
RCLCPP_ERROR(logger_, "%s", exc.what());
|
||||
throw;
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
auto rcl_node = node_base->get_rcl_node_handle();
|
||||
rcl_ret_t rcl_ret = rcl_node_type_description_service_init(rcl_node);
|
||||
if (rcl_ret != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
logger_, "Failed to initialize ~/get_type_description_service: %s",
|
||||
rcl_get_error_string().str);
|
||||
throw std::runtime_error(
|
||||
"Failed to initialize ~/get_type_description service.");
|
||||
}
|
||||
|
||||
rcl_service_t * rcl_srv = nullptr;
|
||||
rcl_ret = rcl_node_get_type_description_service(rcl_node, &rcl_srv);
|
||||
if (rcl_ret != RCL_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
"Failed to get initialized ~/get_type_description service from rcl.");
|
||||
}
|
||||
|
||||
rclcpp::AnyServiceCallback<ServiceT> cb;
|
||||
cb.set(
|
||||
[this](
|
||||
std::shared_ptr<rmw_request_id_t> header,
|
||||
std::shared_ptr<ServiceT::Request> request,
|
||||
std::shared_ptr<ServiceT::Response> response
|
||||
) {
|
||||
rcl_node_type_description_service_handle_request(
|
||||
node_base_->get_rcl_node_handle(),
|
||||
header.get(),
|
||||
request.get(),
|
||||
response.get());
|
||||
});
|
||||
|
||||
type_description_srv_ = std::make_shared<Service<ServiceT>>(
|
||||
node_base_->get_shared_rcl_node_handle(),
|
||||
rcl_srv,
|
||||
cb);
|
||||
node_services->add_service(
|
||||
std::dynamic_pointer_cast<ServiceBase>(type_description_srv_),
|
||||
nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
~NodeTypeDescriptionsImpl()
|
||||
{
|
||||
if (
|
||||
type_description_srv_ &&
|
||||
RCL_RET_OK != rcl_node_type_description_service_fini(node_base_->get_rcl_node_handle()))
|
||||
{
|
||||
RCLCPP_ERROR(
|
||||
logger_,
|
||||
"Error in shutdown of get_type_description service: %s", rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
NodeTypeDescriptions::NodeTypeDescriptions(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters,
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services)
|
||||
: impl_(new NodeTypeDescriptionsImpl(
|
||||
node_base,
|
||||
node_logging,
|
||||
node_parameters,
|
||||
node_services))
|
||||
{}
|
||||
|
||||
NodeTypeDescriptions::~NodeTypeDescriptions()
|
||||
{}
|
||||
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
@@ -32,8 +32,7 @@ NodeWaitables::add_waitable(
|
||||
{
|
||||
if (group) {
|
||||
if (!node_base_->callback_group_in_node(group)) {
|
||||
// TODO(jacobperron): use custom exception
|
||||
throw std::runtime_error("Cannot create waitable, group not in node.");
|
||||
throw rclcpp::exceptions::MissingGroupNodeException("waitable");
|
||||
}
|
||||
} else {
|
||||
group = node_base_->get_default_callback_group();
|
||||
|
||||
@@ -248,6 +248,19 @@ NodeOptions::start_parameter_services(bool start_parameter_services)
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
NodeOptions::enable_logger_service() const
|
||||
{
|
||||
return this->enable_logger_service_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::enable_logger_service(bool enable_logger_service)
|
||||
{
|
||||
this->enable_logger_service_ = enable_logger_service;
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool
|
||||
NodeOptions::start_parameter_event_publisher() const
|
||||
{
|
||||
|
||||
@@ -129,8 +129,7 @@ ParameterValue::ParameterValue(const rcl_interfaces::msg::ParameterValue & value
|
||||
case PARAMETER_NOT_SET:
|
||||
break;
|
||||
default:
|
||||
// TODO(wjwwood): use custom exception
|
||||
throw std::runtime_error("Unknown type: " + std::to_string(value.type));
|
||||
throw rclcpp::exceptions::UnknownTypeError(std::to_string(value.type));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -384,3 +384,22 @@ std::vector<rclcpp::NetworkFlowEndpoint> PublisherBase::get_network_flow_endpoin
|
||||
|
||||
return network_flow_endpoint_vector;
|
||||
}
|
||||
|
||||
size_t PublisherBase::lowest_available_ipm_capacity() const
|
||||
{
|
||||
if (!intra_process_is_enabled_) {
|
||||
return 0u;
|
||||
}
|
||||
|
||||
auto ipm = weak_ipm_.lock();
|
||||
|
||||
if (!ipm) {
|
||||
// TODO(ivanpauno): should this raise an error?
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Intra process manager died for a publisher.");
|
||||
return 0u;
|
||||
}
|
||||
|
||||
return ipm->lowest_available_capacity(intra_process_publisher_id_);
|
||||
}
|
||||
|
||||
113
rclcpp/src/rclcpp/rate.cpp
Normal file
113
rclcpp/src/rclcpp/rate.cpp
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright 2023 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/rate.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
Rate::Rate(
|
||||
const double rate, Clock::SharedPtr clock)
|
||||
: clock_(clock), period_(0, 0), last_interval_(clock_->now())
|
||||
{
|
||||
if (rate <= 0.0) {
|
||||
throw std::invalid_argument{"rate must be greater than 0"};
|
||||
}
|
||||
period_ = Duration::from_seconds(1.0 / rate);
|
||||
}
|
||||
|
||||
Rate::Rate(
|
||||
const Duration & period, Clock::SharedPtr clock)
|
||||
: clock_(clock), period_(period), last_interval_(clock_->now())
|
||||
{
|
||||
if (period <= Duration(0, 0)) {
|
||||
throw std::invalid_argument{"period must be greater than 0"};
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
Rate::sleep()
|
||||
{
|
||||
// Time coming into sleep
|
||||
auto now = clock_->now();
|
||||
// Time of next interval
|
||||
auto next_interval = last_interval_ + period_;
|
||||
// Detect backwards time flow
|
||||
if (now < last_interval_) {
|
||||
// Best thing to do is to set the next_interval to now + period
|
||||
next_interval = now + period_;
|
||||
}
|
||||
// Update the interval
|
||||
last_interval_ += period_;
|
||||
// If the time_to_sleep is negative or zero, don't sleep
|
||||
if (next_interval <= now) {
|
||||
// If an entire cycle was missed then reset next interval.
|
||||
// This might happen if the loop took more than a cycle.
|
||||
// Or if time jumps forward.
|
||||
if (now > next_interval + period_) {
|
||||
last_interval_ = now + period_;
|
||||
}
|
||||
// Either way do not sleep and return false
|
||||
return false;
|
||||
}
|
||||
// Calculate the time to sleep
|
||||
auto time_to_sleep = next_interval - now;
|
||||
// Sleep (will get interrupted by ctrl-c, may not sleep full time)
|
||||
return clock_->sleep_for(time_to_sleep);
|
||||
}
|
||||
|
||||
bool
|
||||
Rate::is_steady() const
|
||||
{
|
||||
return clock_->get_clock_type() == RCL_STEADY_TIME;
|
||||
}
|
||||
|
||||
rcl_clock_type_t
|
||||
Rate::get_type() const
|
||||
{
|
||||
return clock_->get_clock_type();
|
||||
}
|
||||
|
||||
void
|
||||
Rate::reset()
|
||||
{
|
||||
last_interval_ = clock_->now();
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds
|
||||
Rate::period() const
|
||||
{
|
||||
return std::chrono::nanoseconds(period_.nanoseconds());
|
||||
}
|
||||
|
||||
WallRate::WallRate(const double rate)
|
||||
: Rate(rate, std::make_shared<Clock>(RCL_STEADY_TIME))
|
||||
{}
|
||||
|
||||
WallRate::WallRate(const Duration & period)
|
||||
: Rate(period, std::make_shared<Clock>(RCL_STEADY_TIME))
|
||||
{}
|
||||
|
||||
ROSRate::ROSRate(const double rate)
|
||||
: Rate(rate, std::make_shared<Clock>(RCL_ROS_TIME))
|
||||
{}
|
||||
|
||||
ROSRate::ROSRate(const Duration & period)
|
||||
: Rate(period, std::make_shared<Clock>(RCL_ROS_TIME))
|
||||
{}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -52,7 +52,7 @@ SubscriptionBase::SubscriptionBase(
|
||||
intra_process_subscription_id_(0),
|
||||
event_callbacks_(event_callbacks),
|
||||
type_support_(type_support_handle),
|
||||
delivered_message_type_(delivered_message_kind)
|
||||
delivered_message_kind_(delivered_message_kind)
|
||||
{
|
||||
auto custom_deletor = [node_handle = this->node_handle_](rcl_subscription_t * rcl_subs)
|
||||
{
|
||||
@@ -218,7 +218,7 @@ SubscriptionBase::take_type_erased(void * message_out, rclcpp::MessageInfo & mes
|
||||
&message_info_out.get_rmw_message_info(),
|
||||
nullptr // rmw_subscription_allocation_t is unused here
|
||||
);
|
||||
TRACEPOINT(rclcpp_take, static_cast<const void *>(message_out));
|
||||
TRACETOOLS_TRACEPOINT(rclcpp_take, static_cast<const void *>(message_out));
|
||||
if (RCL_RET_SUBSCRIPTION_TAKE_FAILED == ret) {
|
||||
return false;
|
||||
} else if (RCL_RET_OK != ret) {
|
||||
@@ -261,13 +261,13 @@ SubscriptionBase::get_message_type_support_handle() const
|
||||
bool
|
||||
SubscriptionBase::is_serialized() const
|
||||
{
|
||||
return delivered_message_type_ == rclcpp::DeliveredMessageKind::SERIALIZED_MESSAGE;
|
||||
return delivered_message_kind_ == rclcpp::DeliveredMessageKind::SERIALIZED_MESSAGE;
|
||||
}
|
||||
|
||||
rclcpp::DeliveredMessageKind
|
||||
SubscriptionBase::get_subscription_type() const
|
||||
SubscriptionBase::get_delivered_message_kind() const
|
||||
{
|
||||
return delivered_message_type_;
|
||||
return delivered_message_kind_;
|
||||
}
|
||||
|
||||
size_t
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -54,9 +55,7 @@ public:
|
||||
ros_time_active_ = true;
|
||||
|
||||
// Update all attached clocks to zero or last recorded time
|
||||
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
|
||||
set_clock(last_time_msg_, true, *it);
|
||||
}
|
||||
set_all_clocks(last_time_msg_, true);
|
||||
}
|
||||
|
||||
// An internal method to use in the clock callback that iterates and disables all clocks
|
||||
@@ -71,11 +70,8 @@ public:
|
||||
ros_time_active_ = false;
|
||||
|
||||
// Update all attached clocks
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
|
||||
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
set_clock(msg, false, *it);
|
||||
}
|
||||
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
set_all_clocks(msg, false);
|
||||
}
|
||||
|
||||
// Check if ROS time is active
|
||||
@@ -95,7 +91,7 @@ public:
|
||||
}
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
associated_clocks_.push_back(clock);
|
||||
associated_clocks_.insert(clock);
|
||||
// Set the clock to zero unless there's a recently received message
|
||||
set_clock(last_time_msg_, ros_time_active_, clock);
|
||||
}
|
||||
@@ -104,10 +100,8 @@ public:
|
||||
void detachClock(rclcpp::Clock::SharedPtr clock)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
auto result = std::find(associated_clocks_.begin(), associated_clocks_.end(), clock);
|
||||
if (result != associated_clocks_.end()) {
|
||||
associated_clocks_.erase(result);
|
||||
} else {
|
||||
auto removed = associated_clocks_.erase(clock);
|
||||
if (removed == 0) {
|
||||
RCLCPP_ERROR(logger_, "failed to remove clock");
|
||||
}
|
||||
}
|
||||
@@ -184,8 +178,8 @@ private:
|
||||
|
||||
// A lock to protect iterating the associated_clocks_ field.
|
||||
std::mutex clock_list_lock_;
|
||||
// A vector to store references to associated clocks.
|
||||
std::vector<rclcpp::Clock::SharedPtr> associated_clocks_;
|
||||
// An unordered_set to store references to associated clocks.
|
||||
std::unordered_set<rclcpp::Clock::SharedPtr> associated_clocks_;
|
||||
|
||||
// Local storage of validity of ROS time
|
||||
// This is needed when new clocks are added.
|
||||
|
||||
@@ -32,7 +32,8 @@ using rclcpp::TimerBase;
|
||||
TimerBase::TimerBase(
|
||||
rclcpp::Clock::SharedPtr clock,
|
||||
std::chrono::nanoseconds period,
|
||||
rclcpp::Context::SharedPtr context)
|
||||
rclcpp::Context::SharedPtr context,
|
||||
bool autostart)
|
||||
: clock_(clock), timer_handle_(nullptr)
|
||||
{
|
||||
if (nullptr == context) {
|
||||
@@ -64,9 +65,9 @@ TimerBase::TimerBase(
|
||||
rcl_clock_t * clock_handle = clock_->get_clock_handle();
|
||||
{
|
||||
std::lock_guard<std::mutex> clock_guard(clock_->get_clock_mutex());
|
||||
rcl_ret_t ret = rcl_timer_init(
|
||||
timer_handle_.get(), clock_handle, rcl_context.get(), period.count(), nullptr,
|
||||
rcl_get_default_allocator());
|
||||
rcl_ret_t ret = rcl_timer_init2(
|
||||
timer_handle_.get(), clock_handle, rcl_context.get(), period.count(),
|
||||
nullptr, rcl_get_default_allocator(), autostart);
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't initialize rcl timer handle");
|
||||
}
|
||||
|
||||
@@ -262,6 +262,11 @@ if(TARGET test_node_interfaces__node_topics)
|
||||
"test_msgs")
|
||||
target_link_libraries(test_node_interfaces__node_topics ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
ament_add_gtest(test_node_interfaces__node_type_descriptions
|
||||
node_interfaces/test_node_type_descriptions.cpp)
|
||||
if(TARGET test_node_interfaces__node_type_descriptions)
|
||||
target_link_libraries(test_node_interfaces__node_type_descriptions ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
ament_add_gtest(test_node_interfaces__node_waitables
|
||||
node_interfaces/test_node_waitables.cpp)
|
||||
if(TARGET test_node_interfaces__node_waitables)
|
||||
@@ -649,6 +654,13 @@ if(TARGET test_wait_for_message)
|
||||
target_link_libraries(test_wait_for_message ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_logger_service test_logger_service.cpp)
|
||||
if(TARGET test_logger_service)
|
||||
ament_target_dependencies(test_logger_service
|
||||
"rcl_interfaces")
|
||||
target_link_libraries(test_logger_service ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_interface_traits test_interface_traits.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_interface_traits)
|
||||
|
||||
@@ -43,11 +43,6 @@ public:
|
||||
|
||||
TEST_F(TestEventsExecutor, run_pub_sub)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
bool msg_received = false;
|
||||
@@ -65,7 +60,7 @@ TEST_F(TestEventsExecutor, run_pub_sub)
|
||||
executor.add_node(node);
|
||||
|
||||
bool spin_exited = false;
|
||||
std::thread spinner([&spin_exited, &executor, this]() {
|
||||
std::thread spinner([&spin_exited, &executor]() {
|
||||
executor.spin();
|
||||
spin_exited = true;
|
||||
});
|
||||
@@ -80,8 +75,6 @@ TEST_F(TestEventsExecutor, run_pub_sub)
|
||||
!spin_exited &&
|
||||
(std::chrono::high_resolution_clock::now() - start < 1s))
|
||||
{
|
||||
auto time = std::chrono::high_resolution_clock::now() - start;
|
||||
auto time_msec = std::chrono::duration_cast<std::chrono::milliseconds>(time);
|
||||
std::this_thread::sleep_for(25ms);
|
||||
}
|
||||
|
||||
@@ -95,11 +88,6 @@ TEST_F(TestEventsExecutor, run_pub_sub)
|
||||
|
||||
TEST_F(TestEventsExecutor, run_clients_servers)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
bool request_received = false;
|
||||
@@ -119,7 +107,7 @@ TEST_F(TestEventsExecutor, run_clients_servers)
|
||||
executor.add_node(node);
|
||||
|
||||
bool spin_exited = false;
|
||||
std::thread spinner([&spin_exited, &executor, this]() {
|
||||
std::thread spinner([&spin_exited, &executor]() {
|
||||
executor.spin();
|
||||
spin_exited = true;
|
||||
});
|
||||
@@ -153,11 +141,6 @@ TEST_F(TestEventsExecutor, run_clients_servers)
|
||||
|
||||
TEST_F(TestEventsExecutor, spin_once_max_duration_timeout)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
EventsExecutor executor;
|
||||
@@ -190,11 +173,6 @@ TEST_F(TestEventsExecutor, spin_once_max_duration_timeout)
|
||||
|
||||
TEST_F(TestEventsExecutor, spin_once_max_duration_timer)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
EventsExecutor executor;
|
||||
@@ -226,11 +204,6 @@ TEST_F(TestEventsExecutor, spin_once_max_duration_timer)
|
||||
|
||||
TEST_F(TestEventsExecutor, spin_some_max_duration)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
@@ -277,11 +250,6 @@ TEST_F(TestEventsExecutor, spin_some_max_duration)
|
||||
|
||||
TEST_F(TestEventsExecutor, spin_some_zero_duration)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
size_t t_runs = 0;
|
||||
@@ -303,11 +271,6 @@ TEST_F(TestEventsExecutor, spin_some_zero_duration)
|
||||
|
||||
TEST_F(TestEventsExecutor, spin_all_max_duration)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
@@ -358,11 +321,6 @@ TEST_F(TestEventsExecutor, spin_all_max_duration)
|
||||
|
||||
TEST_F(TestEventsExecutor, cancel_while_timers_running)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
EventsExecutor executor;
|
||||
@@ -388,7 +346,7 @@ TEST_F(TestEventsExecutor, cancel_while_timers_running)
|
||||
});
|
||||
|
||||
|
||||
std::thread spinner([&executor, this]() {executor.spin();});
|
||||
std::thread spinner([&executor]() {executor.spin();});
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
// Call cancel while t1 callback is still being executed
|
||||
@@ -402,11 +360,6 @@ TEST_F(TestEventsExecutor, cancel_while_timers_running)
|
||||
|
||||
TEST_F(TestEventsExecutor, cancel_while_timers_waiting)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
|
||||
size_t t1_runs = 0;
|
||||
@@ -420,7 +373,7 @@ TEST_F(TestEventsExecutor, cancel_while_timers_waiting)
|
||||
executor.add_node(node);
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
std::thread spinner([&executor, this]() {executor.spin();});
|
||||
std::thread spinner([&executor]() {executor.spin();});
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
executor.cancel();
|
||||
@@ -435,11 +388,6 @@ TEST_F(TestEventsExecutor, destroy_entities)
|
||||
// This test fails on Windows! We skip it for now
|
||||
GTEST_SKIP();
|
||||
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
// Create a publisher node and start publishing messages
|
||||
auto node_pub = std::make_shared<rclcpp::Node>("node_pub");
|
||||
auto publisher = node_pub->create_publisher<test_msgs::msg::Empty>("topic", rclcpp::QoS(10));
|
||||
@@ -447,7 +395,7 @@ TEST_F(TestEventsExecutor, destroy_entities)
|
||||
2ms, [&]() {publisher->publish(std::make_unique<test_msgs::msg::Empty>());});
|
||||
EventsExecutor executor_pub;
|
||||
executor_pub.add_node(node_pub);
|
||||
std::thread spinner([&executor_pub, this]() {executor_pub.spin();});
|
||||
std::thread spinner([&executor_pub]() {executor_pub.spin();});
|
||||
|
||||
// Create a node with two different subscriptions to the topic
|
||||
auto node_sub = std::make_shared<rclcpp::Node>("node_sub");
|
||||
@@ -485,11 +433,6 @@ std::string * g_sub_log_msg;
|
||||
std::promise<void> * g_log_msgs_promise;
|
||||
TEST_F(TestEventsExecutor, test_default_incompatible_qos_callbacks)
|
||||
{
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
rcutils_logging_output_handler_t original_output_handler = rcutils_logging_get_output_handler();
|
||||
|
||||
|
||||
@@ -20,12 +20,14 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/time.h"
|
||||
@@ -43,18 +45,10 @@ template<typename T>
|
||||
class TestExecutors : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
const auto test_info = ::testing::UnitTest::GetInstance()->current_test_info();
|
||||
std::stringstream test_name;
|
||||
test_name << test_info->test_case_name() << "_" << test_info->name();
|
||||
@@ -75,6 +69,8 @@ public:
|
||||
publisher.reset();
|
||||
subscription.reset();
|
||||
node.reset();
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
@@ -139,14 +135,6 @@ TYPED_TEST_SUITE(TestExecutorsStable, StandardExecutors, ExecutorTypeNames);
|
||||
TYPED_TEST(TestExecutors, detachOnDestruction)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
{
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
@@ -163,14 +151,6 @@ TYPED_TEST(TestExecutors, detachOnDestruction)
|
||||
TYPED_TEST(TestExecutorsStable, addTemporaryNode)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
{
|
||||
@@ -191,14 +171,6 @@ TYPED_TEST(TestExecutorsStable, addTemporaryNode)
|
||||
TYPED_TEST(TestExecutors, emptyExecutor)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
std::thread spinner([&]() {EXPECT_NO_THROW(executor.spin());});
|
||||
std::this_thread::sleep_for(50ms);
|
||||
@@ -210,14 +182,6 @@ TYPED_TEST(TestExecutors, emptyExecutor)
|
||||
TYPED_TEST(TestExecutors, addNodeTwoExecutors)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor1;
|
||||
ExecutorType executor2;
|
||||
EXPECT_NO_THROW(executor1.add_node(this->node));
|
||||
@@ -229,14 +193,6 @@ TYPED_TEST(TestExecutors, addNodeTwoExecutors)
|
||||
TYPED_TEST(TestExecutors, spinWithTimer)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
bool timer_completed = false;
|
||||
@@ -260,14 +216,6 @@ TYPED_TEST(TestExecutors, spinWithTimer)
|
||||
TYPED_TEST(TestExecutors, spinWhileAlreadySpinning)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -295,14 +243,6 @@ TYPED_TEST(TestExecutors, spinWhileAlreadySpinning)
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureComplete)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -326,14 +266,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureComplete)
|
||||
TYPED_TEST(TestExecutors, testSpinUntilSharedFutureComplete)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -358,14 +290,6 @@ TYPED_TEST(TestExecutors, testSpinUntilSharedFutureComplete)
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteNoTimeout)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -413,14 +337,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteNoTimeout)
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteWithTimeout)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -533,14 +449,6 @@ private:
|
||||
TYPED_TEST(TestExecutors, spinAll)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
auto waitable_interfaces = this->node->get_node_waitables_interface();
|
||||
auto my_waitable = std::make_shared<TestWaitable>();
|
||||
@@ -583,14 +491,6 @@ TYPED_TEST(TestExecutors, spinAll)
|
||||
TYPED_TEST(TestExecutors, spinSome)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
auto waitable_interfaces = this->node->get_node_waitables_interface();
|
||||
auto my_waitable = std::make_shared<TestWaitable>();
|
||||
@@ -633,14 +533,6 @@ TYPED_TEST(TestExecutors, spinSome)
|
||||
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodeBasePtr)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
std::promise<bool> promise;
|
||||
@@ -657,14 +549,6 @@ TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodeBasePtr)
|
||||
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodePtr)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
std::promise<bool> promise;
|
||||
@@ -681,14 +565,6 @@ TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodePtr)
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
@@ -729,6 +605,77 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
|
||||
spinner.join();
|
||||
}
|
||||
|
||||
// This test verifies that the add_node operation is robust wrt race conditions.
|
||||
// It's mostly meant to prevent regressions in the events-executor, but the operation should be
|
||||
// thread-safe in all executor implementations.
|
||||
// The initial implementation of the events-executor contained a bug where the executor
|
||||
// would end up in an inconsistent state and stop processing interrupt/shutdown notifications.
|
||||
// Manually adding a node to the executor results in a) producing a notify waitable event
|
||||
// and b) refreshing the executor collections.
|
||||
// The inconsistent state would happen if the event was processed before the collections were
|
||||
// finished to be refreshed: the executor would pick up the event but be unable to process it.
|
||||
// This would leave the `notify_waitable_event_pushed_` flag to true, preventing additional
|
||||
// notify waitable events to be pushed.
|
||||
// The behavior is observable only under heavy load, so this test spawns several worker
|
||||
// threads. Due to the nature of the bug, this test may still succeed even if the
|
||||
// bug is present. However repeated runs will show its flakiness nature and indicate
|
||||
// an eventual regression.
|
||||
TYPED_TEST(TestExecutors, testRaceConditionAddNode)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
// Spawn some threads to do some heavy work
|
||||
std::atomic<bool> should_cancel = false;
|
||||
std::vector<std::thread> stress_threads;
|
||||
for (size_t i = 0; i < 5 * std::thread::hardware_concurrency(); i++) {
|
||||
stress_threads.emplace_back(
|
||||
[&should_cancel, i]() {
|
||||
// This is just some arbitrary heavy work
|
||||
volatile size_t total = 0;
|
||||
for (size_t k = 0; k < 549528914167; k++) {
|
||||
if (should_cancel) {
|
||||
break;
|
||||
}
|
||||
total += k * (i + 42);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Create an executor
|
||||
auto executor = std::make_shared<ExecutorType>();
|
||||
// Start spinning
|
||||
auto executor_thread = std::thread(
|
||||
[executor]() {
|
||||
executor->spin();
|
||||
});
|
||||
// Add a node to the executor
|
||||
executor->add_node(this->node);
|
||||
|
||||
// Cancel the executor (make sure that it's already spinning first)
|
||||
while (!executor->is_spinning() && rclcpp::ok()) {
|
||||
continue;
|
||||
}
|
||||
executor->cancel();
|
||||
|
||||
// Try to join the thread after cancelling the executor
|
||||
// This is the "test". We want to make sure that we can still cancel the executor
|
||||
// regardless of the presence of race conditions
|
||||
executor_thread.join();
|
||||
|
||||
// The test is now completed: we can join the stress threads
|
||||
should_cancel = true;
|
||||
for (auto & t : stress_threads) {
|
||||
t.join();
|
||||
}
|
||||
}
|
||||
|
||||
// Check spin_until_future_complete with node base pointer (instantiates its own executor)
|
||||
TEST(TestExecutors, testSpinUntilFutureCompleteNodeBasePtr)
|
||||
{
|
||||
@@ -791,7 +738,7 @@ public:
|
||||
test_name << test_info->test_case_name() << "_" << test_info->name();
|
||||
node = std::make_shared<rclcpp::Node>("node", test_name.str());
|
||||
|
||||
callback_count = 0;
|
||||
callback_count = 0u;
|
||||
|
||||
const std::string topic_name = std::string("topic_") + test_name.str();
|
||||
|
||||
@@ -800,7 +747,7 @@ public:
|
||||
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(1);
|
||||
this->callback_count.fetch_add(1u);
|
||||
};
|
||||
|
||||
rclcpp::SubscriptionOptions so;
|
||||
@@ -822,7 +769,7 @@ public:
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr publisher;
|
||||
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
|
||||
std::atomic_int callback_count;
|
||||
std::atomic_size_t callback_count;
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE(TestIntraprocessExecutors, ExecutorTypes, ExecutorTypeNames);
|
||||
@@ -832,13 +779,13 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
|
||||
// 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;
|
||||
static constexpr size_t kNumMessages = 100;
|
||||
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
EXPECT_EQ(0, this->callback_count.load());
|
||||
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.
|
||||
@@ -852,7 +799,7 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
|
||||
EXPECT_EQ(1u, this->callback_count.load());
|
||||
|
||||
// reset counter
|
||||
this->callback_count.store(0);
|
||||
this->callback_count.store(0u);
|
||||
|
||||
for (size_t ii = 0; ii < kNumMessages; ++ii) {
|
||||
this->publisher->publish(test_msgs::msg::Empty());
|
||||
@@ -861,11 +808,9 @@ TYPED_TEST(TestIntraprocessExecutors, testIntraprocessRetrigger) {
|
||||
// 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]() {
|
||||
std::chrono::milliseconds(10), [this, &executor, &loops]() {
|
||||
loops++;
|
||||
if (kNumMessages == this->callback_count.load() ||
|
||||
loops == 500)
|
||||
{
|
||||
if (kNumMessages == this->callback_count.load() || loops == 500) {
|
||||
executor.cancel();
|
||||
}
|
||||
});
|
||||
|
||||
@@ -77,9 +77,9 @@ TEST_F(TestNodeParameters, list_parameters)
|
||||
std::vector<std::string> prefixes;
|
||||
const auto list_result = node_parameters->list_parameters(prefixes, 1u);
|
||||
|
||||
// Currently the only default parameter is 'use_sim_time', but that may change.
|
||||
// Currently the default parameters are 'use_sim_time' and 'start_type_description_service'
|
||||
size_t number_of_parameters = list_result.names.size();
|
||||
EXPECT_GE(1u, number_of_parameters);
|
||||
EXPECT_GE(2u, number_of_parameters);
|
||||
|
||||
const std::string parameter_name = "new_parameter";
|
||||
const rclcpp::ParameterValue value(true);
|
||||
|
||||
@@ -92,7 +92,7 @@ TEST_F(TestNodeService, add_service)
|
||||
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node_services->add_service(service, callback_group_in_different_node),
|
||||
std::runtime_error("Cannot create service, group not in node."));
|
||||
rclcpp::exceptions::MissingGroupNodeException("service"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeService, add_service_rcl_trigger_guard_condition_error)
|
||||
@@ -119,7 +119,7 @@ TEST_F(TestNodeService, add_client)
|
||||
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node_services->add_client(client, callback_group_in_different_node),
|
||||
std::runtime_error("Cannot create client, group not in node."));
|
||||
rclcpp::exceptions::MissingGroupNodeException("client"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeService, add_client_rcl_trigger_guard_condition_error)
|
||||
|
||||
@@ -75,7 +75,7 @@ TEST_F(TestNodeTimers, add_timer)
|
||||
different_node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node_timers->add_timer(timer, callback_group_in_different_node),
|
||||
std::runtime_error("Cannot create timer, group not in node."));
|
||||
rclcpp::exceptions::MissingGroupNodeException("timer"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeTimers, add_timer_rcl_trigger_guard_condition_error)
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
// Copyright 2023 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/node_interfaces/node_type_descriptions.hpp"
|
||||
|
||||
class TestNodeTypeDescriptions : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestNodeTypeDescriptions, interface_created)
|
||||
{
|
||||
rclcpp::Node node{"node", "ns"};
|
||||
ASSERT_NE(nullptr, node.get_node_type_descriptions_interface());
|
||||
}
|
||||
|
||||
TEST_F(TestNodeTypeDescriptions, disabled_no_service)
|
||||
{
|
||||
rclcpp::NodeOptions node_options;
|
||||
node_options.append_parameter_override("start_type_description_service", false);
|
||||
rclcpp::Node node{"node", "ns", node_options};
|
||||
|
||||
rcl_node_t * rcl_node = node.get_node_base_interface()->get_rcl_node_handle();
|
||||
rcl_service_t * srv = nullptr;
|
||||
rcl_ret_t ret = rcl_node_get_type_description_service(rcl_node, &srv);
|
||||
ASSERT_EQ(RCL_RET_NOT_INIT, ret);
|
||||
}
|
||||
|
||||
TEST_F(TestNodeTypeDescriptions, enabled_creates_service)
|
||||
{
|
||||
rclcpp::NodeOptions node_options;
|
||||
node_options.append_parameter_override("start_type_description_service", true);
|
||||
rclcpp::Node node{"node", "ns", node_options};
|
||||
|
||||
rcl_node_t * rcl_node = node.get_node_base_interface()->get_rcl_node_handle();
|
||||
rcl_service_t * srv = nullptr;
|
||||
rcl_ret_t ret = rcl_node_get_type_description_service(rcl_node, &srv);
|
||||
ASSERT_EQ(RCL_RET_OK, ret);
|
||||
ASSERT_NE(nullptr, srv);
|
||||
}
|
||||
@@ -78,7 +78,7 @@ TEST_F(TestNodeWaitables, add_remove_waitable)
|
||||
node_waitables->add_waitable(waitable, callback_group1));
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node_waitables->add_waitable(waitable, callback_group2),
|
||||
std::runtime_error("Cannot create waitable, group not in node."));
|
||||
rclcpp::exceptions::MissingGroupNodeException("waitable"));
|
||||
EXPECT_NO_THROW(node_waitables->remove_waitable(waitable, callback_group1));
|
||||
EXPECT_NO_THROW(node_waitables->remove_waitable(waitable, callback_group2));
|
||||
|
||||
|
||||
@@ -99,13 +99,6 @@ TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutorStable, StandardExecutors, Execu
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto timer_callback = []() {};
|
||||
@@ -155,13 +148,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups)
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto timer_callback = []() {};
|
||||
@@ -193,13 +179,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups)
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_duplicate_callback_groups)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
@@ -220,13 +199,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_duplicate_callback_groups)
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups_after_add_node_to_executor)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
@@ -263,13 +235,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups_after_add_node_t
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, add_unallowable_callback_groups)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
@@ -307,13 +272,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_unallowable_callback_groups)
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_executors)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType timer_executor;
|
||||
ExecutorType sub_executor;
|
||||
@@ -355,13 +313,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_e
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutorStable, subscriber_triggered_to_receive_message)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
@@ -428,13 +379,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, subscriber_triggered_to_receiv
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutorStable, callback_group_create_after_spin)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
@@ -481,13 +425,6 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, callback_group_create_after_sp
|
||||
TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_group)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
@@ -193,3 +193,29 @@ TEST(TestCreateTimer, timer_function_pointer)
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestCreateTimer, timer_without_autostart)
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
auto node = std::make_shared<rclcpp::Node>("test_create_timer_node");
|
||||
|
||||
rclcpp::TimerBase::SharedPtr timer;
|
||||
timer = rclcpp::create_timer(
|
||||
node,
|
||||
node->get_clock(),
|
||||
rclcpp::Duration(0ms),
|
||||
[]() {},
|
||||
nullptr,
|
||||
false);
|
||||
|
||||
EXPECT_TRUE(timer->is_canceled());
|
||||
EXPECT_EQ(timer->time_until_trigger().count(), std::chrono::nanoseconds::max().count());
|
||||
|
||||
timer->reset();
|
||||
EXPECT_LE(timer->time_until_trigger().count(), std::chrono::nanoseconds::max().count());
|
||||
EXPECT_FALSE(timer->is_canceled());
|
||||
|
||||
timer->cancel();
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
@@ -546,6 +546,40 @@ TEST_F(TestExecutor, spin_node_once_node) {
|
||||
EXPECT_TRUE(spin_called);
|
||||
}
|
||||
|
||||
TEST_F(TestExecutor, spin_node_all_base_interface) {
|
||||
DummyExecutor dummy;
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
bool spin_called = false;
|
||||
auto timer =
|
||||
node->create_wall_timer(
|
||||
std::chrono::milliseconds(1), [&]() {
|
||||
spin_called = true;
|
||||
});
|
||||
|
||||
// Wait for the wall timer to have expired.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
EXPECT_FALSE(spin_called);
|
||||
dummy.spin_node_all(node->get_node_base_interface(), std::chrono::milliseconds(50));
|
||||
EXPECT_TRUE(spin_called);
|
||||
}
|
||||
|
||||
TEST_F(TestExecutor, spin_node_all_node) {
|
||||
DummyExecutor dummy;
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
bool spin_called = false;
|
||||
auto timer =
|
||||
node->create_wall_timer(
|
||||
std::chrono::milliseconds(1), [&]() {
|
||||
spin_called = true;
|
||||
});
|
||||
|
||||
// Wait for the wall timer to have expired.
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||
EXPECT_FALSE(spin_called);
|
||||
dummy.spin_node_all(node, std::chrono::milliseconds(50));
|
||||
EXPECT_TRUE(spin_called);
|
||||
}
|
||||
|
||||
TEST_F(TestExecutor, spin_until_future_complete_future_already_complete) {
|
||||
DummyExecutor dummy;
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
|
||||
@@ -238,3 +238,75 @@ TEST(TestIntraProcessBuffer, unique_buffer_consume) {
|
||||
EXPECT_EQ(original_value, *popped_unique_msg);
|
||||
EXPECT_EQ(original_message_pointer, popped_message_pointer);
|
||||
}
|
||||
|
||||
/*
|
||||
Check the available buffer capacity while storing and consuming data from an intra-process
|
||||
buffer.
|
||||
The initial available buffer capacity should equal the buffer size.
|
||||
Inserting a message should decrease the available buffer capacity by 1.
|
||||
Consuming a message should increase the available buffer capacity by 1.
|
||||
*/
|
||||
TEST(TestIntraProcessBuffer, available_capacity) {
|
||||
using MessageT = char;
|
||||
using Alloc = std::allocator<void>;
|
||||
using Deleter = std::default_delete<MessageT>;
|
||||
using SharedMessageT = std::shared_ptr<const MessageT>;
|
||||
using UniqueMessageT = std::unique_ptr<MessageT, Deleter>;
|
||||
using UniqueIntraProcessBufferT = rclcpp::experimental::buffers::TypedIntraProcessBuffer<
|
||||
MessageT, Alloc, Deleter, UniqueMessageT>;
|
||||
|
||||
constexpr auto history_depth = 5u;
|
||||
|
||||
auto buffer_impl =
|
||||
std::make_unique<rclcpp::experimental::buffers::RingBufferImplementation<UniqueMessageT>>(
|
||||
history_depth);
|
||||
|
||||
UniqueIntraProcessBufferT intra_process_buffer(std::move(buffer_impl));
|
||||
|
||||
EXPECT_EQ(history_depth, intra_process_buffer.available_capacity());
|
||||
|
||||
auto original_unique_msg = std::make_unique<char>('a');
|
||||
auto original_message_pointer = reinterpret_cast<std::uintptr_t>(original_unique_msg.get());
|
||||
auto original_value = *original_unique_msg;
|
||||
|
||||
intra_process_buffer.add_unique(std::move(original_unique_msg));
|
||||
|
||||
EXPECT_EQ(history_depth - 1u, intra_process_buffer.available_capacity());
|
||||
|
||||
SharedMessageT popped_shared_msg;
|
||||
popped_shared_msg = intra_process_buffer.consume_shared();
|
||||
auto popped_message_pointer = reinterpret_cast<std::uintptr_t>(popped_shared_msg.get());
|
||||
|
||||
EXPECT_EQ(history_depth, intra_process_buffer.available_capacity());
|
||||
EXPECT_EQ(original_value, *popped_shared_msg);
|
||||
EXPECT_EQ(original_message_pointer, popped_message_pointer);
|
||||
|
||||
original_unique_msg = std::make_unique<char>('b');
|
||||
original_message_pointer = reinterpret_cast<std::uintptr_t>(original_unique_msg.get());
|
||||
original_value = *original_unique_msg;
|
||||
|
||||
intra_process_buffer.add_unique(std::move(original_unique_msg));
|
||||
|
||||
auto second_unique_msg = std::make_unique<char>('c');
|
||||
auto second_message_pointer = reinterpret_cast<std::uintptr_t>(second_unique_msg.get());
|
||||
auto second_value = *second_unique_msg;
|
||||
|
||||
intra_process_buffer.add_unique(std::move(second_unique_msg));
|
||||
|
||||
EXPECT_EQ(history_depth - 2u, intra_process_buffer.available_capacity());
|
||||
|
||||
UniqueMessageT popped_unique_msg;
|
||||
popped_unique_msg = intra_process_buffer.consume_unique();
|
||||
popped_message_pointer = reinterpret_cast<std::uintptr_t>(popped_unique_msg.get());
|
||||
|
||||
EXPECT_EQ(history_depth - 1u, intra_process_buffer.available_capacity());
|
||||
EXPECT_EQ(original_value, *popped_unique_msg);
|
||||
EXPECT_EQ(original_message_pointer, popped_message_pointer);
|
||||
|
||||
popped_unique_msg = intra_process_buffer.consume_unique();
|
||||
popped_message_pointer = reinterpret_cast<std::uintptr_t>(popped_unique_msg.get());
|
||||
|
||||
EXPECT_EQ(history_depth, intra_process_buffer.available_capacity());
|
||||
EXPECT_EQ(second_value, *popped_unique_msg);
|
||||
EXPECT_EQ(second_message_pointer, popped_message_pointer);
|
||||
}
|
||||
|
||||
@@ -156,18 +156,26 @@ public:
|
||||
{
|
||||
message_ptr = reinterpret_cast<std::uintptr_t>(msg.get());
|
||||
shared_msg = msg;
|
||||
++num_msgs;
|
||||
}
|
||||
|
||||
void add(MessageUniquePtr msg)
|
||||
{
|
||||
message_ptr = reinterpret_cast<std::uintptr_t>(msg.get());
|
||||
unique_msg = std::move(msg);
|
||||
++num_msgs;
|
||||
}
|
||||
|
||||
void pop(std::uintptr_t & msg_ptr)
|
||||
{
|
||||
msg_ptr = message_ptr;
|
||||
message_ptr = 0;
|
||||
--num_msgs;
|
||||
}
|
||||
|
||||
size_t size() const
|
||||
{
|
||||
return num_msgs;
|
||||
}
|
||||
|
||||
// need to store the messages somewhere otherwise the memory address will be reused
|
||||
@@ -175,6 +183,8 @@ public:
|
||||
MessageUniquePtr unique_msg;
|
||||
|
||||
std::uintptr_t message_ptr;
|
||||
// count add and pop
|
||||
size_t num_msgs = 0u;
|
||||
};
|
||||
|
||||
} // namespace mock
|
||||
@@ -221,6 +231,10 @@ public:
|
||||
return topic_name.c_str();
|
||||
}
|
||||
|
||||
virtual
|
||||
size_t
|
||||
available_capacity() const = 0;
|
||||
|
||||
rclcpp::QoS qos_profile;
|
||||
std::string topic_name;
|
||||
};
|
||||
@@ -280,6 +294,12 @@ public:
|
||||
return take_shared_method;
|
||||
}
|
||||
|
||||
size_t
|
||||
available_capacity() const override
|
||||
{
|
||||
return qos_profile.depth() - buffer->size();
|
||||
}
|
||||
|
||||
bool take_shared_method;
|
||||
|
||||
typename rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>::UniquePtr buffer;
|
||||
@@ -712,3 +732,91 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
EXPECT_EQ(original_message_pointer, received_message_pointer_10);
|
||||
EXPECT_NE(original_message_pointer, received_message_pointer_11);
|
||||
}
|
||||
|
||||
/*
|
||||
This tests the method "lowest_available_capacity":
|
||||
- Creates 1 publisher.
|
||||
- The available buffer capacity should be at least history size.
|
||||
- Add 2 subscribers.
|
||||
- Add everything to the intra-process manager.
|
||||
- All the entities are expected to have different ids.
|
||||
- Check the subscriptions count for the publisher.
|
||||
- The available buffer capacity should be the history size.
|
||||
- Publish one message (without receiving it).
|
||||
- The available buffer capacity should decrease by 1.
|
||||
- Publish another message (without receiving it).
|
||||
- The available buffer capacity should decrease by 1.
|
||||
- One subscriber receives one message.
|
||||
- The available buffer capacity should stay the same,
|
||||
as the other subscriber still has not freed its buffer.
|
||||
- The other subscriber receives one message.
|
||||
- The available buffer capacity should increase by 1.
|
||||
- One subscription goes out of scope.
|
||||
- The available buffer capacity should not change.
|
||||
*/
|
||||
TEST(TestIntraProcessManager, lowest_available_capacity) {
|
||||
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
|
||||
using MessageT = rcl_interfaces::msg::Log;
|
||||
using PublisherT = rclcpp::mock::Publisher<MessageT>;
|
||||
using SubscriptionIntraProcessT = rclcpp::experimental::mock::SubscriptionIntraProcess<MessageT>;
|
||||
|
||||
constexpr auto history_depth = 10u;
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).best_effort());
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
|
||||
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
p1->set_intra_process_manager(p1_id, ipm);
|
||||
|
||||
auto c1 = ipm->lowest_available_capacity(p1_id);
|
||||
|
||||
ASSERT_LE(0u, c1);
|
||||
|
||||
auto s1_id = ipm->add_subscription(s1);
|
||||
auto s2_id = ipm->add_subscription(s2);
|
||||
|
||||
bool unique_ids = s1_id != s2_id && p1_id != s1_id;
|
||||
ASSERT_TRUE(unique_ids);
|
||||
|
||||
size_t p1_subs = ipm->get_subscription_count(p1_id);
|
||||
size_t non_existing_pub_subs = ipm->get_subscription_count(42);
|
||||
ASSERT_EQ(2u, p1_subs);
|
||||
ASSERT_EQ(0u, non_existing_pub_subs);
|
||||
|
||||
c1 = ipm->lowest_available_capacity(p1_id);
|
||||
auto non_existing_pub_c = ipm->lowest_available_capacity(42);
|
||||
|
||||
ASSERT_EQ(history_depth, c1);
|
||||
ASSERT_EQ(0u, non_existing_pub_c);
|
||||
|
||||
auto unique_msg = std::make_unique<MessageT>();
|
||||
p1->publish(std::move(unique_msg));
|
||||
|
||||
c1 = ipm->lowest_available_capacity(p1_id);
|
||||
ASSERT_EQ(history_depth - 1u, c1);
|
||||
|
||||
unique_msg = std::make_unique<MessageT>();
|
||||
p1->publish(std::move(unique_msg));
|
||||
|
||||
c1 = ipm->lowest_available_capacity(p1_id);
|
||||
ASSERT_EQ(history_depth - 2u, c1);
|
||||
|
||||
s1->pop();
|
||||
|
||||
c1 = ipm->lowest_available_capacity(p1_id);
|
||||
ASSERT_EQ(history_depth - 2u, c1);
|
||||
|
||||
s2->pop();
|
||||
|
||||
c1 = ipm->lowest_available_capacity(p1_id);
|
||||
ASSERT_EQ(history_depth - 1u, c1);
|
||||
|
||||
ipm->get_subscription_intra_process(s1_id).reset();
|
||||
|
||||
c1 = ipm->lowest_available_capacity(p1_id);
|
||||
ASSERT_EQ(history_depth - 1u, c1);
|
||||
}
|
||||
|
||||
214
rclcpp/test/rclcpp/test_logger_service.cpp
Normal file
214
rclcpp/test/rclcpp/test_logger_service.cpp
Normal file
@@ -0,0 +1,214 @@
|
||||
// Copyright 2023 Sony Group Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rcl_interfaces/srv/get_logger_levels.hpp"
|
||||
#include "rcl_interfaces/srv/set_logger_levels.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class TestLoggerService : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
rclcpp::NodeOptions options = rclcpp::NodeOptions();
|
||||
options.enable_logger_service(true);
|
||||
node_ = std::make_shared<rclcpp::Node>("test_logger_service", "/test", options);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node_;
|
||||
std::thread thread_;
|
||||
};
|
||||
|
||||
TEST_F(TestLoggerService, check_connect_get_logger_service) {
|
||||
auto client = node_->create_client<rcl_interfaces::srv::GetLoggerLevels>(
|
||||
"/test/test_logger_service/get_logger_levels");
|
||||
ASSERT_TRUE(client->wait_for_service(2s));
|
||||
}
|
||||
|
||||
TEST_F(TestLoggerService, check_connect_set_logger_service) {
|
||||
auto client = node_->create_client<rcl_interfaces::srv::SetLoggerLevels>(
|
||||
"/test/test_logger_service/set_logger_levels");
|
||||
ASSERT_TRUE(client->wait_for_service(2s));
|
||||
}
|
||||
|
||||
TEST_F(TestLoggerService, test_set_and_get_one_logging_level) {
|
||||
std::string test_logger_name = "rcl";
|
||||
uint8_t test_logger_level = 20;
|
||||
{
|
||||
auto client = node_->create_client<rcl_interfaces::srv::SetLoggerLevels>(
|
||||
"/test/test_logger_service/set_logger_levels");
|
||||
ASSERT_TRUE(client->wait_for_service(1s));
|
||||
auto request = std::make_shared<rcl_interfaces::srv::SetLoggerLevels::Request>();
|
||||
auto level = rcl_interfaces::msg::LoggerLevel();
|
||||
level.name = test_logger_name;
|
||||
level.level = test_logger_level;
|
||||
request->levels.push_back(level);
|
||||
auto result = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
rclcpp::spin_until_future_complete(node_, result),
|
||||
rclcpp::FutureReturnCode::SUCCESS);
|
||||
auto result_get = result.get();
|
||||
ASSERT_EQ(result_get->results.size(), 1u);
|
||||
ASSERT_TRUE(result_get->results[0].successful);
|
||||
ASSERT_STREQ(result_get->results[0].reason.c_str(), "");
|
||||
}
|
||||
|
||||
{
|
||||
auto client = node_->create_client<rcl_interfaces::srv::GetLoggerLevels>(
|
||||
"/test/test_logger_service/get_logger_levels");
|
||||
ASSERT_TRUE(client->wait_for_service(1s));
|
||||
auto request = std::make_shared<rcl_interfaces::srv::GetLoggerLevels::Request>();
|
||||
request->names.emplace_back(test_logger_name);
|
||||
auto result = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
rclcpp::spin_until_future_complete(node_, result),
|
||||
rclcpp::FutureReturnCode::SUCCESS);
|
||||
auto result_get = result.get();
|
||||
ASSERT_EQ(result_get->levels.size(), 1u);
|
||||
ASSERT_STREQ(result_get->levels[0].name.c_str(), test_logger_name.c_str());
|
||||
ASSERT_EQ(result_get->levels[0].level, test_logger_level);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestLoggerService, test_set_and_get_multi_logging_level) {
|
||||
std::vector<std::pair<std::string, uint8_t>> test_data {
|
||||
{"rcl", 30},
|
||||
{"rclcpp", 40},
|
||||
{"/test/test_logger_service", 50}
|
||||
};
|
||||
|
||||
// Set multi log levels
|
||||
{
|
||||
auto client = node_->create_client<rcl_interfaces::srv::SetLoggerLevels>(
|
||||
"/test/test_logger_service/set_logger_levels");
|
||||
ASSERT_TRUE(client->wait_for_service(1s));
|
||||
auto request = std::make_shared<rcl_interfaces::srv::SetLoggerLevels::Request>();
|
||||
for (auto & set_level : test_data) {
|
||||
auto level = rcl_interfaces::msg::LoggerLevel();
|
||||
level.name = std::get<0>(set_level);
|
||||
level.level = std::get<1>(set_level);
|
||||
request->levels.push_back(level);
|
||||
}
|
||||
auto result = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
rclcpp::spin_until_future_complete(node_, result),
|
||||
rclcpp::FutureReturnCode::SUCCESS);
|
||||
auto result_get = result.get();
|
||||
ASSERT_EQ(result_get->results.size(), test_data.size());
|
||||
for (uint32_t i = 0; i < test_data.size(); i++) {
|
||||
ASSERT_TRUE(result_get->results[0].successful);
|
||||
}
|
||||
}
|
||||
|
||||
// Get multi log levels
|
||||
{
|
||||
auto client = node_->create_client<rcl_interfaces::srv::GetLoggerLevels>(
|
||||
"/test/test_logger_service/get_logger_levels");
|
||||
ASSERT_TRUE(client->wait_for_service(1s));
|
||||
auto request = std::make_shared<rcl_interfaces::srv::GetLoggerLevels::Request>();
|
||||
for (auto & set_level : test_data) {
|
||||
request->names.emplace_back(std::get<0>(set_level));
|
||||
}
|
||||
auto result = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
rclcpp::spin_until_future_complete(node_, result),
|
||||
rclcpp::FutureReturnCode::SUCCESS);
|
||||
auto result_get = result.get();
|
||||
ASSERT_EQ(result_get->levels.size(), test_data.size());
|
||||
for (uint32_t i = 0; i < test_data.size(); i++) {
|
||||
ASSERT_STREQ(result_get->levels[i].name.c_str(), std::get<0>(test_data[i]).c_str());
|
||||
ASSERT_EQ(result_get->levels[i].level, std::get<1>(test_data[i]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestLoggerService, test_set_logging_level_with_invalid_param) {
|
||||
std::vector<std::pair<std::string, uint8_t>> test_data {
|
||||
{"rcl", 12},
|
||||
{"/test/test_logger_service", 22}
|
||||
};
|
||||
|
||||
// Set multi log levels
|
||||
{
|
||||
auto client = node_->create_client<rcl_interfaces::srv::SetLoggerLevels>(
|
||||
"/test/test_logger_service/set_logger_levels");
|
||||
ASSERT_TRUE(client->wait_for_service(1s));
|
||||
auto request = std::make_shared<rcl_interfaces::srv::SetLoggerLevels::Request>();
|
||||
for (auto & set_level : test_data) {
|
||||
auto level = rcl_interfaces::msg::LoggerLevel();
|
||||
level.name = std::get<0>(set_level);
|
||||
level.level = std::get<1>(set_level);
|
||||
request->levels.push_back(level);
|
||||
}
|
||||
auto result = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
rclcpp::spin_until_future_complete(node_, result),
|
||||
rclcpp::FutureReturnCode::SUCCESS);
|
||||
auto result_get = result.get();
|
||||
ASSERT_EQ(result_get->results.size(), test_data.size());
|
||||
for (uint32_t i = 0; i < test_data.size(); i++) {
|
||||
ASSERT_FALSE(result_get->results[i].successful);
|
||||
// Check string starts with prefix
|
||||
ASSERT_EQ(
|
||||
result_get->results[i].reason.rfind("Unable to determine severity_string for severity", 0),
|
||||
0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestLoggerService, test_set_logging_level_with_partial_invalid_param) {
|
||||
std::vector<std::pair<std::string, uint8_t>> test_data {
|
||||
{"rcl", 20},
|
||||
{"rclcpp", 22},
|
||||
{"/test/test_logger_service", 30}
|
||||
};
|
||||
|
||||
// Set multi log levels
|
||||
{
|
||||
auto client = node_->create_client<rcl_interfaces::srv::SetLoggerLevels>(
|
||||
"/test/test_logger_service/set_logger_levels");
|
||||
ASSERT_TRUE(client->wait_for_service(1s));
|
||||
auto request = std::make_shared<rcl_interfaces::srv::SetLoggerLevels::Request>();
|
||||
for (auto & set_level : test_data) {
|
||||
auto level = rcl_interfaces::msg::LoggerLevel();
|
||||
level.name = std::get<0>(set_level);
|
||||
level.level = std::get<1>(set_level);
|
||||
request->levels.push_back(level);
|
||||
}
|
||||
auto result = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
rclcpp::spin_until_future_complete(node_, result),
|
||||
rclcpp::FutureReturnCode::SUCCESS);
|
||||
auto result_get = result.get();
|
||||
ASSERT_EQ(result_get->results.size(), test_data.size());
|
||||
ASSERT_TRUE(result_get->results[0].successful);
|
||||
ASSERT_FALSE(result_get->results[1].successful);
|
||||
ASSERT_TRUE(result_get->results[2].successful);
|
||||
}
|
||||
}
|
||||
@@ -78,6 +78,7 @@ TEST_F(TestNode, construction_and_destruction) {
|
||||
EXPECT_NE(nullptr, node->get_node_options().get_rcl_node_options());
|
||||
EXPECT_NE(nullptr, node->get_graph_event());
|
||||
EXPECT_NE(nullptr, node->get_clock());
|
||||
EXPECT_NE(nullptr, node->get_node_type_descriptions_interface());
|
||||
}
|
||||
|
||||
{
|
||||
|
||||
@@ -266,6 +266,11 @@ TEST(TestNodeOptions, bool_setters_and_getters) {
|
||||
EXPECT_FALSE(options.automatically_declare_parameters_from_overrides());
|
||||
options.automatically_declare_parameters_from_overrides(true);
|
||||
EXPECT_TRUE(options.automatically_declare_parameters_from_overrides());
|
||||
|
||||
options.enable_logger_service(false);
|
||||
EXPECT_FALSE(options.enable_logger_service());
|
||||
options.enable_logger_service(true);
|
||||
EXPECT_TRUE(options.enable_logger_service());
|
||||
}
|
||||
|
||||
TEST(TestNodeOptions, parameter_event_qos) {
|
||||
|
||||
@@ -59,6 +59,8 @@ protected:
|
||||
node_with_option.reset();
|
||||
}
|
||||
|
||||
// "start_type_description_service" and "use_sim_time"
|
||||
const uint64_t builtin_param_count = 2;
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::Node::SharedPtr node_with_option;
|
||||
};
|
||||
@@ -925,6 +927,7 @@ TEST_F(TestParameterClient, sync_parameter_delete_parameters) {
|
||||
Coverage for async load_parameters
|
||||
*/
|
||||
TEST_F(TestParameterClient, async_parameter_load_parameters) {
|
||||
const uint64_t expected_param_count = 4 + builtin_param_count;
|
||||
auto load_node = std::make_shared<rclcpp::Node>(
|
||||
"load_node",
|
||||
"namespace",
|
||||
@@ -944,12 +947,13 @@ TEST_F(TestParameterClient, async_parameter_load_parameters) {
|
||||
auto list_parameters = asynchronous_client->list_parameters({}, 3);
|
||||
rclcpp::spin_until_future_complete(
|
||||
load_node, list_parameters, std::chrono::milliseconds(100));
|
||||
ASSERT_EQ(list_parameters.get().names.size(), static_cast<uint64_t>(5));
|
||||
ASSERT_EQ(list_parameters.get().names.size(), expected_param_count);
|
||||
}
|
||||
/*
|
||||
Coverage for sync load_parameters
|
||||
*/
|
||||
TEST_F(TestParameterClient, sync_parameter_load_parameters) {
|
||||
const uint64_t expected_param_count = 4 + builtin_param_count;
|
||||
auto load_node = std::make_shared<rclcpp::Node>(
|
||||
"load_node",
|
||||
"namespace",
|
||||
@@ -964,13 +968,14 @@ TEST_F(TestParameterClient, sync_parameter_load_parameters) {
|
||||
ASSERT_EQ(load_future[0].successful, true);
|
||||
// list parameters
|
||||
auto list_parameters = synchronous_client->list_parameters({}, 3);
|
||||
ASSERT_EQ(list_parameters.names.size(), static_cast<uint64_t>(5));
|
||||
ASSERT_EQ(list_parameters.names.size(), static_cast<uint64_t>(expected_param_count));
|
||||
}
|
||||
|
||||
/*
|
||||
Coverage for async load_parameters with complicated regex expression
|
||||
*/
|
||||
TEST_F(TestParameterClient, async_parameter_load_parameters_complicated_regex) {
|
||||
const uint64_t expected_param_count = 5 + builtin_param_count;
|
||||
auto load_node = std::make_shared<rclcpp::Node>(
|
||||
"load_node",
|
||||
"namespace",
|
||||
@@ -990,7 +995,7 @@ TEST_F(TestParameterClient, async_parameter_load_parameters_complicated_regex) {
|
||||
auto list_parameters = asynchronous_client->list_parameters({}, 3);
|
||||
rclcpp::spin_until_future_complete(
|
||||
load_node, list_parameters, std::chrono::milliseconds(100));
|
||||
ASSERT_EQ(list_parameters.get().names.size(), static_cast<uint64_t>(6));
|
||||
ASSERT_EQ(list_parameters.get().names.size(), expected_param_count);
|
||||
// to check the parameter "a_value"
|
||||
std::string param_name = "a_value";
|
||||
auto param = load_node->get_parameter(param_name);
|
||||
@@ -1020,6 +1025,7 @@ TEST_F(TestParameterClient, async_parameter_load_no_valid_parameter) {
|
||||
Coverage for async load_parameters from maps with complicated regex expression
|
||||
*/
|
||||
TEST_F(TestParameterClient, async_parameter_load_parameters_from_map) {
|
||||
const uint64_t expected_param_count = 5 + builtin_param_count;
|
||||
auto load_node = std::make_shared<rclcpp::Node>(
|
||||
"load_node",
|
||||
"namespace",
|
||||
@@ -1068,7 +1074,7 @@ TEST_F(TestParameterClient, async_parameter_load_parameters_from_map) {
|
||||
auto list_parameters = asynchronous_client->list_parameters({}, 3);
|
||||
rclcpp::spin_until_future_complete(
|
||||
load_node, list_parameters, std::chrono::milliseconds(100));
|
||||
ASSERT_EQ(list_parameters.get().names.size(), static_cast<uint64_t>(6));
|
||||
ASSERT_EQ(list_parameters.get().names.size(), expected_param_count);
|
||||
// to check the parameter "a_value"
|
||||
std::string param_name = "a_value";
|
||||
auto param = load_node->get_parameter(param_name);
|
||||
|
||||
@@ -629,6 +629,41 @@ TEST_P(TestPublisherWaitForAllAcked, check_wait_for_all_acked_with_QosPolicy) {
|
||||
EXPECT_TRUE(pub->wait_for_all_acked(std::chrono::milliseconds(6000)));
|
||||
}
|
||||
|
||||
TEST_F(TestPublisher, lowest_available_ipm_capacity) {
|
||||
constexpr auto history_depth = 10u;
|
||||
|
||||
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||
|
||||
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options_ipm_disabled;
|
||||
options_ipm_disabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
|
||||
|
||||
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> options_ipm_enabled;
|
||||
options_ipm_enabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
|
||||
|
||||
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Strings>) {};
|
||||
auto pub_ipm_disabled = node->create_publisher<test_msgs::msg::Strings>(
|
||||
"topic", history_depth,
|
||||
options_ipm_disabled);
|
||||
auto pub_ipm_enabled = node->create_publisher<test_msgs::msg::Strings>(
|
||||
"topic", history_depth,
|
||||
options_ipm_enabled);
|
||||
auto sub = node->create_subscription<test_msgs::msg::Strings>(
|
||||
"topic",
|
||||
history_depth,
|
||||
do_nothing);
|
||||
|
||||
ASSERT_EQ(1, pub_ipm_enabled->get_intra_process_subscription_count());
|
||||
ASSERT_EQ(0, pub_ipm_disabled->lowest_available_ipm_capacity());
|
||||
ASSERT_EQ(history_depth, pub_ipm_enabled->lowest_available_ipm_capacity());
|
||||
|
||||
auto msg = std::make_shared<test_msgs::msg::Strings>();
|
||||
ASSERT_NO_THROW(pub_ipm_disabled->publish(*msg));
|
||||
ASSERT_NO_THROW(pub_ipm_enabled->publish(*msg));
|
||||
|
||||
ASSERT_EQ(0, pub_ipm_disabled->lowest_available_ipm_capacity());
|
||||
ASSERT_EQ(history_depth - 1u, pub_ipm_enabled->lowest_available_ipm_capacity());
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TestWaitForAllAckedWithParm,
|
||||
TestPublisherWaitForAllAcked,
|
||||
|
||||
@@ -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.
|
||||
*/
|
||||
|
||||
@@ -314,11 +314,6 @@ TEST_F(TestQosEvent, add_to_wait_set) {
|
||||
|
||||
TEST_F(TestQosEvent, test_on_new_event_callback)
|
||||
{
|
||||
// rmw_connextdds doesn't support rmw_event_set_callback() interface
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto offered_deadline = rclcpp::Duration(std::chrono::milliseconds(1));
|
||||
auto requested_deadline = rclcpp::Duration(std::chrono::milliseconds(2));
|
||||
|
||||
@@ -360,11 +355,6 @@ TEST_F(TestQosEvent, test_on_new_event_callback)
|
||||
|
||||
TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
|
||||
{
|
||||
// rmw_connextdds doesn't support rmw_event_set_callback() interface
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto pub = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
|
||||
auto dummy_cb = [](size_t count_events) {(void)count_events;};
|
||||
@@ -439,11 +429,6 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
|
||||
|
||||
TEST_F(TestQosEvent, test_pub_matched_event_by_set_event_callback)
|
||||
{
|
||||
// rmw_connextdds doesn't support rmw_event_set_callback() interface
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
std::atomic_size_t matched_count = 0;
|
||||
|
||||
rclcpp::PublisherOptions pub_options;
|
||||
@@ -451,8 +436,10 @@ TEST_F(TestQosEvent, test_pub_matched_event_by_set_event_callback)
|
||||
auto pub = node->create_publisher<test_msgs::msg::Empty>(
|
||||
topic_name, 10, pub_options);
|
||||
|
||||
auto matched_event_callback = [&matched_count](size_t count) {
|
||||
std::promise<void> prom;
|
||||
auto matched_event_callback = [&matched_count, &prom](size_t count) {
|
||||
matched_count += count;
|
||||
prom.set_value();
|
||||
};
|
||||
|
||||
pub->set_on_new_qos_event_callback(matched_event_callback, RCL_PUBLISHER_MATCHED);
|
||||
@@ -460,34 +447,32 @@ TEST_F(TestQosEvent, test_pub_matched_event_by_set_event_callback)
|
||||
rclcpp::executors::SingleThreadedExecutor ex;
|
||||
ex.add_node(node->get_node_base_interface());
|
||||
|
||||
const auto timeout = std::chrono::milliseconds(200);
|
||||
const auto timeout = std::chrono::seconds(10);
|
||||
|
||||
{
|
||||
auto sub1 = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
prom = {};
|
||||
EXPECT_EQ(matched_count, static_cast<size_t>(1));
|
||||
|
||||
{
|
||||
auto sub2 = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback);
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
prom = {};
|
||||
EXPECT_EQ(matched_count, static_cast<size_t>(2));
|
||||
}
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
prom = {};
|
||||
EXPECT_EQ(matched_count, static_cast<size_t>(3));
|
||||
}
|
||||
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
EXPECT_EQ(matched_count, static_cast<size_t>(4));
|
||||
}
|
||||
|
||||
TEST_F(TestQosEvent, test_sub_matched_event_by_set_event_callback)
|
||||
{
|
||||
// rmw_connextdds doesn't support rmw_event_set_callback() interface
|
||||
if (std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
std::atomic_size_t matched_count = 0;
|
||||
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
@@ -495,8 +480,10 @@ TEST_F(TestQosEvent, test_sub_matched_event_by_set_event_callback)
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, sub_options);
|
||||
|
||||
auto matched_event_callback = [&matched_count](size_t count) {
|
||||
std::promise<void> prom;
|
||||
auto matched_event_callback = [&matched_count, &prom](size_t count) {
|
||||
matched_count += count;
|
||||
prom.set_value();
|
||||
};
|
||||
|
||||
sub->set_on_new_qos_event_callback(matched_event_callback, RCL_SUBSCRIPTION_MATCHED);
|
||||
@@ -504,39 +491,44 @@ TEST_F(TestQosEvent, test_sub_matched_event_by_set_event_callback)
|
||||
rclcpp::executors::SingleThreadedExecutor ex;
|
||||
ex.add_node(node->get_node_base_interface());
|
||||
|
||||
const auto timeout = std::chrono::milliseconds(200);
|
||||
const auto timeout = std::chrono::seconds(10000);
|
||||
|
||||
{
|
||||
auto pub1 = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
|
||||
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
prom = {};
|
||||
EXPECT_EQ(matched_count, static_cast<size_t>(1));
|
||||
|
||||
{
|
||||
auto pub2 = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
prom = {};
|
||||
EXPECT_EQ(matched_count, static_cast<size_t>(2));
|
||||
}
|
||||
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
prom = {};
|
||||
EXPECT_EQ(matched_count, static_cast<size_t>(3));
|
||||
}
|
||||
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
EXPECT_EQ(matched_count, static_cast<size_t>(4));
|
||||
}
|
||||
|
||||
TEST_F(TestQosEvent, test_pub_matched_event_by_option_event_callback)
|
||||
{
|
||||
rmw_matched_status_t matched_expected_result;
|
||||
std::promise<void> prom;
|
||||
|
||||
rclcpp::PublisherOptions pub_options;
|
||||
pub_options.event_callbacks.matched_callback =
|
||||
[&matched_expected_result](rmw_matched_status_t & s) {
|
||||
[&matched_expected_result, &prom](rmw_matched_status_t & s) {
|
||||
EXPECT_EQ(s.total_count, matched_expected_result.total_count);
|
||||
EXPECT_EQ(s.total_count_change, matched_expected_result.total_count_change);
|
||||
EXPECT_EQ(s.current_count, matched_expected_result.current_count);
|
||||
EXPECT_EQ(s.current_count_change, matched_expected_result.current_count_change);
|
||||
prom.set_value();
|
||||
};
|
||||
|
||||
auto pub = node->create_publisher<test_msgs::msg::Empty>(
|
||||
@@ -551,11 +543,12 @@ TEST_F(TestQosEvent, test_pub_matched_event_by_option_event_callback)
|
||||
matched_expected_result.current_count = 1;
|
||||
matched_expected_result.current_count_change = 1;
|
||||
|
||||
const auto timeout = std::chrono::milliseconds(200);
|
||||
const auto timeout = std::chrono::seconds(10);
|
||||
|
||||
{
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
prom = {};
|
||||
|
||||
// destroy a connected subscription
|
||||
matched_expected_result.total_count = 1;
|
||||
@@ -563,20 +556,22 @@ TEST_F(TestQosEvent, test_pub_matched_event_by_option_event_callback)
|
||||
matched_expected_result.current_count = 0;
|
||||
matched_expected_result.current_count_change = -1;
|
||||
}
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
}
|
||||
|
||||
TEST_F(TestQosEvent, test_sub_matched_event_by_option_event_callback)
|
||||
{
|
||||
rmw_matched_status_t matched_expected_result;
|
||||
|
||||
std::promise<void> prom;
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
sub_options.event_callbacks.matched_callback =
|
||||
[&matched_expected_result](rmw_matched_status_t & s) {
|
||||
[&matched_expected_result, &prom](rmw_matched_status_t & s) {
|
||||
EXPECT_EQ(s.total_count, matched_expected_result.total_count);
|
||||
EXPECT_EQ(s.total_count_change, matched_expected_result.total_count_change);
|
||||
EXPECT_EQ(s.current_count, matched_expected_result.current_count);
|
||||
EXPECT_EQ(s.current_count_change, matched_expected_result.current_count_change);
|
||||
prom.set_value();
|
||||
};
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, sub_options);
|
||||
@@ -590,10 +585,11 @@ TEST_F(TestQosEvent, test_sub_matched_event_by_option_event_callback)
|
||||
matched_expected_result.current_count = 1;
|
||||
matched_expected_result.current_count_change = 1;
|
||||
|
||||
const auto timeout = std::chrono::milliseconds(200);
|
||||
const auto timeout = std::chrono::seconds(10);
|
||||
{
|
||||
auto pub1 = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
prom = {};
|
||||
|
||||
// destroy a connected publisher
|
||||
matched_expected_result.total_count = 1;
|
||||
@@ -601,5 +597,5 @@ TEST_F(TestQosEvent, test_sub_matched_event_by_option_event_callback)
|
||||
matched_expected_result.current_count = 0;
|
||||
matched_expected_result.current_count_change = -1;
|
||||
}
|
||||
ex.spin_some(timeout);
|
||||
ex.spin_until_future_complete(prom.get_future(), timeout);
|
||||
}
|
||||
|
||||
@@ -14,14 +14,19 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/rate.hpp"
|
||||
|
||||
#include "../utils/rclcpp_gtest_macros.hpp"
|
||||
|
||||
/*
|
||||
Basic tests for the Rate and WallRate classes.
|
||||
Basic tests for the Rate, WallRate and ROSRate classes.
|
||||
*/
|
||||
TEST(TestRate, rate_basics) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
auto period = std::chrono::milliseconds(1000);
|
||||
auto offset = std::chrono::milliseconds(500);
|
||||
auto epsilon = std::chrono::milliseconds(100);
|
||||
@@ -29,8 +34,23 @@ TEST(TestRate, rate_basics) {
|
||||
|
||||
auto start = std::chrono::system_clock::now();
|
||||
rclcpp::Rate r(period);
|
||||
EXPECT_EQ(period, r.period());
|
||||
EXPECT_EQ(rclcpp::Duration(period), r.period());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_FALSE(r.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
ASSERT_EQ(RCL_SYSTEM_TIME, r.get_type());
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto one = std::chrono::system_clock::now();
|
||||
auto delta = one - start;
|
||||
@@ -59,9 +79,13 @@ TEST(TestRate, rate_basics) {
|
||||
auto five = std::chrono::system_clock::now();
|
||||
delta = five - four;
|
||||
ASSERT_TRUE(epsilon > delta);
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestRate, wall_rate_basics) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
auto period = std::chrono::milliseconds(100);
|
||||
auto offset = std::chrono::milliseconds(50);
|
||||
auto epsilon = std::chrono::milliseconds(1);
|
||||
@@ -69,8 +93,25 @@ TEST(TestRate, wall_rate_basics) {
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
rclcpp::WallRate r(period);
|
||||
EXPECT_EQ(period, r.period());
|
||||
EXPECT_EQ(rclcpp::Duration(period), r.period());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_TRUE(r.is_steady());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_EQ(RCL_STEADY_TIME, r.get_type());
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto one = std::chrono::steady_clock::now();
|
||||
auto delta = one - start;
|
||||
@@ -99,23 +140,288 @@ TEST(TestRate, wall_rate_basics) {
|
||||
auto five = std::chrono::steady_clock::now();
|
||||
delta = five - four;
|
||||
EXPECT_GT(epsilon, delta);
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestRate, ros_rate_basics) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
auto period = std::chrono::milliseconds(100);
|
||||
auto offset = std::chrono::milliseconds(50);
|
||||
auto epsilon = std::chrono::milliseconds(1);
|
||||
double overrun_ratio = 1.5;
|
||||
|
||||
rclcpp::Clock clock(RCL_ROS_TIME);
|
||||
|
||||
auto start = clock.now();
|
||||
rclcpp::ROSRate r(period);
|
||||
EXPECT_EQ(rclcpp::Duration(period), r.period());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_FALSE(r.is_steady());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_EQ(RCL_ROS_TIME, r.get_type());
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto one = clock.now();
|
||||
auto delta = one - start;
|
||||
EXPECT_LT(rclcpp::Duration(period), delta);
|
||||
EXPECT_GT(rclcpp::Duration(period * overrun_ratio), delta);
|
||||
|
||||
clock.sleep_for(offset);
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto two = clock.now();
|
||||
delta = two - start;
|
||||
EXPECT_LT(rclcpp::Duration(2 * period), delta + epsilon);
|
||||
EXPECT_GT(rclcpp::Duration(2 * period * overrun_ratio), delta);
|
||||
|
||||
clock.sleep_for(offset);
|
||||
auto two_offset = clock.now();
|
||||
r.reset();
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto three = clock.now();
|
||||
delta = three - two_offset;
|
||||
EXPECT_LT(rclcpp::Duration(period), delta);
|
||||
EXPECT_GT(rclcpp::Duration(period * overrun_ratio), delta);
|
||||
|
||||
clock.sleep_for(offset + period);
|
||||
auto four = clock.now();
|
||||
ASSERT_FALSE(r.sleep());
|
||||
auto five = clock.now();
|
||||
delta = five - four;
|
||||
EXPECT_GT(rclcpp::Duration(epsilon), delta);
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
/*
|
||||
Basic test for the deprecated GenericRate class.
|
||||
*/
|
||||
TEST(TestRate, generic_rate) {
|
||||
auto period = std::chrono::milliseconds(100);
|
||||
auto offset = std::chrono::milliseconds(50);
|
||||
auto epsilon = std::chrono::milliseconds(1);
|
||||
double overrun_ratio = 1.5;
|
||||
|
||||
{
|
||||
auto start = std::chrono::system_clock::now();
|
||||
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
rclcpp::GenericRate<std::chrono::system_clock> gr(period);
|
||||
ASSERT_FALSE(gr.is_steady());
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
ASSERT_EQ(gr.get_type(), RCL_SYSTEM_TIME);
|
||||
EXPECT_EQ(period, gr.period());
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto one = std::chrono::system_clock::now();
|
||||
auto delta = one - start;
|
||||
EXPECT_LT(period, delta + epsilon);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto two = std::chrono::system_clock::now();
|
||||
delta = two - start;
|
||||
EXPECT_LT(2 * period, delta);
|
||||
EXPECT_GT(2 * period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
auto two_offset = std::chrono::system_clock::now();
|
||||
gr.reset();
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto three = std::chrono::system_clock::now();
|
||||
delta = three - two_offset;
|
||||
EXPECT_LT(period, delta + epsilon);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset + period);
|
||||
auto four = std::chrono::system_clock::now();
|
||||
ASSERT_FALSE(gr.sleep());
|
||||
auto five = std::chrono::system_clock::now();
|
||||
delta = five - four;
|
||||
ASSERT_TRUE(epsilon > delta);
|
||||
}
|
||||
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
rclcpp::GenericRate<std::chrono::steady_clock> gr(period);
|
||||
ASSERT_TRUE(gr.is_steady());
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
ASSERT_EQ(gr.get_type(), RCL_STEADY_TIME);
|
||||
EXPECT_EQ(period, gr.period());
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto one = std::chrono::steady_clock::now();
|
||||
auto delta = one - start;
|
||||
EXPECT_LT(period, delta);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto two = std::chrono::steady_clock::now();
|
||||
delta = two - start;
|
||||
EXPECT_LT(2 * period, delta + epsilon);
|
||||
EXPECT_GT(2 * period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
auto two_offset = std::chrono::steady_clock::now();
|
||||
gr.reset();
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto three = std::chrono::steady_clock::now();
|
||||
delta = three - two_offset;
|
||||
EXPECT_LT(period, delta);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset + period);
|
||||
auto four = std::chrono::steady_clock::now();
|
||||
ASSERT_FALSE(gr.sleep());
|
||||
auto five = std::chrono::steady_clock::now();
|
||||
delta = five - four;
|
||||
EXPECT_GT(epsilon, delta);
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestRate, from_double) {
|
||||
{
|
||||
rclcpp::WallRate rate(1.0);
|
||||
EXPECT_EQ(std::chrono::seconds(1), rate.period());
|
||||
rclcpp::Rate rate(1.0);
|
||||
EXPECT_EQ(rclcpp::Duration(std::chrono::seconds(1)), rate.period());
|
||||
}
|
||||
{
|
||||
rclcpp::WallRate rate(2.0);
|
||||
EXPECT_EQ(std::chrono::milliseconds(500), rate.period());
|
||||
EXPECT_EQ(rclcpp::Duration(std::chrono::milliseconds(500)), rate.period());
|
||||
}
|
||||
{
|
||||
rclcpp::WallRate rate(0.5);
|
||||
EXPECT_EQ(std::chrono::seconds(2), rate.period());
|
||||
EXPECT_EQ(rclcpp::Duration(std::chrono::seconds(2)), rate.period());
|
||||
}
|
||||
{
|
||||
rclcpp::WallRate rate(4.0);
|
||||
EXPECT_EQ(std::chrono::milliseconds(250), rate.period());
|
||||
rclcpp::ROSRate rate(4.0);
|
||||
EXPECT_EQ(rclcpp::Duration(std::chrono::milliseconds(250)), rate.period());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestRate, clock_types) {
|
||||
{
|
||||
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_SYSTEM_TIME));
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
EXPECT_FALSE(rate.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
EXPECT_EQ(RCL_SYSTEM_TIME, rate.get_type());
|
||||
}
|
||||
{
|
||||
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_STEADY_TIME));
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
EXPECT_TRUE(rate.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
EXPECT_EQ(RCL_STEADY_TIME, rate.get_type());
|
||||
}
|
||||
{
|
||||
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_ROS_TIME));
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
EXPECT_FALSE(rate.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
EXPECT_EQ(RCL_ROS_TIME, rate.get_type());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestRate, incorrect_constuctor) {
|
||||
// Constructor with 0-frequency
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
rclcpp::Rate rate(0.0),
|
||||
std::invalid_argument("rate must be greater than 0"));
|
||||
|
||||
// Constructor with negative frequency
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
rclcpp::Rate rate(-1.0),
|
||||
std::invalid_argument("rate must be greater than 0"));
|
||||
|
||||
// Constructor with 0-duration
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
rclcpp::Rate rate(rclcpp::Duration(0, 0)),
|
||||
std::invalid_argument("period must be greater than 0"));
|
||||
|
||||
// Constructor with negative duration
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
rclcpp::Rate rate(rclcpp::Duration(-1, 0)),
|
||||
std::invalid_argument("period must be greater than 0"));
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild) {
|
||||
// before calling get_child of Logger
|
||||
{
|
||||
RCLCPP_INFO(
|
||||
rclcpp::get_logger(logger_name), this->rosout_msg_data.c_str());
|
||||
rclcpp::get_logger(logger_name), "%s", this->rosout_msg_data.c_str());
|
||||
auto future = received_msg_promise.get_future();
|
||||
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
|
||||
@@ -83,7 +83,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild) {
|
||||
// after calling get_child of Logger
|
||||
// 1. use child_logger directly
|
||||
{
|
||||
RCLCPP_INFO(child_logger, this->rosout_msg_data.c_str());
|
||||
RCLCPP_INFO(child_logger, "%s", this->rosout_msg_data.c_str());
|
||||
auto future = received_msg_promise.get_future();
|
||||
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
|
||||
@@ -93,7 +93,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild) {
|
||||
|
||||
// 2. use rclcpp::get_logger
|
||||
{
|
||||
RCLCPP_INFO(rclcpp::get_logger(logger_name), this->rosout_msg_data.c_str());
|
||||
RCLCPP_INFO(rclcpp::get_logger(logger_name), "%s", this->rosout_msg_data.c_str());
|
||||
auto future = received_msg_promise.get_future();
|
||||
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
|
||||
@@ -104,7 +104,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild) {
|
||||
|
||||
// `child_logger` is end of life, there is no sublogger
|
||||
{
|
||||
RCLCPP_INFO(rclcpp::get_logger(logger_name), this->rosout_msg_data.c_str());
|
||||
RCLCPP_INFO(rclcpp::get_logger(logger_name), "%s", this->rosout_msg_data.c_str());
|
||||
auto future = received_msg_promise.get_future();
|
||||
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
|
||||
@@ -119,7 +119,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_parent_log) {
|
||||
|
||||
rclcpp::Logger logger = this->node->get_logger();
|
||||
ASSERT_EQ(logger.get_name(), logger_name);
|
||||
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
|
||||
RCLCPP_INFO(logger, "%s", this->rosout_msg_data.c_str());
|
||||
auto future = received_msg_promise.get_future();
|
||||
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
|
||||
@@ -133,14 +133,14 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_child_log) {
|
||||
this->rosout_msg_name = logger_name;
|
||||
|
||||
rclcpp::Logger logger = this->node->get_logger();
|
||||
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
|
||||
RCLCPP_INFO(logger, "%s", this->rosout_msg_data.c_str());
|
||||
auto future = received_msg_promise.get_future();
|
||||
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
|
||||
received_msg_promise = {};
|
||||
|
||||
logger = this->node->get_logger().get_child("child1");
|
||||
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
|
||||
RCLCPP_INFO(logger, "%s", this->rosout_msg_data.c_str());
|
||||
future = received_msg_promise.get_future();
|
||||
return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
|
||||
@@ -148,14 +148,14 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_child_log) {
|
||||
received_msg_promise = {};
|
||||
|
||||
logger = this->node->get_logger().get_child("child2");
|
||||
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
|
||||
RCLCPP_INFO(logger, "%s", this->rosout_msg_data.c_str());
|
||||
future = received_msg_promise.get_future();
|
||||
return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::TIMEOUT, return_code);
|
||||
received_msg_promise = {};
|
||||
|
||||
this->rosout_msg_name = "ns.test_rosout_subscription.child2";
|
||||
RCLCPP_INFO(logger, this->rosout_msg_data.c_str());
|
||||
RCLCPP_INFO(logger, "%s", this->rosout_msg_data.c_str());
|
||||
future = received_msg_promise.get_future();
|
||||
return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
|
||||
@@ -171,7 +171,7 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild_hierarchy) {
|
||||
rclcpp::Logger grandchild_logger =
|
||||
this->node->get_logger().get_child("child").get_child("grandchild");
|
||||
ASSERT_EQ(grandchild_logger.get_name(), logger_name);
|
||||
RCLCPP_INFO(grandchild_logger, this->rosout_msg_data.c_str());
|
||||
RCLCPP_INFO(grandchild_logger, "%s", this->rosout_msg_data.c_str());
|
||||
auto future = received_msg_promise.get_future();
|
||||
auto return_code = rclcpp::spin_until_future_complete(this->node, future, 3s);
|
||||
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
|
||||
|
||||
@@ -17,6 +17,8 @@
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
@@ -34,114 +36,28 @@ using namespace std::chrono_literals;
|
||||
class TestSubscription : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
void on_message(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
protected:
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void initialize(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("test_subscription", "/ns", node_options);
|
||||
node_ = std::make_shared<rclcpp::Node>("test_subscription", "/ns", node_options);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
struct TestParameters
|
||||
{
|
||||
TestParameters(rclcpp::QoS qos, std::string description)
|
||||
: qos(qos), description(description) {}
|
||||
rclcpp::QoS qos;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & out, const TestParameters & params)
|
||||
{
|
||||
out << params.description;
|
||||
return out;
|
||||
}
|
||||
|
||||
class TestSubscriptionInvalidIntraprocessQos
|
||||
: public TestSubscription,
|
||||
public ::testing::WithParamInterface<TestParameters>
|
||||
{};
|
||||
|
||||
class TestSubscriptionSub : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("test_subscription", "/ns");
|
||||
subnode = node->create_sub_node("sub_ns");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::Node::SharedPtr subnode;
|
||||
};
|
||||
|
||||
class SubscriptionClassNodeInheritance : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
SubscriptionClassNodeInheritance()
|
||||
: Node("subscription_class_node_inheritance")
|
||||
{
|
||||
}
|
||||
|
||||
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
|
||||
void CreateSubscription()
|
||||
{
|
||||
auto callback = std::bind(
|
||||
&SubscriptionClassNodeInheritance::OnMessage, this, std::placeholders::_1);
|
||||
using test_msgs::msg::Empty;
|
||||
auto sub = this->create_subscription<Empty>("topic", 10, callback);
|
||||
}
|
||||
};
|
||||
|
||||
class SubscriptionClass
|
||||
{
|
||||
public:
|
||||
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
|
||||
void CreateSubscription()
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("test_subscription_member_callback", "/ns");
|
||||
auto callback = std::bind(&SubscriptionClass::OnMessage, this, std::placeholders::_1);
|
||||
using test_msgs::msg::Empty;
|
||||
auto sub = node->create_subscription<Empty>("topic", 10, callback);
|
||||
}
|
||||
rclcpp::Node::SharedPtr node_;
|
||||
};
|
||||
|
||||
/*
|
||||
@@ -155,7 +71,7 @@ TEST_F(TestSubscription, construction_and_destruction) {
|
||||
};
|
||||
{
|
||||
constexpr size_t depth = 10u;
|
||||
auto sub = node->create_subscription<Empty>("topic", depth, callback);
|
||||
auto sub = node_->create_subscription<Empty>("topic", depth, callback);
|
||||
|
||||
EXPECT_NE(nullptr, sub->get_subscription_handle());
|
||||
// Converting to base class was necessary for the compiler to choose the const version of
|
||||
@@ -172,40 +88,7 @@ TEST_F(TestSubscription, construction_and_destruction) {
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto sub = node->create_subscription<Empty>("invalid_topic?", 10, callback);
|
||||
}, rclcpp::exceptions::InvalidTopicNameError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing subscription construction and destruction for subnodes.
|
||||
*/
|
||||
TEST_F(TestSubscriptionSub, construction_and_destruction) {
|
||||
using test_msgs::msg::Empty;
|
||||
auto callback = [](Empty::ConstSharedPtr msg) {
|
||||
(void)msg;
|
||||
};
|
||||
{
|
||||
auto sub = subnode->create_subscription<Empty>("topic", 1, callback);
|
||||
EXPECT_STREQ(sub->get_topic_name(), "/ns/sub_ns/topic");
|
||||
}
|
||||
|
||||
{
|
||||
auto sub = subnode->create_subscription<Empty>("/topic", 1, callback);
|
||||
EXPECT_STREQ(sub->get_topic_name(), "/topic");
|
||||
}
|
||||
|
||||
{
|
||||
auto sub = subnode->create_subscription<Empty>("~/topic", 1, callback);
|
||||
std::string expected_topic_name =
|
||||
std::string(node->get_namespace()) + "/" + node->get_name() + "/topic";
|
||||
EXPECT_STREQ(sub->get_topic_name(), expected_topic_name.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto sub = node->create_subscription<Empty>("invalid_topic?", 1, callback);
|
||||
auto sub = node_->create_subscription<Empty>("invalid_topic?", 10, callback);
|
||||
}, rclcpp::exceptions::InvalidTopicNameError);
|
||||
}
|
||||
}
|
||||
@@ -218,31 +101,31 @@ TEST_F(TestSubscription, various_creation_signatures) {
|
||||
using test_msgs::msg::Empty;
|
||||
auto cb = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
{
|
||||
auto sub = node->create_subscription<Empty>("topic", 1, cb);
|
||||
auto sub = node_->create_subscription<Empty>("topic", 1, cb);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub = node->create_subscription<Empty>("topic", rclcpp::QoS(1), cb);
|
||||
auto sub = node_->create_subscription<Empty>("topic", rclcpp::QoS(1), cb);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub =
|
||||
node->create_subscription<Empty>("topic", rclcpp::QoS(rclcpp::KeepLast(1)), cb);
|
||||
node_->create_subscription<Empty>("topic", rclcpp::QoS(rclcpp::KeepLast(1)), cb);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub =
|
||||
node->create_subscription<Empty>("topic", rclcpp::QoS(rclcpp::KeepAll()), cb);
|
||||
node_->create_subscription<Empty>("topic", rclcpp::QoS(rclcpp::KeepAll()), cb);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub = node->create_subscription<Empty>(
|
||||
auto sub = node_->create_subscription<Empty>(
|
||||
"topic", 42, cb, rclcpp::SubscriptionOptions());
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
auto sub = rclcpp::create_subscription<Empty>(
|
||||
node, "topic", 42, cb, rclcpp::SubscriptionOptions());
|
||||
node_, "topic", 42, cb, rclcpp::SubscriptionOptions());
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
@@ -250,40 +133,78 @@ TEST_F(TestSubscription, various_creation_signatures) {
|
||||
options.allocator = std::make_shared<std::allocator<void>>();
|
||||
EXPECT_NE(nullptr, options.get_allocator());
|
||||
auto sub = rclcpp::create_subscription<Empty>(
|
||||
node, "topic", 42, cb, options);
|
||||
node_, "topic", 42, cb, options);
|
||||
(void)sub;
|
||||
}
|
||||
{
|
||||
rclcpp::SubscriptionOptionsBase options_base;
|
||||
rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void>> options(options_base);
|
||||
auto sub = rclcpp::create_subscription<Empty>(
|
||||
node, "topic", 42, cb, options);
|
||||
node_, "topic", 42, cb, options);
|
||||
(void)sub;
|
||||
}
|
||||
}
|
||||
|
||||
class SubscriptionClass final
|
||||
{
|
||||
public:
|
||||
void custom_create_subscription()
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("test_subscription_member_callback", "/ns");
|
||||
auto callback = std::bind(&SubscriptionClass::on_message, this, std::placeholders::_1);
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
}
|
||||
|
||||
private:
|
||||
void on_message(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
};
|
||||
|
||||
class SubscriptionClassNodeInheritance final : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
SubscriptionClassNodeInheritance()
|
||||
: Node("subscription_class_node_inheritance")
|
||||
{
|
||||
}
|
||||
|
||||
void custom_create_subscription()
|
||||
{
|
||||
auto callback = std::bind(
|
||||
&SubscriptionClassNodeInheritance::on_message, this, std::placeholders::_1);
|
||||
auto sub = this->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
}
|
||||
|
||||
private:
|
||||
void on_message(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
Testing subscriptions using std::bind.
|
||||
*/
|
||||
TEST_F(TestSubscription, callback_bind) {
|
||||
initialize();
|
||||
using test_msgs::msg::Empty;
|
||||
{
|
||||
// Member callback for plain class
|
||||
SubscriptionClass subscription_object;
|
||||
subscription_object.CreateSubscription();
|
||||
subscription_object.custom_create_subscription();
|
||||
}
|
||||
{
|
||||
// Member callback for class inheriting from rclcpp::Node
|
||||
SubscriptionClassNodeInheritance subscription_object;
|
||||
subscription_object.CreateSubscription();
|
||||
subscription_object.custom_create_subscription();
|
||||
}
|
||||
{
|
||||
// Member callback for class inheriting from testing::Test
|
||||
// Regression test for https://github.com/ros2/rclcpp/issues/479 where the TEST_F GTest macro
|
||||
// was interfering with rclcpp's `function_traits`.
|
||||
auto callback = std::bind(&TestSubscription::OnMessage, this, std::placeholders::_1);
|
||||
auto sub = node->create_subscription<Empty>("topic", 1, callback);
|
||||
auto callback = std::bind(&TestSubscription::on_message, this, std::placeholders::_1);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 1, callback);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -292,10 +213,9 @@ TEST_F(TestSubscription, callback_bind) {
|
||||
*/
|
||||
TEST_F(TestSubscription, take) {
|
||||
initialize();
|
||||
using test_msgs::msg::Empty;
|
||||
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {FAIL();};
|
||||
{
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing);
|
||||
test_msgs::msg::Empty msg;
|
||||
rclcpp::MessageInfo msg_info;
|
||||
EXPECT_FALSE(sub->take(msg, msg_info));
|
||||
@@ -303,23 +223,23 @@ TEST_F(TestSubscription, take) {
|
||||
{
|
||||
rclcpp::SubscriptionOptions so;
|
||||
so.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing, so);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing, so);
|
||||
rclcpp::PublisherOptions po;
|
||||
po.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
|
||||
auto pub = node->create_publisher<test_msgs::msg::Empty>("~/test_take", 1, po);
|
||||
auto pub = node_->create_publisher<test_msgs::msg::Empty>("~/test_take", 1, po);
|
||||
{
|
||||
test_msgs::msg::Empty msg;
|
||||
pub->publish(msg);
|
||||
}
|
||||
test_msgs::msg::Empty msg;
|
||||
rclcpp::MessageInfo msg_info;
|
||||
bool message_recieved = false;
|
||||
bool message_received = false;
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
do {
|
||||
message_recieved = sub->take(msg, msg_info);
|
||||
message_received = sub->take(msg, msg_info);
|
||||
std::this_thread::sleep_for(100ms);
|
||||
} while (!message_recieved && std::chrono::steady_clock::now() - start < 10s);
|
||||
EXPECT_TRUE(message_recieved);
|
||||
} while (!message_received && std::chrono::steady_clock::now() - start < 10s);
|
||||
EXPECT_TRUE(message_received);
|
||||
}
|
||||
// TODO(wjwwood): figure out a good way to test the intra-process exclusion behavior.
|
||||
}
|
||||
@@ -329,10 +249,9 @@ TEST_F(TestSubscription, take) {
|
||||
*/
|
||||
TEST_F(TestSubscription, take_serialized) {
|
||||
initialize();
|
||||
using test_msgs::msg::Empty;
|
||||
auto do_nothing = [](std::shared_ptr<const rclcpp::SerializedMessage>) {FAIL();};
|
||||
{
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing);
|
||||
std::shared_ptr<rclcpp::SerializedMessage> msg = sub->create_serialized_message();
|
||||
rclcpp::MessageInfo msg_info;
|
||||
EXPECT_FALSE(sub->take_serialized(*msg, msg_info));
|
||||
@@ -340,23 +259,23 @@ TEST_F(TestSubscription, take_serialized) {
|
||||
{
|
||||
rclcpp::SubscriptionOptions so;
|
||||
so.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing, so);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 1, do_nothing, so);
|
||||
rclcpp::PublisherOptions po;
|
||||
po.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
|
||||
auto pub = node->create_publisher<test_msgs::msg::Empty>("~/test_take", 1, po);
|
||||
auto pub = node_->create_publisher<test_msgs::msg::Empty>("~/test_take", 1, po);
|
||||
{
|
||||
test_msgs::msg::Empty msg;
|
||||
pub->publish(msg);
|
||||
}
|
||||
std::shared_ptr<rclcpp::SerializedMessage> msg = sub->create_serialized_message();
|
||||
rclcpp::MessageInfo msg_info;
|
||||
bool message_recieved = false;
|
||||
bool message_received = false;
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
do {
|
||||
message_recieved = sub->take_serialized(*msg, msg_info);
|
||||
message_received = sub->take_serialized(*msg, msg_info);
|
||||
std::this_thread::sleep_for(100ms);
|
||||
} while (!message_recieved && std::chrono::steady_clock::now() - start < 10s);
|
||||
EXPECT_TRUE(message_recieved);
|
||||
} while (!message_received && std::chrono::steady_clock::now() - start < 10s);
|
||||
EXPECT_TRUE(message_received);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -368,7 +287,7 @@ TEST_F(TestSubscription, rcl_subscription_init_error) {
|
||||
|
||||
// reset() is not needed for triggering exception, just to avoid an unused return value warning
|
||||
EXPECT_THROW(
|
||||
node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback).reset(),
|
||||
node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback).reset(),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
@@ -380,7 +299,7 @@ TEST_F(TestSubscription, rcl_subscription_fini_error) {
|
||||
|
||||
// Cleanup just fails, no exception expected
|
||||
EXPECT_NO_THROW(
|
||||
node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback).reset());
|
||||
node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback).reset());
|
||||
}
|
||||
|
||||
TEST_F(TestSubscription, rcl_subscription_get_actual_qos_error) {
|
||||
@@ -389,7 +308,7 @@ TEST_F(TestSubscription, rcl_subscription_get_actual_qos_error) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_subscription_get_actual_qos, nullptr);
|
||||
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
sub->get_actual_qos(), std::runtime_error("failed to get qos settings: error not set"));
|
||||
}
|
||||
@@ -400,7 +319,7 @@ TEST_F(TestSubscription, rcl_take_type_erased_error) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_take, RCL_RET_ERROR);
|
||||
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
test_msgs::msg::Empty msg;
|
||||
rclcpp::MessageInfo message_info;
|
||||
|
||||
@@ -413,7 +332,7 @@ TEST_F(TestSubscription, rcl_take_serialized_message_error) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_take_serialized_message, RCL_RET_ERROR);
|
||||
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
rclcpp::SerializedMessage msg;
|
||||
rclcpp::MessageInfo message_info;
|
||||
|
||||
@@ -426,14 +345,14 @@ TEST_F(TestSubscription, rcl_subscription_get_publisher_count_error) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_subscription_get_publisher_count, RCL_RET_ERROR);
|
||||
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
EXPECT_THROW(sub->get_publisher_count(), rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
TEST_F(TestSubscription, handle_loaned_message) {
|
||||
initialize();
|
||||
auto callback = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("topic", 10, callback);
|
||||
|
||||
test_msgs::msg::Empty msg;
|
||||
rclcpp::MessageInfo message_info;
|
||||
@@ -448,13 +367,13 @@ TEST_F(TestSubscription, on_new_message_callback) {
|
||||
using test_msgs::msg::Empty;
|
||||
|
||||
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {FAIL();};
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 10, do_nothing);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 10, do_nothing);
|
||||
|
||||
std::atomic<size_t> c1 {0};
|
||||
auto increase_c1_cb = [&c1](size_t count_msgs) {c1 += count_msgs;};
|
||||
sub->set_on_new_message_callback(increase_c1_cb);
|
||||
|
||||
auto pub = node->create_publisher<test_msgs::msg::Empty>("~/test_take", 3);
|
||||
auto pub = node_->create_publisher<test_msgs::msg::Empty>("~/test_take", 3);
|
||||
{
|
||||
test_msgs::msg::Empty msg;
|
||||
pub->publish(msg);
|
||||
@@ -518,13 +437,13 @@ TEST_F(TestSubscription, on_new_intra_process_message_callback) {
|
||||
using test_msgs::msg::Empty;
|
||||
|
||||
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {FAIL();};
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>("~/test_take", 10, do_nothing);
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("~/test_take", 10, do_nothing);
|
||||
|
||||
std::atomic<size_t> c1 {0};
|
||||
auto increase_c1_cb = [&c1](size_t count_msgs) {c1 += count_msgs;};
|
||||
sub->set_on_new_intra_process_message_callback(increase_c1_cb);
|
||||
|
||||
auto pub = node->create_publisher<test_msgs::msg::Empty>("~/test_take", 1);
|
||||
auto pub = node_->create_publisher<test_msgs::msg::Empty>("~/test_take", 1);
|
||||
{
|
||||
test_msgs::msg::Empty msg;
|
||||
pub->publish(msg);
|
||||
@@ -580,80 +499,13 @@ TEST_F(TestSubscription, on_new_intra_process_message_callback) {
|
||||
EXPECT_THROW(sub->set_on_new_intra_process_message_callback(invalid_cb), std::invalid_argument);
|
||||
}
|
||||
|
||||
/*
|
||||
Testing subscription with intraprocess enabled and invalid QoS
|
||||
*/
|
||||
TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws) {
|
||||
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||
rclcpp::QoS qos = GetParam().qos;
|
||||
using test_msgs::msg::Empty;
|
||||
{
|
||||
auto callback = std::bind(
|
||||
&TestSubscriptionInvalidIntraprocessQos::OnMessage,
|
||||
this,
|
||||
std::placeholders::_1);
|
||||
|
||||
ASSERT_THROW(
|
||||
{auto subscription = node->create_subscription<Empty>(
|
||||
"topic",
|
||||
qos,
|
||||
callback);},
|
||||
std::invalid_argument);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing subscription with invalid use_intra_process_comm
|
||||
*/
|
||||
TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws_intraprocess) {
|
||||
rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void>> options;
|
||||
options.use_intra_process_comm = static_cast<rclcpp::IntraProcessSetting>(5);
|
||||
|
||||
initialize();
|
||||
rclcpp::QoS qos = GetParam().qos;
|
||||
auto callback = std::bind(
|
||||
&TestSubscriptionInvalidIntraprocessQos::OnMessage,
|
||||
this,
|
||||
std::placeholders::_1);
|
||||
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
{auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic",
|
||||
qos,
|
||||
callback,
|
||||
options);},
|
||||
std::runtime_error("Unrecognized IntraProcessSetting value"));
|
||||
}
|
||||
|
||||
static std::vector<TestParameters> invalid_qos_profiles()
|
||||
{
|
||||
std::vector<TestParameters> parameters;
|
||||
|
||||
parameters.reserve(3);
|
||||
parameters.push_back(
|
||||
TestParameters(
|
||||
rclcpp::QoS(rclcpp::KeepLast(10)).transient_local(),
|
||||
"transient_local_qos"));
|
||||
parameters.push_back(
|
||||
TestParameters(
|
||||
rclcpp::QoS(rclcpp::KeepAll()),
|
||||
"keep_all_qos"));
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TestSubscriptionThrows, TestSubscriptionInvalidIntraprocessQos,
|
||||
::testing::ValuesIn(invalid_qos_profiles()),
|
||||
::testing::PrintToStringParamName());
|
||||
|
||||
TEST_F(TestSubscription, get_network_flow_endpoints_errors) {
|
||||
initialize();
|
||||
const rclcpp::QoS subscription_qos(1);
|
||||
auto subscription_callback = [](test_msgs::msg::Empty::ConstSharedPtr msg) {
|
||||
(void)msg;
|
||||
};
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
auto subscription = node_->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", subscription_qos, subscription_callback);
|
||||
|
||||
{
|
||||
@@ -680,3 +532,143 @@ TEST_F(TestSubscription, get_network_flow_endpoints_errors) {
|
||||
EXPECT_NO_THROW(subscription->get_network_flow_endpoints());
|
||||
}
|
||||
}
|
||||
|
||||
class TestSubscriptionSub : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node_ = std::make_shared<rclcpp::Node>("test_subscription", "/ns");
|
||||
subnode_ = node_->create_sub_node("sub_ns");
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node_;
|
||||
rclcpp::Node::SharedPtr subnode_;
|
||||
};
|
||||
|
||||
/*
|
||||
Testing subscription construction and destruction for subnodes.
|
||||
*/
|
||||
TEST_F(TestSubscriptionSub, construction_and_destruction) {
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr msg) {
|
||||
(void)msg;
|
||||
};
|
||||
{
|
||||
auto sub = subnode_->create_subscription<test_msgs::msg::Empty>("topic", 1, callback);
|
||||
EXPECT_STREQ(sub->get_topic_name(), "/ns/sub_ns/topic");
|
||||
}
|
||||
|
||||
{
|
||||
auto sub = subnode_->create_subscription<test_msgs::msg::Empty>("/topic", 1, callback);
|
||||
EXPECT_STREQ(sub->get_topic_name(), "/topic");
|
||||
}
|
||||
|
||||
{
|
||||
auto sub = subnode_->create_subscription<test_msgs::msg::Empty>("~/topic", 1, callback);
|
||||
std::string expected_topic_name =
|
||||
std::string(node_->get_namespace()) + "/" + node_->get_name() + "/topic";
|
||||
EXPECT_STREQ(sub->get_topic_name(), expected_topic_name.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto sub = node_->create_subscription<test_msgs::msg::Empty>("invalid_topic?", 1, callback);
|
||||
}, rclcpp::exceptions::InvalidTopicNameError);
|
||||
}
|
||||
}
|
||||
|
||||
struct TestParameters final
|
||||
{
|
||||
TestParameters(rclcpp::QoS qos, std::string description)
|
||||
: qos(qos), description(description) {}
|
||||
rclcpp::QoS qos;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & out, const TestParameters & params)
|
||||
{
|
||||
out << params.description;
|
||||
return out;
|
||||
}
|
||||
|
||||
class TestSubscriptionInvalidIntraprocessQos
|
||||
: public TestSubscription,
|
||||
public ::testing::WithParamInterface<TestParameters>
|
||||
{};
|
||||
|
||||
static std::vector<TestParameters> invalid_qos_profiles()
|
||||
{
|
||||
std::vector<TestParameters> parameters;
|
||||
|
||||
parameters.reserve(3);
|
||||
parameters.push_back(
|
||||
TestParameters(
|
||||
rclcpp::QoS(rclcpp::KeepLast(10)).transient_local(),
|
||||
"transient_local_qos"));
|
||||
parameters.push_back(
|
||||
TestParameters(
|
||||
rclcpp::QoS(rclcpp::KeepAll()),
|
||||
"keep_all_qos"));
|
||||
|
||||
return parameters;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TestSubscriptionThrows, TestSubscriptionInvalidIntraprocessQos,
|
||||
::testing::ValuesIn(invalid_qos_profiles()),
|
||||
::testing::PrintToStringParamName());
|
||||
|
||||
/*
|
||||
Testing subscription with intraprocess enabled and invalid QoS
|
||||
*/
|
||||
TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws) {
|
||||
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||
rclcpp::QoS qos = GetParam().qos;
|
||||
{
|
||||
auto callback = std::bind(
|
||||
&TestSubscriptionInvalidIntraprocessQos::on_message,
|
||||
this,
|
||||
std::placeholders::_1);
|
||||
|
||||
ASSERT_THROW(
|
||||
{auto subscription = node_->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic",
|
||||
qos,
|
||||
callback);},
|
||||
std::invalid_argument);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing subscription with invalid use_intra_process_comm
|
||||
*/
|
||||
TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws_intraprocess) {
|
||||
rclcpp::SubscriptionOptionsWithAllocator<std::allocator<void>> options;
|
||||
options.use_intra_process_comm = static_cast<rclcpp::IntraProcessSetting>(5);
|
||||
|
||||
initialize();
|
||||
rclcpp::QoS qos = GetParam().qos;
|
||||
auto callback = std::bind(
|
||||
&TestSubscriptionInvalidIntraprocessQos::on_message,
|
||||
this,
|
||||
std::placeholders::_1);
|
||||
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
{auto subscription = node_->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic",
|
||||
qos,
|
||||
callback,
|
||||
options);},
|
||||
std::runtime_error("Unrecognized IntraProcessSetting value"));
|
||||
}
|
||||
|
||||
@@ -305,7 +305,7 @@ TEST_F(TestTimeSource, clock) {
|
||||
|
||||
trigger_clock_changes(node, ros_clock, false);
|
||||
|
||||
// Even now that we've recieved a message, ROS time should still not be active since the
|
||||
// Even now that we've received a message, ROS time should still not be active since the
|
||||
// parameter has not been explicitly set.
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
|
||||
|
||||
@@ -73,6 +73,20 @@ protected:
|
||||
EXPECT_FALSE(timer->is_steady());
|
||||
break;
|
||||
}
|
||||
timer_without_autostart = test_node->create_wall_timer(
|
||||
100ms,
|
||||
[this]() -> void
|
||||
{
|
||||
this->has_timer_run.store(true);
|
||||
|
||||
if (this->cancel_timer.load()) {
|
||||
this->timer->cancel();
|
||||
}
|
||||
// prevent any tests running timer from blocking
|
||||
this->executor->cancel();
|
||||
}, nullptr, false);
|
||||
EXPECT_TRUE(timer_without_autostart->is_steady());
|
||||
|
||||
executor->add_node(test_node);
|
||||
// don't start spinning, let the test dictate when
|
||||
}
|
||||
@@ -93,6 +107,7 @@ protected:
|
||||
std::atomic<bool> cancel_timer;
|
||||
rclcpp::Node::SharedPtr test_node;
|
||||
std::shared_ptr<rclcpp::TimerBase> timer;
|
||||
std::shared_ptr<rclcpp::TimerBase> timer_without_autostart;
|
||||
std::shared_ptr<rclcpp::executors::SingleThreadedExecutor> executor;
|
||||
};
|
||||
|
||||
@@ -334,3 +349,18 @@ INSTANTIATE_TEST_SUITE_P(
|
||||
return std::string("unknown");
|
||||
}
|
||||
);
|
||||
|
||||
/// Simple test of a timer without autostart
|
||||
TEST_P(TestTimer, test_timer_without_autostart)
|
||||
{
|
||||
EXPECT_TRUE(timer_without_autostart->is_canceled());
|
||||
EXPECT_EQ(
|
||||
timer_without_autostart->time_until_trigger().count(),
|
||||
std::chrono::nanoseconds::max().count());
|
||||
// Reset to change start timer
|
||||
timer_without_autostart->reset();
|
||||
EXPECT_LE(
|
||||
timer_without_autostart->time_until_trigger().count(),
|
||||
std::chrono::nanoseconds::max().count());
|
||||
EXPECT_FALSE(timer_without_autostart->is_canceled());
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
@@ -65,8 +66,10 @@ TEST_F(TestTimersManager, empty_manager)
|
||||
TEST_F(TestTimersManager, add_run_remove_timer)
|
||||
{
|
||||
size_t t_runs = 0;
|
||||
std::chrono::milliseconds timer_period(10);
|
||||
|
||||
auto t = TimerT::make_shared(
|
||||
10ms,
|
||||
timer_period,
|
||||
[&t_runs]() {
|
||||
t_runs++;
|
||||
},
|
||||
@@ -79,7 +82,7 @@ TEST_F(TestTimersManager, add_run_remove_timer)
|
||||
timers_manager->add_timer(t);
|
||||
|
||||
// Sleep for more 3 times the timer period
|
||||
std::this_thread::sleep_for(30ms);
|
||||
std::this_thread::sleep_for(3 * timer_period);
|
||||
|
||||
// The timer is executed only once, even if we slept 3 times the period
|
||||
execute_all_ready_timers(timers_manager);
|
||||
@@ -191,67 +194,6 @@ TEST_F(TestTimersManager, head_not_ready)
|
||||
EXPECT_EQ(0u, t_runs);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, timers_order)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t1_runs = 0;
|
||||
auto t1 = TimerT::make_shared(
|
||||
10ms,
|
||||
[&t1_runs]() {
|
||||
t1_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t2_runs = 0;
|
||||
auto t2 = TimerT::make_shared(
|
||||
30ms,
|
||||
[&t2_runs]() {
|
||||
t2_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t3_runs = 0;
|
||||
auto t3 = TimerT::make_shared(
|
||||
100ms,
|
||||
[&t3_runs]() {
|
||||
t3_runs++;
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
// Add timers in a random order
|
||||
timers_manager->add_timer(t2);
|
||||
timers_manager->add_timer(t3);
|
||||
timers_manager->add_timer(t1);
|
||||
|
||||
std::this_thread::sleep_for(10ms);
|
||||
execute_all_ready_timers(timers_manager);
|
||||
EXPECT_EQ(1u, t1_runs);
|
||||
EXPECT_EQ(0u, t2_runs);
|
||||
EXPECT_EQ(0u, t3_runs);
|
||||
|
||||
std::this_thread::sleep_for(30ms);
|
||||
execute_all_ready_timers(timers_manager);
|
||||
EXPECT_EQ(2u, t1_runs);
|
||||
EXPECT_EQ(1u, t2_runs);
|
||||
EXPECT_EQ(0u, t3_runs);
|
||||
|
||||
std::this_thread::sleep_for(100ms);
|
||||
execute_all_ready_timers(timers_manager);
|
||||
EXPECT_EQ(3u, t1_runs);
|
||||
EXPECT_EQ(2u, t2_runs);
|
||||
EXPECT_EQ(1u, t3_runs);
|
||||
|
||||
timers_manager->remove_timer(t1);
|
||||
|
||||
std::this_thread::sleep_for(30ms);
|
||||
execute_all_ready_timers(timers_manager);
|
||||
EXPECT_EQ(3u, t1_runs);
|
||||
EXPECT_EQ(3u, t2_runs);
|
||||
EXPECT_EQ(1u, t3_runs);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, start_stop_timers_thread)
|
||||
{
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
@@ -274,7 +216,7 @@ TEST_F(TestTimersManager, timers_thread)
|
||||
auto timers_manager = std::make_shared<TimersManager>(
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t1_runs = 0;
|
||||
int t1_runs = 0;
|
||||
auto t1 = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t1_runs]() {
|
||||
@@ -282,7 +224,7 @@ TEST_F(TestTimersManager, timers_thread)
|
||||
},
|
||||
rclcpp::contexts::get_global_default_context());
|
||||
|
||||
size_t t2_runs = 0;
|
||||
int t2_runs = 0;
|
||||
auto t2 = TimerT::make_shared(
|
||||
1ms,
|
||||
[&t2_runs]() {
|
||||
@@ -296,12 +238,12 @@ TEST_F(TestTimersManager, timers_thread)
|
||||
|
||||
// Run timers thread for a while
|
||||
timers_manager->start();
|
||||
std::this_thread::sleep_for(5ms);
|
||||
std::this_thread::sleep_for(50ms);
|
||||
timers_manager->stop();
|
||||
|
||||
EXPECT_LT(1u, t1_runs);
|
||||
EXPECT_LT(1u, t2_runs);
|
||||
EXPECT_EQ(t1_runs, t2_runs);
|
||||
EXPECT_LE(std::abs(t1_runs - t2_runs), 1);
|
||||
}
|
||||
|
||||
TEST_F(TestTimersManager, destructor)
|
||||
@@ -365,13 +307,13 @@ TEST_F(TestTimersManager, add_remove_while_thread_running)
|
||||
timers_manager->start();
|
||||
|
||||
// After a while remove t1 and add t2
|
||||
std::this_thread::sleep_for(5ms);
|
||||
std::this_thread::sleep_for(50ms);
|
||||
timers_manager->remove_timer(t1);
|
||||
size_t tmp_t1 = t1_runs;
|
||||
timers_manager->add_timer(t2);
|
||||
|
||||
// Wait some more time and then stop
|
||||
std::this_thread::sleep_for(5ms);
|
||||
std::this_thread::sleep_for(50ms);
|
||||
timers_manager->stop();
|
||||
|
||||
// t1 has stopped running
|
||||
|
||||
@@ -3,6 +3,40 @@ Changelog for package rclcpp_action
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
23.0.0 (2023-09-08)
|
||||
-------------------
|
||||
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
|
||||
* fix(ClientGoalHandle): Made mutex recursive to prevent deadlocks (`#2267 <https://github.com/ros2/rclcpp/issues/2267>`_)
|
||||
* Contributors: Christophe Bedard, jmachowinski
|
||||
|
||||
22.2.0 (2023-09-07)
|
||||
-------------------
|
||||
* Correct the position of a comment. (`#2290 <https://github.com/ros2/rclcpp/issues/2290>`_)
|
||||
* Fix a typo in a comment. (`#2283 <https://github.com/ros2/rclcpp/issues/2283>`_)
|
||||
* doc fix: call `canceled` only after goal state is in canceling. (`#2266 <https://github.com/ros2/rclcpp/issues/2266>`_)
|
||||
* Contributors: Chris Lalancette, Jiaqi Li, Tomoya Fujita
|
||||
|
||||
22.1.0 (2023-08-21)
|
||||
-------------------
|
||||
|
||||
22.0.0 (2023-07-11)
|
||||
-------------------
|
||||
|
||||
21.3.0 (2023-06-12)
|
||||
-------------------
|
||||
|
||||
21.2.0 (2023-06-07)
|
||||
-------------------
|
||||
|
||||
21.1.1 (2023-05-11)
|
||||
-------------------
|
||||
|
||||
21.1.0 (2023-04-27)
|
||||
-------------------
|
||||
|
||||
21.0.0 (2023-04-18)
|
||||
-------------------
|
||||
|
||||
20.0.0 (2023-04-13)
|
||||
-------------------
|
||||
* extract the result response before the callback is issued. (`#2132 <https://github.com/ros2/rclcpp/issues/2132>`_)
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
|
||||
Adds action APIs for C++.
|
||||
|
||||
Visit the [rclcpp_action API documentation](http://docs.ros2.org/latest/api/rclcpp_action/) for a complete list of its main components and features. For more information about Actions in ROS 2, see the [design document](http://design.ros2.org/articles/actions.html).
|
||||
The link to the latest rclcpp_action API documentation, which includes a complete list of its main components and features, can be found on the rclcpp_action package info page, at the [ROS Index](https://index.ros.org/p/rclcpp_action/).
|
||||
For more information about Actions in ROS 2, see the [design document](http://design.ros2.org/articles/actions.html).
|
||||
|
||||
## Quality Declaration
|
||||
|
||||
|
||||
@@ -163,7 +163,7 @@ private:
|
||||
ResultCallback result_callback_{nullptr};
|
||||
int8_t status_{GoalStatus::STATUS_ACCEPTED};
|
||||
|
||||
std::mutex handle_mutex_;
|
||||
std::recursive_mutex handle_mutex_;
|
||||
};
|
||||
} // namespace rclcpp_action
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ template<typename ActionT>
|
||||
std::shared_future<typename ClientGoalHandle<ActionT>::WrappedResult>
|
||||
ClientGoalHandle<ActionT>::async_get_result()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
if (!is_result_aware_) {
|
||||
throw exceptions::UnawareGoalHandleError();
|
||||
}
|
||||
@@ -70,7 +70,7 @@ template<typename ActionT>
|
||||
void
|
||||
ClientGoalHandle<ActionT>::set_result(const WrappedResult & wrapped_result)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
status_ = static_cast<int8_t>(wrapped_result.code);
|
||||
result_promise_.set_value(wrapped_result);
|
||||
if (result_callback_) {
|
||||
@@ -82,7 +82,7 @@ template<typename ActionT>
|
||||
void
|
||||
ClientGoalHandle<ActionT>::set_feedback_callback(FeedbackCallback callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
feedback_callback_ = callback;
|
||||
}
|
||||
|
||||
@@ -90,7 +90,7 @@ template<typename ActionT>
|
||||
void
|
||||
ClientGoalHandle<ActionT>::set_result_callback(ResultCallback callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
result_callback_ = callback;
|
||||
}
|
||||
|
||||
@@ -98,7 +98,7 @@ template<typename ActionT>
|
||||
int8_t
|
||||
ClientGoalHandle<ActionT>::get_status()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
return status_;
|
||||
}
|
||||
|
||||
@@ -106,7 +106,7 @@ template<typename ActionT>
|
||||
void
|
||||
ClientGoalHandle<ActionT>::set_status(int8_t status)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
status_ = status;
|
||||
}
|
||||
|
||||
@@ -114,7 +114,7 @@ template<typename ActionT>
|
||||
bool
|
||||
ClientGoalHandle<ActionT>::is_feedback_aware()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
return feedback_callback_ != nullptr;
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ template<typename ActionT>
|
||||
bool
|
||||
ClientGoalHandle<ActionT>::is_result_aware()
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
return is_result_aware_;
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ template<typename ActionT>
|
||||
bool
|
||||
ClientGoalHandle<ActionT>::set_result_awareness(bool awareness)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
bool previous = is_result_aware_;
|
||||
is_result_aware_ = awareness;
|
||||
return previous;
|
||||
@@ -140,7 +140,7 @@ template<typename ActionT>
|
||||
void
|
||||
ClientGoalHandle<ActionT>::invalidate(const exceptions::UnawareGoalHandleError & ex)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
// Guard against multiple calls
|
||||
if (is_invalidated()) {
|
||||
return;
|
||||
@@ -168,7 +168,7 @@ ClientGoalHandle<ActionT>::call_feedback_callback(
|
||||
RCLCPP_ERROR(rclcpp::get_logger("rclcpp_action"), "Sent feedback to wrong goal handle.");
|
||||
return;
|
||||
}
|
||||
std::lock_guard<std::mutex> guard(handle_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
|
||||
if (nullptr == feedback_callback_) {
|
||||
// Normal, some feedback messages may arrive after the goal result.
|
||||
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp_action"), "Received feedback but goal ignores it.");
|
||||
|
||||
@@ -128,7 +128,7 @@ class Server;
|
||||
* accepted.
|
||||
* A `Server` will create an instance and give it to the user in their `handle_accepted` callback.
|
||||
*
|
||||
* Internally, this class is responsible for coverting between the C++ action type and generic
|
||||
* Internally, this class is responsible for converting between the C++ action type and generic
|
||||
* types for `rclcpp_action::ServerGoalHandleBase`.
|
||||
*/
|
||||
template<typename ActionT>
|
||||
@@ -196,7 +196,7 @@ public:
|
||||
|
||||
/// Indicate that a goal has been canceled.
|
||||
/**
|
||||
* Only call this if the goal is executing or pending, but has been canceled.
|
||||
* Only call this if the goal is canceling.
|
||||
* This is a terminal state, no more methods should be called on a goal handle after this is
|
||||
* called.
|
||||
*
|
||||
|
||||
@@ -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>20.0.0</version>
|
||||
<version>23.0.0</version>
|
||||
<description>Adds action APIs for C++.</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -163,7 +163,6 @@ bool
|
||||
ClientBase::wait_for_action_server_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
// make an event to reuse, rather than create a new one each time
|
||||
auto node_ptr = pimpl_->node_graph_.lock();
|
||||
if (!node_ptr) {
|
||||
throw rclcpp::exceptions::InvalidNodeError();
|
||||
@@ -172,6 +171,7 @@ ClientBase::wait_for_action_server_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
if (this->action_server_is_ready()) {
|
||||
return true;
|
||||
}
|
||||
// make an event to reuse, rather than create a new one each time
|
||||
auto event = node_ptr->get_graph_event();
|
||||
if (timeout == std::chrono::nanoseconds(0)) {
|
||||
// check was non-blocking, return immediately
|
||||
|
||||
@@ -852,6 +852,86 @@ TEST_F(TestClientAgainstServer, async_cancel_some_goals_with_callback)
|
||||
EXPECT_EQ(rclcpp_action::GoalStatus::STATUS_CANCELED, goal_handle0->get_status());
|
||||
}
|
||||
|
||||
TEST_F(TestClientAgainstServer, deadlock_in_callbacks)
|
||||
{
|
||||
std::atomic<bool> feedback_callback_called = false;
|
||||
std::atomic<bool> response_callback_called = false;
|
||||
std::atomic<bool> result_callback_called = false;
|
||||
std::atomic<bool> no_deadlock = false;
|
||||
|
||||
std::thread tr = std::thread(
|
||||
[&]() {
|
||||
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
|
||||
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
|
||||
|
||||
ActionGoal goal;
|
||||
|
||||
using GoalHandle = rclcpp_action::ClientGoalHandle<ActionType>;
|
||||
rclcpp_action::Client<ActionType>::SendGoalOptions ops;
|
||||
ops.feedback_callback =
|
||||
[&feedback_callback_called](const GoalHandle::SharedPtr handle,
|
||||
ActionType::Feedback::ConstSharedPtr) {
|
||||
// call functions on the handle that acquire the lock
|
||||
handle->get_status();
|
||||
handle->is_feedback_aware();
|
||||
handle->is_result_aware();
|
||||
|
||||
feedback_callback_called = true;
|
||||
};
|
||||
ops.goal_response_callback = [&response_callback_called](
|
||||
const GoalHandle::SharedPtr & handle) {
|
||||
// call functions on the handle that acquire the lock
|
||||
handle->get_status();
|
||||
handle->is_feedback_aware();
|
||||
handle->is_result_aware();
|
||||
|
||||
response_callback_called = true;
|
||||
};
|
||||
ops.result_callback = [&result_callback_called](
|
||||
const GoalHandle::WrappedResult &) {
|
||||
result_callback_called = true;
|
||||
};
|
||||
|
||||
goal.order = 6;
|
||||
auto future_goal_handle = action_client->async_send_goal(goal, ops);
|
||||
dual_spin_until_future_complete(future_goal_handle);
|
||||
auto goal_handle = future_goal_handle.get();
|
||||
|
||||
ASSERT_TRUE(goal_handle);
|
||||
|
||||
ASSERT_EQ(RCL_RET_OK, rcl_set_ros_time_override(clock.get_clock_handle(), RCL_S_TO_NS(2)));
|
||||
|
||||
auto result_future = action_client->async_get_result(goal_handle);
|
||||
dual_spin_until_future_complete(result_future);
|
||||
|
||||
EXPECT_TRUE(result_future.valid());
|
||||
auto result = result_future.get();
|
||||
|
||||
no_deadlock = true;
|
||||
});
|
||||
|
||||
auto start_time = std::chrono::system_clock::now();
|
||||
|
||||
while (std::chrono::system_clock::now() - start_time < std::chrono::milliseconds(2000) &&
|
||||
!no_deadlock)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
|
||||
if (no_deadlock) {
|
||||
tr.join();
|
||||
} else {
|
||||
// In case of a failure, the thread is assumed to be in a deadlock.
|
||||
// We detach the thread so we don't block further tests.
|
||||
tr.detach();
|
||||
}
|
||||
|
||||
EXPECT_TRUE(no_deadlock);
|
||||
EXPECT_TRUE(response_callback_called);
|
||||
EXPECT_TRUE(result_callback_called);
|
||||
EXPECT_TRUE(feedback_callback_called);
|
||||
}
|
||||
|
||||
TEST_F(TestClientAgainstServer, send_rcl_errors)
|
||||
{
|
||||
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
|
||||
|
||||
@@ -2,6 +2,35 @@
|
||||
Changelog for package rclcpp_components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
23.0.0 (2023-09-08)
|
||||
-------------------
|
||||
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
|
||||
* Contributors: Christophe Bedard
|
||||
|
||||
22.2.0 (2023-09-07)
|
||||
-------------------
|
||||
|
||||
22.1.0 (2023-08-21)
|
||||
-------------------
|
||||
|
||||
22.0.0 (2023-07-11)
|
||||
-------------------
|
||||
|
||||
21.3.0 (2023-06-12)
|
||||
-------------------
|
||||
|
||||
21.2.0 (2023-06-07)
|
||||
-------------------
|
||||
|
||||
21.1.1 (2023-05-11)
|
||||
-------------------
|
||||
|
||||
21.1.0 (2023-04-27)
|
||||
-------------------
|
||||
|
||||
21.0.0 (2023-04-18)
|
||||
-------------------
|
||||
|
||||
20.0.0 (2023-04-13)
|
||||
-------------------
|
||||
* Update all rclcpp packages to C++17. (`#2121 <https://github.com/ros2/rclcpp/issues/2121>`_)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user