Compare commits
42 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
283925677b | ||
|
|
340309d05c | ||
|
|
6adab6eab6 | ||
|
|
910cf32489 | ||
|
|
07f6b642ca | ||
|
|
d0fa844a09 | ||
|
|
260e62d5d6 | ||
|
|
da6c0e7090 | ||
|
|
5a09a4655f | ||
|
|
e9313c3dc5 | ||
|
|
c02a6a3cd3 | ||
|
|
2d208c5df3 | ||
|
|
42fb17ff95 | ||
|
|
2f2232b723 | ||
|
|
2616dfaef9 | ||
|
|
33de648095 | ||
|
|
82e4e72a2e | ||
|
|
7596ed4db0 | ||
|
|
bff6916e8f | ||
|
|
b8df9347a1 | ||
|
|
ec70642c55 | ||
|
|
14aba06922 | ||
|
|
1c2bd84725 | ||
|
|
c4a68b4199 | ||
|
|
085f161230 | ||
|
|
893679e44f | ||
|
|
a62287bf8d | ||
|
|
98ab933a73 | ||
|
|
a9c6521466 | ||
|
|
41fedb7beb | ||
|
|
98fc7fecc2 | ||
|
|
f9f90e0226 | ||
|
|
61fcc766f8 | ||
|
|
091a8bcf86 | ||
|
|
6af478d0ba | ||
|
|
06a4ee01d4 | ||
|
|
7b94f288e5 | ||
|
|
7226725d2f | ||
|
|
1037822a63 | ||
|
|
95adde2a19 | ||
|
|
cd3fd53c8c | ||
|
|
70dfa2e778 |
@@ -12,6 +12,6 @@ Visit the [rclcpp API documentation](http://docs.ros2.org/latest/api/rclcpp/) fo
|
||||
|
||||
### Examples
|
||||
|
||||
The ROS 2 tutorials [Writing a simple publisher and subscriber](https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber/)
|
||||
and [Writing a simple service and client](https://index.ros.org/doc/ros2/Tutorials/Writing-A-Simple-Cpp-Service-And-Client/)
|
||||
The ROS 2 tutorials [Writing a simple publisher and subscriber](https://docs.ros.org/en/rolling/Tutorials/Writing-A-Simple-Cpp-Publisher-And-Subscriber.html).
|
||||
and [Writing a simple service and client](https://docs.ros.org/en/rolling/Tutorials/Writing-A-Simple-Cpp-Service-And-Client.html)
|
||||
contain some examples of rclcpp APIs in use.
|
||||
|
||||
@@ -2,6 +2,55 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
9.2.0 (2021-09-17)
|
||||
------------------
|
||||
* Added thread safe for_each_callback_group method (`#1741 <https://github.com/ros2/rclcpp/issues/1741>`_)
|
||||
* Added a function rclcpp::wait_for_message() convenience function (`#1705 <https://github.com/ros2/rclcpp/issues/1705>`_) (`#1740 <https://github.com/ros2/rclcpp/issues/1740>`_)
|
||||
* Fixed a documentation bug (`#1719 <https://github.com/ros2/rclcpp/issues/1719>`_) (`#1720 <https://github.com/ros2/rclcpp/issues/1720>`_)
|
||||
* Contributors: Aditya Pande, Karsten Knese, mergify[bot]
|
||||
|
||||
9.1.0 (2021-05-20)
|
||||
------------------
|
||||
* Allow declaring uninitialized statically typed parameters. (`#1673 <https://github.com/ros2/rclcpp/issues/1673>`_) (`#1681 <https://github.com/ros2/rclcpp/issues/1681>`_)
|
||||
* [service] Don't use a weak_ptr to avoid leaking. (`#1668 <https://github.com/ros2/rclcpp/issues/1668>`_) (`#1670 <https://github.com/ros2/rclcpp/issues/1670>`_)
|
||||
* Contributors: Jacob Perron, Ivan Santiago Paunovic
|
||||
|
||||
9.0.3 (2021-05-10)
|
||||
------------------
|
||||
* Use OnShutdown callback handle instead of OnShutdown callback (`#1639 <https://github.com/ros2/rclcpp/issues/1639>`_) (`#1650 <https://github.com/ros2/rclcpp/issues/1650>`_)
|
||||
* use dynamic_pointer_cast to detect allocator mismatch in intra process manager (`#1643 <https://github.com/ros2/rclcpp/issues/1643>`_) (`#1644 <https://github.com/ros2/rclcpp/issues/1644>`_)
|
||||
* Increase cppcheck timeout to 500s (`#1634 <https://github.com/ros2/rclcpp/issues/1634>`_)
|
||||
* Clarify node parameters docs (`#1631 <https://github.com/ros2/rclcpp/issues/1631>`_)
|
||||
* Contributors: Barry Xu, Jacob Perron, Michel Hidalgo, Shane Loretz, William Woodall
|
||||
|
||||
9.0.2 (2021-04-14)
|
||||
------------------
|
||||
* Avoid returning loan when none was obtained. (`#1629 <https://github.com/ros2/rclcpp/issues/1629>`_)
|
||||
* Use a different implementation of mutex two priorities (`#1628 <https://github.com/ros2/rclcpp/issues/1628>`_)
|
||||
* Do not test the value of the history policy when testing the get_publishers/subscriptions_info_by_topic() methods (`#1626 <https://github.com/ros2/rclcpp/issues/1626>`_)
|
||||
* Check first parameter type and range before calling the user validation callbacks (`#1627 <https://github.com/ros2/rclcpp/issues/1627>`_)
|
||||
* Contributors: Ivan Santiago Paunovic, Miguel Company
|
||||
|
||||
9.0.1 (2021-04-12)
|
||||
------------------
|
||||
* Restore test exception for Connext (`#1625 <https://github.com/ros2/rclcpp/issues/1625>`_)
|
||||
* Fix race condition in TimeSource clock thread setup (`#1623 <https://github.com/ros2/rclcpp/issues/1623>`_)
|
||||
* Contributors: Andrea Sorbini, Michel Hidalgo
|
||||
|
||||
9.0.0 (2021-04-06)
|
||||
------------------
|
||||
* remove deprecated code which was deprecated in foxy and should be removed in galactic (`#1622 <https://github.com/ros2/rclcpp/issues/1622>`_)
|
||||
* Change index.ros.org -> docs.ros.org. (`#1620 <https://github.com/ros2/rclcpp/issues/1620>`_)
|
||||
* Unique network flows (`#1496 <https://github.com/ros2/rclcpp/issues/1496>`_)
|
||||
* Add spin_some support to the StaticSingleThreadedExecutor (`#1338 <https://github.com/ros2/rclcpp/issues/1338>`_)
|
||||
* Add publishing instrumentation (`#1600 <https://github.com/ros2/rclcpp/issues/1600>`_)
|
||||
* Create load_parameters and delete_parameters methods (`#1596 <https://github.com/ros2/rclcpp/issues/1596>`_)
|
||||
* refactor AnySubscriptionCallback and add/deprecate callback signatures (`#1598 <https://github.com/ros2/rclcpp/issues/1598>`_)
|
||||
* Add generic publisher and generic subscription for serialized messages (`#1452 <https://github.com/ros2/rclcpp/issues/1452>`_)
|
||||
* use context from `node_base\_` for clock executor. (`#1617 <https://github.com/ros2/rclcpp/issues/1617>`_)
|
||||
* updating quality declaration links (re: `ros2/docs.ros2.org#52 <https://github.com/ros2/docs.ros2.org/issues/52>`_) (`#1615 <https://github.com/ros2/rclcpp/issues/1615>`_)
|
||||
* Contributors: Ananya Muddukrishna, BriceRenaudeau, Chris Lalancette, Christophe Bedard, Nikolai Morin, Tomoya Fujita, William Woodall, mauropasse, shonigmann
|
||||
|
||||
8.2.0 (2021-03-31)
|
||||
------------------
|
||||
* Initialize integers in test_parameter_event_handler.cpp to avoid undefined behavior (`#1609 <https://github.com/ros2/rclcpp/issues/1609>`_)
|
||||
|
||||
@@ -5,6 +5,7 @@ project(rclcpp)
|
||||
find_package(Threads REQUIRED)
|
||||
|
||||
find_package(ament_cmake_ros REQUIRED)
|
||||
find_package(ament_index_cpp REQUIRED)
|
||||
find_package(builtin_interfaces REQUIRED)
|
||||
find_package(libstatistics_collector REQUIRED)
|
||||
find_package(rcl REQUIRED)
|
||||
@@ -20,9 +21,10 @@ find_package(rosidl_typesupport_cpp REQUIRED)
|
||||
find_package(statistics_msgs REQUIRED)
|
||||
find_package(tracetools REQUIRED)
|
||||
|
||||
# Default to C++14
|
||||
# TODO(wjwwood): remove this when gtest can build on its own, when using target_compile_features()
|
||||
# Default to C++17
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
set(CMAKE_CXX_STANDARD 17)
|
||||
endif()
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# About -Wno-sign-conversion: With Clang, -Wconversion implies -Wsign-conversion. There are a number of
|
||||
@@ -51,12 +53,14 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/executable_list.cpp
|
||||
src/rclcpp/executor.cpp
|
||||
src/rclcpp/executors.cpp
|
||||
src/rclcpp/expand_topic_or_service_name.cpp
|
||||
src/rclcpp/executors/multi_threaded_executor.cpp
|
||||
src/rclcpp/executors/single_threaded_executor.cpp
|
||||
src/rclcpp/executors/static_executor_entities_collector.cpp
|
||||
src/rclcpp/executors/static_single_threaded_executor.cpp
|
||||
src/rclcpp/expand_topic_or_service_name.cpp
|
||||
src/rclcpp/future_return_code.cpp
|
||||
src/rclcpp/generic_publisher.cpp
|
||||
src/rclcpp/generic_subscription.cpp
|
||||
src/rclcpp/graph_listener.cpp
|
||||
src/rclcpp/guard_condition.cpp
|
||||
src/rclcpp/init_options.cpp
|
||||
@@ -66,8 +70,8 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/memory_strategies.cpp
|
||||
src/rclcpp/memory_strategy.cpp
|
||||
src/rclcpp/message_info.cpp
|
||||
src/rclcpp/network_flow_endpoint.cpp
|
||||
src/rclcpp/node.cpp
|
||||
src/rclcpp/node_options.cpp
|
||||
src/rclcpp/node_interfaces/node_base.cpp
|
||||
src/rclcpp/node_interfaces/node_clock.cpp
|
||||
src/rclcpp/node_interfaces/node_graph.cpp
|
||||
@@ -78,13 +82,14 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/node_interfaces/node_timers.cpp
|
||||
src/rclcpp/node_interfaces/node_topics.cpp
|
||||
src/rclcpp/node_interfaces/node_waitables.cpp
|
||||
src/rclcpp/node_options.cpp
|
||||
src/rclcpp/parameter.cpp
|
||||
src/rclcpp/parameter_value.cpp
|
||||
src/rclcpp/parameter_client.cpp
|
||||
src/rclcpp/parameter_event_handler.cpp
|
||||
src/rclcpp/parameter_events_filter.cpp
|
||||
src/rclcpp/parameter_map.cpp
|
||||
src/rclcpp/parameter_service.cpp
|
||||
src/rclcpp/parameter_value.cpp
|
||||
src/rclcpp/publisher_base.cpp
|
||||
src/rclcpp/qos.cpp
|
||||
src/rclcpp/qos_event.cpp
|
||||
@@ -99,6 +104,7 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/time_source.cpp
|
||||
src/rclcpp/timer.cpp
|
||||
src/rclcpp/type_support.cpp
|
||||
src/rclcpp/typesupport_helpers.cpp
|
||||
src/rclcpp/utilities.cpp
|
||||
src/rclcpp/wait_set_policies/detail/write_preferring_read_write_lock.cpp
|
||||
src/rclcpp/waitable.cpp
|
||||
@@ -170,8 +176,12 @@ foreach(interface_file ${interface_files})
|
||||
include/rclcpp/node_interfaces/get_${interface_name}.hpp)
|
||||
endforeach()
|
||||
|
||||
add_library(${PROJECT_NAME}
|
||||
${${PROJECT_NAME}_SRCS})
|
||||
add_library(${PROJECT_NAME} ${${PROJECT_NAME}_SRCS})
|
||||
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_17)
|
||||
# TODO(wjwwood): address all deprecation warnings and then remove this
|
||||
if(WIN32)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC "_SILENCE_ALL_CXX17_DEPRECATION_WARNINGS")
|
||||
endif()
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
|
||||
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
|
||||
@@ -179,6 +189,7 @@ target_include_directories(${PROJECT_NAME} PUBLIC
|
||||
target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
|
||||
# specific order: dependents before dependencies
|
||||
ament_target_dependencies(${PROJECT_NAME}
|
||||
"ament_index_cpp"
|
||||
"libstatistics_collector"
|
||||
"rcl"
|
||||
"rcl_yaml_param_parser"
|
||||
@@ -209,6 +220,7 @@ ament_export_include_directories(include)
|
||||
ament_export_libraries(${PROJECT_NAME})
|
||||
ament_export_targets(${PROJECT_NAME})
|
||||
|
||||
ament_export_dependencies(ament_index_cpp)
|
||||
ament_export_dependencies(libstatistics_collector)
|
||||
ament_export_dependencies(rcl)
|
||||
ament_export_dependencies(rcpputils)
|
||||
@@ -235,3 +247,8 @@ install(
|
||||
DIRECTORY include/ ${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
DESTINATION include
|
||||
)
|
||||
|
||||
if(TEST cppcheck)
|
||||
# must set the property after ament_package()
|
||||
set_tests_properties(cppcheck PROPERTIES TIMEOUT 500)
|
||||
endif()
|
||||
|
||||
@@ -4,13 +4,13 @@ This document is a declaration of software quality for the `rclcpp` package, bas
|
||||
|
||||
The package `rclcpp` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware.
|
||||
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#package-quality-categories) of the ROS2 developer guide.
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://www.ros.org/reps/rep-2004.html) of the ROS2 developer guide.
|
||||
|
||||
## Version Policy [1]
|
||||
|
||||
### Version Scheme [1.i]
|
||||
|
||||
`rclcpp` uses `semver` according to the recommendation for ROS Core packages in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#versioning).
|
||||
`rclcpp` uses `semver` according to the recommendation for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#versioning).
|
||||
|
||||
### Version Stability [1.ii]
|
||||
|
||||
@@ -39,11 +39,11 @@ Headers under the folder `detail` are not considered part of the public API and
|
||||
|
||||
## Change Control Process [2]
|
||||
|
||||
`rclcpp` follows the recommended guidelines for ROS Core packages in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#change-control-process).
|
||||
`rclcpp` follows the recommended guidelines for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process).
|
||||
|
||||
### Change Requests [2.i]
|
||||
|
||||
All changes will occur through a pull request, check [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#change-control-process) for additional information.
|
||||
All changes will occur through a pull request, check [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process) for additional information.
|
||||
|
||||
### Contributor Origin [2.ii]
|
||||
|
||||
@@ -51,7 +51,7 @@ This package uses DCO as its confirmation of contributor origin policy. More inf
|
||||
|
||||
### Peer Review Policy [2.iii]
|
||||
|
||||
All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#change-control-process) for additional information.
|
||||
All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process) for additional information.
|
||||
|
||||
### Continuous Integration [2.iv]
|
||||
|
||||
@@ -111,7 +111,7 @@ The tests aim to cover both typical usage and corner cases, but are quantified b
|
||||
|
||||
### Coverage [4.iii]
|
||||
|
||||
`rclcpp` follows the recommendations for ROS Core packages in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#code-coverage), and opts to use line coverage instead of branch coverage.
|
||||
`rclcpp` follows the recommendations for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#code-coverage), and opts to use line coverage instead of branch coverage.
|
||||
|
||||
This includes:
|
||||
|
||||
@@ -121,13 +121,13 @@ This includes:
|
||||
|
||||
Changes are required to make a best effort to keep or increase coverage before being accepted, but decreases are allowed if properly justified and accepted by reviewers.
|
||||
|
||||
Current coverage statistics can be viewed [here](https://ci.ros2.org/job/nightly_linux_coverage/lastCompletedBuild/cobertura/src_ros2_rclcpp_rclcpp_src_rclcpp/). A description of how coverage statistics are calculated is summarized in this page ["ROS 2 Onboarding Guide"](https://index.ros.org/doc/ros2/Contributing/ROS-2-On-boarding-Guide/#note-on-coverage-runs).
|
||||
Current coverage statistics can be viewed [here](https://ci.ros2.org/job/nightly_linux_coverage/lastCompletedBuild/cobertura/src_ros2_rclcpp_rclcpp_src_rclcpp/). A description of how coverage statistics are calculated is summarized in this page ["ROS 2 Onboarding Guide"](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#note-on-coverage-runs).
|
||||
|
||||
`rclcpp` has a line coverage `>= 95%`, which is calculated over all directories within `rclcpp` with the exception of the `experimental` directory.
|
||||
|
||||
### Performance [4.iv]
|
||||
|
||||
`rclcpp` follows the recommendations for performance testing of C/C++ code in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#performance), and opts to do performance analysis on each release rather than each change.
|
||||
`rclcpp` follows the recommendations for performance testing of C/C++ code in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#performance), and opts to do performance analysis on each release rather than each change.
|
||||
|
||||
The performance tests of `rclcpp` are located in the [test/benchmark directory](https://github.com/ros2/rclcpp/tree/master/rclcpp/test/benchmark).
|
||||
|
||||
@@ -139,7 +139,7 @@ Changes that introduce regressions in performance must be adequately justified i
|
||||
|
||||
### Linters and Static Analysis [4.v]
|
||||
|
||||
`rclcpp` uses and passes all the ROS2 standard linters and static analysis tools for a C++ package as described in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#linters-and-static-analysis). Passing implies there are no linter/static errors when testing against CI of supported platforms.
|
||||
`rclcpp` uses and passes all the ROS2 standard linters and static analysis tools for a C++ package as described in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#linters-and-static-analysis). Passing implies there are no linter/static errors when testing against CI of supported platforms.
|
||||
|
||||
Currently nightly test results can be seen here:
|
||||
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/testReport/rclcpp/)
|
||||
|
||||
@@ -50,12 +50,6 @@ struct AnyExecutable
|
||||
std::shared_ptr<void> data;
|
||||
};
|
||||
|
||||
namespace executor
|
||||
{
|
||||
|
||||
using AnyExecutable [[deprecated("use rclcpp::AnyExecutable instead")]] = AnyExecutable;
|
||||
|
||||
} // namespace executor
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__ANY_EXECUTABLE_HPP_
|
||||
|
||||
@@ -15,256 +15,394 @@
|
||||
#ifndef RCLCPP__ANY_SUBSCRIPTION_CALLBACK_HPP_
|
||||
#define RCLCPP__ANY_SUBSCRIPTION_CALLBACK_HPP_
|
||||
|
||||
#include <rmw/types.h>
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant> // NOLINT[build/include_order]
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/message_info.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "tracetools/tracetools.h"
|
||||
#include "tracetools/utils.hpp"
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/detail/subscription_callback_type_helper.hpp"
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/message_info.hpp"
|
||||
|
||||
template<class>
|
||||
inline constexpr bool always_false_v = false;
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
template<typename MessageT, typename Alloc>
|
||||
class AnySubscriptionCallback
|
||||
namespace detail
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
|
||||
template<typename MessageT, typename AllocatorT>
|
||||
struct MessageDeleterHelper
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, AllocatorT>;
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
|
||||
};
|
||||
|
||||
using SharedPtrCallback = std::function<void (const std::shared_ptr<MessageT>)>;
|
||||
using SharedPtrWithInfoCallback =
|
||||
std::function<void (const std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)>;
|
||||
using ConstSharedPtrCallback = std::function<void (const std::shared_ptr<const MessageT>)>;
|
||||
using ConstSharedPtrWithInfoCallback =
|
||||
std::function<void (const std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)>;
|
||||
using UniquePtrCallback = std::function<void (MessageUniquePtr)>;
|
||||
template<typename MessageT, typename AllocatorT>
|
||||
struct AnySubscriptionCallbackHelper
|
||||
{
|
||||
using MessageDeleter = typename MessageDeleterHelper<MessageT, AllocatorT>::MessageDeleter;
|
||||
|
||||
using ConstRefCallback =
|
||||
std::function<void (const MessageT &)>;
|
||||
using ConstRefWithInfoCallback =
|
||||
std::function<void (const MessageT &, const rclcpp::MessageInfo &)>;
|
||||
|
||||
using UniquePtrCallback =
|
||||
std::function<void (std::unique_ptr<MessageT, MessageDeleter>)>;
|
||||
using UniquePtrWithInfoCallback =
|
||||
std::function<void (MessageUniquePtr, const rclcpp::MessageInfo &)>;
|
||||
std::function<void (std::unique_ptr<MessageT, MessageDeleter>, const rclcpp::MessageInfo &)>;
|
||||
|
||||
SharedPtrCallback shared_ptr_callback_;
|
||||
SharedPtrWithInfoCallback shared_ptr_with_info_callback_;
|
||||
ConstSharedPtrCallback const_shared_ptr_callback_;
|
||||
ConstSharedPtrWithInfoCallback const_shared_ptr_with_info_callback_;
|
||||
UniquePtrCallback unique_ptr_callback_;
|
||||
UniquePtrWithInfoCallback unique_ptr_with_info_callback_;
|
||||
using SharedConstPtrCallback =
|
||||
std::function<void (std::shared_ptr<const MessageT>)>;
|
||||
using SharedConstPtrWithInfoCallback =
|
||||
std::function<void (std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)>;
|
||||
|
||||
using ConstRefSharedConstPtrCallback =
|
||||
std::function<void (const std::shared_ptr<const MessageT> &)>;
|
||||
using ConstRefSharedConstPtrWithInfoCallback =
|
||||
std::function<void (const std::shared_ptr<const MessageT> &, const rclcpp::MessageInfo &)>;
|
||||
|
||||
// Deprecated signatures:
|
||||
using SharedPtrCallback =
|
||||
std::function<void (std::shared_ptr<MessageT>)>;
|
||||
using SharedPtrWithInfoCallback =
|
||||
std::function<void (std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)>;
|
||||
|
||||
using variant_type = std::variant<
|
||||
ConstRefCallback,
|
||||
ConstRefWithInfoCallback,
|
||||
UniquePtrCallback,
|
||||
UniquePtrWithInfoCallback,
|
||||
SharedConstPtrCallback,
|
||||
SharedConstPtrWithInfoCallback,
|
||||
ConstRefSharedConstPtrCallback,
|
||||
ConstRefSharedConstPtrWithInfoCallback,
|
||||
SharedPtrCallback,
|
||||
SharedPtrWithInfoCallback
|
||||
>;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<
|
||||
typename MessageT,
|
||||
typename AllocatorT = std::allocator<void>
|
||||
>
|
||||
class AnySubscriptionCallback
|
||||
{
|
||||
private:
|
||||
using HelperT = typename rclcpp::detail::AnySubscriptionCallbackHelper<MessageT, AllocatorT>;
|
||||
using MessageDeleterHelper = rclcpp::detail::MessageDeleterHelper<MessageT, AllocatorT>;
|
||||
|
||||
using MessageAllocTraits = typename MessageDeleterHelper::MessageAllocTraits;
|
||||
using MessageAlloc = typename MessageDeleterHelper::MessageAlloc;
|
||||
using MessageDeleter = typename MessageDeleterHelper::MessageDeleter;
|
||||
|
||||
// See AnySubscriptionCallbackHelper for the types of these.
|
||||
using ConstRefCallback = typename HelperT::ConstRefCallback;
|
||||
using ConstRefWithInfoCallback = typename HelperT::ConstRefWithInfoCallback;
|
||||
|
||||
using UniquePtrCallback = typename HelperT::UniquePtrCallback;
|
||||
using UniquePtrWithInfoCallback = typename HelperT::UniquePtrWithInfoCallback;
|
||||
|
||||
using SharedConstPtrCallback = typename HelperT::SharedConstPtrCallback;
|
||||
using SharedConstPtrWithInfoCallback = typename HelperT::SharedConstPtrWithInfoCallback;
|
||||
|
||||
using ConstRefSharedConstPtrCallback =
|
||||
typename HelperT::ConstRefSharedConstPtrCallback;
|
||||
using ConstRefSharedConstPtrWithInfoCallback =
|
||||
typename HelperT::ConstRefSharedConstPtrWithInfoCallback;
|
||||
|
||||
using SharedPtrCallback = typename HelperT::SharedPtrCallback;
|
||||
using SharedPtrWithInfoCallback = typename HelperT::SharedPtrWithInfoCallback;
|
||||
|
||||
public:
|
||||
explicit AnySubscriptionCallback(std::shared_ptr<Alloc> allocator)
|
||||
: shared_ptr_callback_(nullptr), shared_ptr_with_info_callback_(nullptr),
|
||||
const_shared_ptr_callback_(nullptr), const_shared_ptr_with_info_callback_(nullptr),
|
||||
unique_ptr_callback_(nullptr), unique_ptr_with_info_callback_(nullptr)
|
||||
explicit
|
||||
AnySubscriptionCallback(const AllocatorT & allocator = AllocatorT()) // NOLINT[runtime/explicit]
|
||||
{
|
||||
message_allocator_ = allocator;
|
||||
allocator::set_allocator_for_deleter(&message_deleter_, &message_allocator_);
|
||||
}
|
||||
|
||||
[[deprecated("use AnySubscriptionCallback(const AllocatorT & allocator) instead")]]
|
||||
explicit
|
||||
AnySubscriptionCallback(std::shared_ptr<AllocatorT> allocator) // NOLINT[runtime/explicit]
|
||||
{
|
||||
if (allocator == nullptr) {
|
||||
throw std::runtime_error("invalid allocator");
|
||||
}
|
||||
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
|
||||
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
|
||||
message_allocator_ = *allocator;
|
||||
allocator::set_allocator_for_deleter(&message_deleter_, &message_allocator_);
|
||||
}
|
||||
|
||||
AnySubscriptionCallback(const AnySubscriptionCallback &) = default;
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrCallback
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
/// Generic function for setting the callback.
|
||||
/**
|
||||
* There are specializations that overload this in order to deprecate some
|
||||
* callback signatures, and also to fix ambiguity between shared_ptr and
|
||||
* unique_ptr callback signatures when using them with lambda functions.
|
||||
*/
|
||||
template<typename CallbackT>
|
||||
AnySubscriptionCallback<MessageT, AllocatorT>
|
||||
set(CallbackT callback)
|
||||
{
|
||||
shared_ptr_callback_ = callback;
|
||||
// Use the SubscriptionCallbackTypeHelper to determine the actual type of
|
||||
// the CallbackT, in terms of std::function<...>, which does not happen
|
||||
// automatically with lambda functions in cases where the arguments can be
|
||||
// converted to one another, e.g. shared_ptr and unique_ptr.
|
||||
using scbth = detail::SubscriptionCallbackTypeHelper<MessageT, CallbackT>;
|
||||
|
||||
// Determine if the given CallbackT is a deprecated signature or not.
|
||||
constexpr auto is_deprecated =
|
||||
rclcpp::function_traits::same_arguments<
|
||||
typename scbth::callback_type,
|
||||
std::function<void(std::shared_ptr<MessageT>)>
|
||||
>::value
|
||||
||
|
||||
rclcpp::function_traits::same_arguments<
|
||||
typename scbth::callback_type,
|
||||
std::function<void(std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)>
|
||||
>::value;
|
||||
|
||||
// Use the discovered type to force the type of callback when assigning
|
||||
// into the variant.
|
||||
if constexpr (is_deprecated) {
|
||||
// If deprecated, call sub-routine that is deprecated.
|
||||
set_deprecated(static_cast<typename scbth::callback_type>(callback));
|
||||
} else {
|
||||
// Otherwise just assign it.
|
||||
callback_variant_ = static_cast<typename scbth::callback_type>(callback);
|
||||
}
|
||||
|
||||
// Return copy of self for easier testing, normally will be compiled out.
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrWithInfoCallback
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
/// Function for shared_ptr to non-const MessageT, which is deprecated.
|
||||
// TODO(wjwwood): enable this deprecation after Galactic
|
||||
// [[deprecated(
|
||||
// "use 'void (std::shared_ptr<const MessageT>)' instead"
|
||||
// )]]
|
||||
void
|
||||
set_deprecated(std::function<void(std::shared_ptr<MessageT>)> callback)
|
||||
// set(CallbackT callback)
|
||||
{
|
||||
shared_ptr_with_info_callback_ = callback;
|
||||
callback_variant_ = callback;
|
||||
}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
ConstSharedPtrCallback
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
/// Function for shared_ptr to non-const MessageT with MessageInfo, which is deprecated.
|
||||
// TODO(wjwwood): enable this deprecation after Galactic
|
||||
// [[deprecated(
|
||||
// "use 'void (std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)' instead"
|
||||
// )]]
|
||||
void
|
||||
set_deprecated(
|
||||
std::function<void(std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)> callback)
|
||||
{
|
||||
const_shared_ptr_callback_ = callback;
|
||||
callback_variant_ = callback;
|
||||
}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
ConstSharedPtrWithInfoCallback
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
std::unique_ptr<MessageT, MessageDeleter>
|
||||
create_unique_ptr_from_shared_ptr_message(const std::shared_ptr<const MessageT> & message)
|
||||
{
|
||||
const_shared_ptr_with_info_callback_ = callback;
|
||||
auto ptr = MessageAllocTraits::allocate(message_allocator_, 1);
|
||||
MessageAllocTraits::construct(message_allocator_, ptr, *message);
|
||||
return std::unique_ptr<MessageT, MessageDeleter>(ptr, message_deleter_);
|
||||
}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
UniquePtrCallback
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
{
|
||||
unique_ptr_callback_ = callback;
|
||||
}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
UniquePtrWithInfoCallback
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void set(CallbackT callback)
|
||||
{
|
||||
unique_ptr_with_info_callback_ = callback;
|
||||
}
|
||||
|
||||
void dispatch(
|
||||
std::shared_ptr<MessageT> message, const rclcpp::MessageInfo & message_info)
|
||||
void
|
||||
dispatch(
|
||||
std::shared_ptr<MessageT> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
if (shared_ptr_callback_) {
|
||||
shared_ptr_callback_(message);
|
||||
} else if (shared_ptr_with_info_callback_) {
|
||||
shared_ptr_with_info_callback_(message, message_info);
|
||||
} else if (const_shared_ptr_callback_) {
|
||||
const_shared_ptr_callback_(message);
|
||||
} else if (const_shared_ptr_with_info_callback_) {
|
||||
const_shared_ptr_with_info_callback_(message, message_info);
|
||||
} else if (unique_ptr_callback_) {
|
||||
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
|
||||
MessageAllocTraits::construct(*message_allocator_.get(), ptr, *message);
|
||||
unique_ptr_callback_(MessageUniquePtr(ptr, message_deleter_));
|
||||
} else if (unique_ptr_with_info_callback_) {
|
||||
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
|
||||
MessageAllocTraits::construct(*message_allocator_.get(), ptr, *message);
|
||||
unique_ptr_with_info_callback_(MessageUniquePtr(ptr, message_deleter_), message_info);
|
||||
} else {
|
||||
throw std::runtime_error("unexpected message without any callback set");
|
||||
}
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
}
|
||||
|
||||
void dispatch_intra_process(
|
||||
ConstMessageSharedPtr message, const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
|
||||
if (const_shared_ptr_callback_) {
|
||||
const_shared_ptr_callback_(message);
|
||||
} else if (const_shared_ptr_with_info_callback_) {
|
||||
const_shared_ptr_with_info_callback_(message, message_info);
|
||||
} else {
|
||||
if (
|
||||
unique_ptr_callback_ || unique_ptr_with_info_callback_ ||
|
||||
shared_ptr_callback_ || shared_ptr_with_info_callback_)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"unexpected dispatch_intra_process const shared "
|
||||
"message call with no const shared_ptr callback");
|
||||
} else {
|
||||
throw std::runtime_error("unexpected message without any callback set");
|
||||
// Check if the variant is "unset", throw if it is.
|
||||
if (callback_variant_.index() == 0) {
|
||||
if (std::get<0>(callback_variant_) == nullptr) {
|
||||
// This can happen if it is default initialized, or if it is assigned nullptr.
|
||||
throw std::runtime_error("dispatch called on an unset AnySubscriptionCallback");
|
||||
}
|
||||
}
|
||||
// Dispatch.
|
||||
std::visit(
|
||||
[&message, &message_info, this](auto && callback) {
|
||||
using T = std::decay_t<decltype(callback)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, ConstRefCallback>) {
|
||||
callback(*message);
|
||||
} else if constexpr (std::is_same_v<T, ConstRefWithInfoCallback>) {
|
||||
callback(*message, message_info);
|
||||
} else if constexpr (std::is_same_v<T, UniquePtrCallback>) {
|
||||
callback(create_unique_ptr_from_shared_ptr_message(message));
|
||||
} else if constexpr (std::is_same_v<T, UniquePtrWithInfoCallback>) {
|
||||
callback(create_unique_ptr_from_shared_ptr_message(message), message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrCallback>||
|
||||
std::is_same_v<T, SharedPtrCallback>)
|
||||
{
|
||||
callback(message);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoCallback>)
|
||||
{
|
||||
callback(message, message_info);
|
||||
} else {
|
||||
static_assert(always_false_v<T>, "unhandled callback type");
|
||||
}
|
||||
}, callback_variant_);
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
}
|
||||
|
||||
void dispatch_intra_process(
|
||||
MessageUniquePtr message, const rclcpp::MessageInfo & message_info)
|
||||
void
|
||||
dispatch_intra_process(
|
||||
std::shared_ptr<const MessageT> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
|
||||
if (shared_ptr_callback_) {
|
||||
typename std::shared_ptr<MessageT> shared_message = std::move(message);
|
||||
shared_ptr_callback_(shared_message);
|
||||
} else if (shared_ptr_with_info_callback_) {
|
||||
typename std::shared_ptr<MessageT> shared_message = std::move(message);
|
||||
shared_ptr_with_info_callback_(shared_message, message_info);
|
||||
} else if (unique_ptr_callback_) {
|
||||
unique_ptr_callback_(std::move(message));
|
||||
} else if (unique_ptr_with_info_callback_) {
|
||||
unique_ptr_with_info_callback_(std::move(message), message_info);
|
||||
} else if (const_shared_ptr_callback_ || const_shared_ptr_with_info_callback_) {
|
||||
throw std::runtime_error(
|
||||
"unexpected dispatch_intra_process unique message call"
|
||||
" with const shared_ptr callback");
|
||||
} else {
|
||||
throw std::runtime_error("unexpected message without any callback set");
|
||||
// Check if the variant is "unset", throw if it is.
|
||||
if (callback_variant_.index() == 0) {
|
||||
if (std::get<0>(callback_variant_) == nullptr) {
|
||||
// This can happen if it is default initialized, or if it is assigned nullptr.
|
||||
throw std::runtime_error("dispatch called on an unset AnySubscriptionCallback");
|
||||
}
|
||||
}
|
||||
// Dispatch.
|
||||
std::visit(
|
||||
[&message, &message_info, this](auto && callback) {
|
||||
using T = std::decay_t<decltype(callback)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, ConstRefCallback>) {
|
||||
callback(*message);
|
||||
} else if constexpr (std::is_same_v<T, ConstRefWithInfoCallback>) {
|
||||
callback(*message, message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, UniquePtrCallback>||
|
||||
std::is_same_v<T, SharedPtrCallback>)
|
||||
{
|
||||
callback(create_unique_ptr_from_shared_ptr_message(message));
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, UniquePtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoCallback>)
|
||||
{
|
||||
callback(create_unique_ptr_from_shared_ptr_message(message), message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrCallback>)
|
||||
{
|
||||
callback(message);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>)
|
||||
{
|
||||
callback(message, message_info);
|
||||
} else {
|
||||
static_assert(always_false_v<T>, "unhandled callback type");
|
||||
}
|
||||
}, callback_variant_);
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
}
|
||||
|
||||
bool use_take_shared_method() const
|
||||
void
|
||||
dispatch_intra_process(
|
||||
std::unique_ptr<MessageT, MessageDeleter> message,
|
||||
const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
return const_shared_ptr_callback_ || const_shared_ptr_with_info_callback_;
|
||||
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) {
|
||||
// This can happen if it is default initialized, or if it is assigned nullptr.
|
||||
throw std::runtime_error("dispatch called on an unset AnySubscriptionCallback");
|
||||
}
|
||||
}
|
||||
// Dispatch.
|
||||
std::visit(
|
||||
[&message, &message_info](auto && callback) {
|
||||
using T = std::decay_t<decltype(callback)>;
|
||||
|
||||
if constexpr (std::is_same_v<T, ConstRefCallback>) {
|
||||
callback(*message);
|
||||
} else if constexpr (std::is_same_v<T, ConstRefWithInfoCallback>) {
|
||||
callback(*message, message_info);
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, UniquePtrCallback>||
|
||||
std::is_same_v<T, SharedConstPtrCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrCallback>||
|
||||
std::is_same_v<T, SharedPtrCallback>)
|
||||
{
|
||||
callback(std::move(message));
|
||||
} else if constexpr ( // NOLINT[readability/braces]
|
||||
std::is_same_v<T, UniquePtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>||
|
||||
std::is_same_v<T, SharedPtrWithInfoCallback>)
|
||||
{
|
||||
callback(std::move(message), message_info);
|
||||
} else {
|
||||
static_assert(always_false_v<T>, "unhandled callback type");
|
||||
}
|
||||
}, callback_variant_);
|
||||
TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
}
|
||||
|
||||
void register_callback_for_tracing()
|
||||
constexpr
|
||||
bool
|
||||
use_take_shared_method() const
|
||||
{
|
||||
return
|
||||
std::holds_alternative<SharedConstPtrCallback>(callback_variant_) ||
|
||||
std::holds_alternative<SharedConstPtrWithInfoCallback>(callback_variant_) ||
|
||||
std::holds_alternative<ConstRefSharedConstPtrCallback>(callback_variant_) ||
|
||||
std::holds_alternative<ConstRefSharedConstPtrWithInfoCallback>(callback_variant_);
|
||||
}
|
||||
|
||||
void
|
||||
register_callback_for_tracing()
|
||||
{
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
if (shared_ptr_callback_) {
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
tracetools::get_symbol(shared_ptr_callback_));
|
||||
} else if (shared_ptr_with_info_callback_) {
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
tracetools::get_symbol(shared_ptr_with_info_callback_));
|
||||
} else if (unique_ptr_callback_) {
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
tracetools::get_symbol(unique_ptr_callback_));
|
||||
} else if (unique_ptr_with_info_callback_) {
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
tracetools::get_symbol(unique_ptr_with_info_callback_));
|
||||
}
|
||||
std::visit(
|
||||
[this](auto && callback) {
|
||||
TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
tracetools::get_symbol(callback));
|
||||
}, callback_variant_);
|
||||
#endif // TRACETOOLS_DISABLED
|
||||
}
|
||||
|
||||
typename HelperT::variant_type &
|
||||
get_variant()
|
||||
{
|
||||
return callback_variant_;
|
||||
}
|
||||
|
||||
const typename HelperT::variant_type &
|
||||
get_variant() const
|
||||
{
|
||||
return callback_variant_;
|
||||
}
|
||||
|
||||
private:
|
||||
std::shared_ptr<MessageAlloc> message_allocator_;
|
||||
// TODO(wjwwood): switch to inheriting from std::variant (i.e. HelperT::variant_type) once
|
||||
// inheriting from std::variant is realistic (maybe C++23?), see:
|
||||
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2162r0.html
|
||||
// For now, compose the variant into this class as a private attribute.
|
||||
typename HelperT::variant_type callback_variant_;
|
||||
|
||||
MessageAlloc message_allocator_;
|
||||
MessageDeleter message_deleter_;
|
||||
};
|
||||
|
||||
|
||||
@@ -222,13 +222,6 @@ private:
|
||||
}
|
||||
};
|
||||
|
||||
namespace callback_group
|
||||
{
|
||||
|
||||
using CallbackGroupType [[deprecated("use rclcpp::CallbackGroupType instead")]] = CallbackGroupType;
|
||||
using CallbackGroup [[deprecated("use rclcpp::CallbackGroup instead")]] = CallbackGroup;
|
||||
|
||||
} // namespace callback_group
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CALLBACK_GROUP_HPP_
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
@@ -47,6 +48,17 @@ public:
|
||||
/// Forward declare WeakContextsWrapper
|
||||
class WeakContextsWrapper;
|
||||
|
||||
class OnShutdownCallbackHandle
|
||||
{
|
||||
friend class Context;
|
||||
|
||||
public:
|
||||
using OnShutdownCallbackType = std::function<void ()>;
|
||||
|
||||
private:
|
||||
std::weak_ptr<OnShutdownCallbackType> callback;
|
||||
};
|
||||
|
||||
/// Context which encapsulates shared state between nodes and other similar entities.
|
||||
/**
|
||||
* A context also represents the lifecycle between init and shutdown of rclcpp.
|
||||
@@ -177,7 +189,7 @@ public:
|
||||
bool
|
||||
shutdown(const std::string & reason);
|
||||
|
||||
using OnShutdownCallback = std::function<void ()>;
|
||||
using OnShutdownCallback = OnShutdownCallbackHandle::OnShutdownCallbackType;
|
||||
|
||||
/// Add a on_shutdown callback to be called when shutdown is called for this context.
|
||||
/**
|
||||
@@ -203,23 +215,47 @@ public:
|
||||
OnShutdownCallback
|
||||
on_shutdown(OnShutdownCallback callback);
|
||||
|
||||
/// Return the shutdown callbacks as const.
|
||||
/// Add a on_shutdown callback to be called when shutdown is called for this context.
|
||||
/**
|
||||
* Using the returned reference is not thread-safe with calls that modify
|
||||
* the list of "on shutdown" callbacks, i.e. on_shutdown().
|
||||
* These callbacks will be called in the order they are added as the second
|
||||
* to last step in shutdown().
|
||||
*
|
||||
* When shutdown occurs due to the signal handler, these callbacks are run
|
||||
* asynchronously in the dedicated singal handling thread.
|
||||
*
|
||||
* Also, shutdown() may be called from the destructor of this function.
|
||||
* Therefore, it is not safe to throw exceptions from these callbacks.
|
||||
* Instead, log errors or use some other mechanism to indicate an error has
|
||||
* occurred.
|
||||
*
|
||||
* On shutdown callbacks may be registered before init and after shutdown,
|
||||
* and persist on repeated init's.
|
||||
*
|
||||
* \param[in] callback the on_shutdown callback to be registered
|
||||
* \return the created callback handle
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<OnShutdownCallback> &
|
||||
get_on_shutdown_callbacks() const;
|
||||
virtual
|
||||
OnShutdownCallbackHandle
|
||||
add_on_shutdown_callback(OnShutdownCallback callback);
|
||||
|
||||
/// Remove an registered on_shutdown callbacks.
|
||||
/**
|
||||
* \param[in] callback_handle the on_shutdown callback handle to be removed.
|
||||
* \return true if the callback is found and removed, otherwise false.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
remove_on_shutdown_callback(const OnShutdownCallbackHandle & callback_handle);
|
||||
|
||||
/// Return the shutdown callbacks.
|
||||
/**
|
||||
* Using the returned reference is not thread-safe with calls that modify
|
||||
* the list of "on shutdown" callbacks, i.e. on_shutdown().
|
||||
* Returned callbacks are a copy of the registered callbacks.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<OnShutdownCallback> &
|
||||
get_on_shutdown_callbacks();
|
||||
std::vector<OnShutdownCallback>
|
||||
get_on_shutdown_callbacks() const;
|
||||
|
||||
/// Return the internal rcl context.
|
||||
RCLCPP_PUBLIC
|
||||
@@ -299,8 +335,8 @@ private:
|
||||
// attempt to acquire another sub context.
|
||||
std::recursive_mutex sub_contexts_mutex_;
|
||||
|
||||
std::vector<OnShutdownCallback> on_shutdown_callbacks_;
|
||||
std::mutex on_shutdown_callbacks_mutex_;
|
||||
std::unordered_set<std::shared_ptr<OnShutdownCallback>> on_shutdown_callbacks_;
|
||||
mutable std::mutex on_shutdown_callbacks_mutex_;
|
||||
|
||||
/// Condition variable for timed sleep (see sleep_for).
|
||||
std::condition_variable interrupt_condition_variable_;
|
||||
|
||||
@@ -36,22 +36,6 @@ RCLCPP_PUBLIC
|
||||
DefaultContext::SharedPtr
|
||||
get_global_default_context();
|
||||
|
||||
namespace default_context
|
||||
{
|
||||
|
||||
using DefaultContext
|
||||
[[deprecated("use rclcpp::contexts::DefaultContext instead")]] = DefaultContext;
|
||||
|
||||
[[deprecated("use rclcpp::contexts::get_global_default_context() instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
inline
|
||||
DefaultContext::SharedPtr
|
||||
get_global_default_context()
|
||||
{
|
||||
return rclcpp::contexts::get_global_default_context();
|
||||
}
|
||||
|
||||
} // namespace default_context
|
||||
} // namespace contexts
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
69
rclcpp/include/rclcpp/create_generic_publisher.hpp
Normal file
69
rclcpp/include/rclcpp/create_generic_publisher.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2020, Apex.AI 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__CREATE_GENERIC_PUBLISHER_HPP_
|
||||
#define RCLCPP__CREATE_GENERIC_PUBLISHER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/generic_publisher.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/publisher_options.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Create and return a GenericPublisher.
|
||||
/**
|
||||
* The returned pointer will never be empty, but this function can throw various exceptions, for
|
||||
* instance when the message's package can not be found on the AMENT_PREFIX_PATH.
|
||||
*
|
||||
* \param topics_interface NodeTopicsInterface pointer used in parts of the setup
|
||||
* \param topic_name Topic name
|
||||
* \param topic_type Topic type
|
||||
* \param qos %QoS settings
|
||||
* \param options %Publisher options.
|
||||
* Not all publisher options are currently respected, the only relevant options for this
|
||||
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
std::shared_ptr<GenericPublisher> create_generic_publisher(
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr topics_interface,
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
)
|
||||
{
|
||||
auto ts_lib = rclcpp::get_typesupport_library(topic_type, "rosidl_typesupport_cpp");
|
||||
auto pub = std::make_shared<GenericPublisher>(
|
||||
topics_interface->get_node_base_interface(),
|
||||
std::move(ts_lib),
|
||||
topic_name,
|
||||
topic_type,
|
||||
qos,
|
||||
options);
|
||||
topics_interface->add_publisher(pub, options.callback_group);
|
||||
return pub;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CREATE_GENERIC_PUBLISHER_HPP_
|
||||
79
rclcpp/include/rclcpp/create_generic_subscription.hpp
Normal file
79
rclcpp/include/rclcpp/create_generic_subscription.hpp
Normal file
@@ -0,0 +1,79 @@
|
||||
// Copyright 2020, Apex.AI 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__CREATE_GENERIC_SUBSCRIPTION_HPP_
|
||||
#define RCLCPP__CREATE_GENERIC_SUBSCRIPTION_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/subscription.h"
|
||||
#include "rclcpp/generic_subscription.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/serialized_message.hpp"
|
||||
#include "rclcpp/subscription_options.hpp"
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Create and return a GenericSubscription.
|
||||
/**
|
||||
* The returned pointer will never be empty, but this function can throw various exceptions, for
|
||||
* instance when the message's package can not be found on the AMENT_PREFIX_PATH.
|
||||
*
|
||||
* \param topics_interface NodeTopicsInterface pointer used in parts of the setup.
|
||||
* \param topic_name Topic name
|
||||
* \param topic_type Topic type
|
||||
* \param qos %QoS settings
|
||||
* \param callback Callback for new messages of serialized form
|
||||
* \param options %Publisher options.
|
||||
* Not all publisher options are currently respected, the only relevant options for this
|
||||
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
std::shared_ptr<GenericSubscription> create_generic_subscription(
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr topics_interface,
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
)
|
||||
{
|
||||
auto ts_lib = rclcpp::get_typesupport_library(
|
||||
topic_type, "rosidl_typesupport_cpp");
|
||||
|
||||
auto subscription = std::make_shared<GenericSubscription>(
|
||||
topics_interface->get_node_base_interface(),
|
||||
std::move(ts_lib),
|
||||
topic_name,
|
||||
topic_type,
|
||||
qos,
|
||||
callback,
|
||||
options);
|
||||
|
||||
topics_interface->add_subscription(subscription, options.callback_group);
|
||||
|
||||
return subscription;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CREATE_GENERIC_SUBSCRIPTION_HPP_
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP__DETAIL__MUTEX_TWO_PRIORITIES_HPP_
|
||||
#define RCLCPP__DETAIL__MUTEX_TWO_PRIORITIES_HPP_
|
||||
|
||||
#include <condition_variable>
|
||||
#include <mutex>
|
||||
|
||||
namespace rclcpp
|
||||
@@ -62,11 +63,11 @@ private:
|
||||
get_low_priority_lockable();
|
||||
|
||||
private:
|
||||
// Implementation detail: the idea here is that only one low priority thread can be
|
||||
// trying to take the data_ mutex while the others are excluded by the barrier_ mutex.
|
||||
// All high priority threads are already waiting for the data_ mutex.
|
||||
std::mutex barrier_;
|
||||
std::mutex data_;
|
||||
std::condition_variable hp_cv_;
|
||||
std::condition_variable lp_cv_;
|
||||
std::mutex cv_mutex_;
|
||||
size_t hp_waiting_count_{0u};
|
||||
bool data_taken_{false};
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
// Copyright 2021 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__DETAIL__SUBSCRIPTION_CALLBACK_TYPE_HELPER_HPP_
|
||||
#define RCLCPP__DETAIL__SUBSCRIPTION_CALLBACK_TYPE_HELPER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/message_info.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// Template metaprogramming helper used to resolve the callback argument into a std::function.
|
||||
/**
|
||||
* Sometimes the CallbackT is a std::function already, but it could also be a
|
||||
* function pointer, lambda, bind, or some variant of those.
|
||||
* In some cases, like a lambda where the arguments can be converted between one
|
||||
* another, e.g. std::function<void (shared_ptr<...>)> and
|
||||
* std::function<void (unique_ptr<...>)>, you need to make that not ambiguous
|
||||
* by checking the arguments independently using function traits rather than
|
||||
* rely on overloading the two std::function types.
|
||||
*
|
||||
* This issue, with the lambda's, can be demonstrated with this minimal program:
|
||||
*
|
||||
* #include <functional>
|
||||
* #include <memory>
|
||||
*
|
||||
* void f(std::function<void (std::shared_ptr<int>)>) {}
|
||||
* void f(std::function<void (std::unique_ptr<int>)>) {}
|
||||
*
|
||||
* int main() {
|
||||
* // Fails to compile with an "ambiguous call" error.
|
||||
* f([](std::shared_ptr<int>){});
|
||||
*
|
||||
* // Works.
|
||||
* std::function<void (std::shared_ptr<int>)> cb = [](std::shared_ptr<int>){};
|
||||
* f(cb);
|
||||
* }
|
||||
*
|
||||
* If this program ever starts working in a future version of C++, this class
|
||||
* may become redundant.
|
||||
*
|
||||
* This helper works by using SFINAE with rclcpp::function_traits::same_arguments<>
|
||||
* to narrow down the exact std::function<> type for the given CallbackT.
|
||||
*/
|
||||
template<typename MessageT, typename CallbackT, typename Enable = void>
|
||||
struct SubscriptionCallbackTypeHelper
|
||||
{
|
||||
using callback_type = typename rclcpp::function_traits::as_std_function<CallbackT>::type;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(std::shared_ptr<const MessageT>)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type = std::function<void (std::shared_ptr<const MessageT>)>;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type =
|
||||
std::function<void (std::shared_ptr<const MessageT>, const rclcpp::MessageInfo &)>;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(const std::shared_ptr<const MessageT> &)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type = std::function<void (const std::shared_ptr<const MessageT> &)>;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(const std::shared_ptr<const MessageT> &, const rclcpp::MessageInfo &)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type =
|
||||
std::function<void (const std::shared_ptr<const MessageT> &, const rclcpp::MessageInfo &)>;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(std::shared_ptr<MessageT>)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type = std::function<void (std::shared_ptr<MessageT>)>;
|
||||
};
|
||||
|
||||
template<typename MessageT, typename CallbackT>
|
||||
struct SubscriptionCallbackTypeHelper<
|
||||
MessageT,
|
||||
CallbackT,
|
||||
typename std::enable_if_t<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
std::function<void(std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)>
|
||||
>::value
|
||||
>
|
||||
>
|
||||
{
|
||||
using callback_type =
|
||||
std::function<void (std::shared_ptr<MessageT>, const rclcpp::MessageInfo &)>;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DETAIL__SUBSCRIPTION_CALLBACK_TYPE_HELPER_HPP_
|
||||
@@ -282,8 +282,8 @@ class ParameterModifiedInCallbackException : public std::runtime_error
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/// Thrown when a parameter override wasn't provided and one was required.
|
||||
class NoParameterOverrideProvided : public std::runtime_error
|
||||
/// Thrown when an uninitialized parameter is accessed.
|
||||
class ParameterUninitializedException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
/// Construct an instance.
|
||||
@@ -291,8 +291,8 @@ public:
|
||||
* \param[in] name the name of the parameter.
|
||||
* \param[in] message custom exception message.
|
||||
*/
|
||||
explicit NoParameterOverrideProvided(const std::string & name)
|
||||
: std::runtime_error("parameter '" + name + "' requires an user provided parameter override")
|
||||
explicit ParameterUninitializedException(const std::string & name)
|
||||
: std::runtime_error("parameter '" + name + "' is not initialized")
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/executor_options.hpp"
|
||||
@@ -544,7 +545,7 @@ protected:
|
||||
RCLCPP_DISABLE_COPY(Executor)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
virtual void
|
||||
spin_once_impl(std::chrono::nanoseconds timeout);
|
||||
|
||||
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
|
||||
@@ -571,14 +572,11 @@ protected:
|
||||
/// nodes that are associated with the executor
|
||||
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>
|
||||
weak_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
|
||||
|
||||
/// shutdown callback handle registered to Context
|
||||
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
|
||||
};
|
||||
|
||||
namespace executor
|
||||
{
|
||||
|
||||
using Executor [[deprecated("use rclcpp::Executor instead")]] = rclcpp::Executor;
|
||||
|
||||
} // namespace executor
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXECUTOR_HPP_
|
||||
|
||||
@@ -38,20 +38,6 @@ struct ExecutorOptions
|
||||
size_t max_conditions;
|
||||
};
|
||||
|
||||
namespace executor
|
||||
{
|
||||
|
||||
using ExecutorArgs [[deprecated("use rclcpp::ExecutorOptions instead")]] = ExecutorOptions;
|
||||
|
||||
[[deprecated("use rclcpp::ExecutorOptions() instead")]]
|
||||
inline
|
||||
rclcpp::ExecutorOptions
|
||||
create_default_executor_arguments()
|
||||
{
|
||||
return rclcpp::ExecutorOptions();
|
||||
}
|
||||
|
||||
} // namespace executor
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXECUTOR_OPTIONS_HPP_
|
||||
|
||||
@@ -68,6 +68,10 @@ public:
|
||||
rcl_guard_condition_t * executor_guard_condition);
|
||||
|
||||
/// Finalize StaticExecutorEntitiesCollector to clear resources
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_init() {return initialized_;}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
fini();
|
||||
@@ -339,6 +343,9 @@ private:
|
||||
|
||||
/// Executable list: timers, subscribers, clients, services and waitables
|
||||
rclcpp::experimental::ExecutableList exec_list_;
|
||||
|
||||
/// Bool to check if the entities collector has been initialized
|
||||
bool initialized_ = false;
|
||||
};
|
||||
|
||||
} // namespace executors
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
|
||||
#define RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
|
||||
|
||||
#include <chrono>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
@@ -78,6 +79,42 @@ public:
|
||||
void
|
||||
spin() override;
|
||||
|
||||
/// Static executor implementation of spin some
|
||||
/**
|
||||
* This non-blocking function will execute entities that
|
||||
* were ready when this API was called, until timeout or no
|
||||
* more work available. Entities that got ready while
|
||||
* executing work, won't be taken into account here.
|
||||
*
|
||||
* Example:
|
||||
* while(condition) {
|
||||
* spin_some();
|
||||
* sleep(); // User should have some sync work or
|
||||
* // sleep to avoid a 100% CPU usage
|
||||
* }
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0)) override;
|
||||
|
||||
/// Static executor implementation of spin all
|
||||
/**
|
||||
* This non-blocking function will execute entities until
|
||||
* timeout or no more work available. If new entities get ready
|
||||
* while executing work available, they will be executed
|
||||
* as long as the timeout hasn't expired.
|
||||
*
|
||||
* Example:
|
||||
* while(condition) {
|
||||
* spin_all();
|
||||
* sleep(); // User should have some sync work or
|
||||
* // sleep to avoid a 100% CPU usage
|
||||
* }
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_all(std::chrono::nanoseconds max_duration) override;
|
||||
|
||||
/// Add a callback group to an executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::add_callback_group
|
||||
@@ -155,113 +192,23 @@ public:
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_automatically_added_callback_groups_from_nodes() override;
|
||||
|
||||
/// Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
|
||||
/**
|
||||
* \param[in] future The future to wait on. If this function returns SUCCESS, the future can be
|
||||
* accessed without blocking (though it may still throw an exception).
|
||||
* \param[in] timeout Optional timeout parameter, which gets passed to
|
||||
* Executor::execute_ready_executables.
|
||||
* `-1` is block forever, `0` is non-blocking.
|
||||
* If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
|
||||
* code.
|
||||
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
|
||||
*
|
||||
* Example usage:
|
||||
* rclcpp::executors::StaticSingleThreadedExecutor exec;
|
||||
* // ... other part of code like creating node
|
||||
* // define future
|
||||
* exec.add_node(node);
|
||||
* exec.spin_until_future_complete(future);
|
||||
*/
|
||||
template<typename FutureT, typename TimeRepT = int64_t, typename TimeT = std::milli>
|
||||
rclcpp::FutureReturnCode
|
||||
spin_until_future_complete(
|
||||
FutureT & future,
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
std::future_status status = future.wait_for(std::chrono::seconds(0));
|
||||
if (status == std::future_status::ready) {
|
||||
return rclcpp::FutureReturnCode::SUCCESS;
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
timeout);
|
||||
if (timeout_ns > std::chrono::nanoseconds::zero()) {
|
||||
end_time += timeout_ns;
|
||||
}
|
||||
std::chrono::nanoseconds timeout_left = timeout_ns;
|
||||
|
||||
entities_collector_->init(&wait_set_, memory_strategy_, &interrupt_guard_condition_);
|
||||
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
|
||||
|
||||
while (rclcpp::ok(this->context_)) {
|
||||
// Do one set of work.
|
||||
entities_collector_->refresh_wait_set(timeout_left);
|
||||
execute_ready_executables();
|
||||
// Check if the future is set, return SUCCESS if it is.
|
||||
status = future.wait_for(std::chrono::seconds(0));
|
||||
if (status == std::future_status::ready) {
|
||||
return rclcpp::FutureReturnCode::SUCCESS;
|
||||
}
|
||||
// If the original timeout is < 0, then this is blocking, never TIMEOUT.
|
||||
if (timeout_ns < std::chrono::nanoseconds::zero()) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise check if we still have time to wait, return TIMEOUT if not.
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (now >= end_time) {
|
||||
return rclcpp::FutureReturnCode::TIMEOUT;
|
||||
}
|
||||
// Subtract the elapsed time from the original timeout.
|
||||
timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
|
||||
}
|
||||
|
||||
// The future did not complete before ok() returned false, return INTERRUPTED.
|
||||
return rclcpp::FutureReturnCode::INTERRUPTED;
|
||||
}
|
||||
|
||||
/// Not yet implemented, see https://github.com/ros2/rclcpp/issues/1219 for tracking
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0)) override
|
||||
{
|
||||
(void)max_duration;
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"spin_some is not implemented for StaticSingleThreadedExecutor, use spin or "
|
||||
"spin_until_future_complete");
|
||||
}
|
||||
|
||||
/// Not yet implemented, see https://github.com/ros2/rclcpp/issues/1219 for tracking
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_all(std::chrono::nanoseconds) override
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"spin_all is not implemented for StaticSingleThreadedExecutor, use spin or "
|
||||
"spin_until_future_complete");
|
||||
}
|
||||
|
||||
/// Not yet implemented, see https://github.com/ros2/rclcpp/issues/1219 for tracking
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_once(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1)) override
|
||||
{
|
||||
(void)timeout;
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"spin_once is not implemented for StaticSingleThreadedExecutor, use spin or "
|
||||
"spin_until_future_complete");
|
||||
}
|
||||
|
||||
protected:
|
||||
/// Check which executables in ExecutableList struct are ready from wait_set and execute them.
|
||||
/**
|
||||
* \param[in] exec_list Structure that can hold subscriptionbases, timerbases, etc
|
||||
* \param[in] timeout Optional timeout parameter.
|
||||
* @brief Executes ready executables from wait set.
|
||||
* @param spin_once if true executes only the first ready executable.
|
||||
* @return true if any executable was ready.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
execute_ready_executables(bool spin_once = false);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
execute_ready_executables();
|
||||
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_once_impl(std::chrono::nanoseconds timeout) override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(StaticSingleThreadedExecutor)
|
||||
|
||||
@@ -202,7 +202,8 @@ public:
|
||||
// None of the buffers require ownership, so we promote the pointer
|
||||
std::shared_ptr<MessageT> msg = std::move(message);
|
||||
|
||||
this->template add_shared_msg_to_buffers<MessageT>(msg, sub_ids.take_shared_subscriptions);
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
msg, sub_ids.take_shared_subscriptions);
|
||||
} else if (!sub_ids.take_ownership_subscriptions.empty() && // NOLINT
|
||||
sub_ids.take_shared_subscriptions.size() <= 1)
|
||||
{
|
||||
@@ -227,7 +228,7 @@ public:
|
||||
// for the buffers that do not require ownership
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
|
||||
|
||||
this->template add_shared_msg_to_buffers<MessageT>(
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
shared_msg, sub_ids.take_shared_subscriptions);
|
||||
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
std::move(message), sub_ids.take_ownership_subscriptions, allocator);
|
||||
@@ -263,7 +264,7 @@ public:
|
||||
// If there are no owning, just convert to shared.
|
||||
std::shared_ptr<MessageT> shared_msg = std::move(message);
|
||||
if (!sub_ids.take_shared_subscriptions.empty()) {
|
||||
this->template add_shared_msg_to_buffers<MessageT>(
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
shared_msg, sub_ids.take_shared_subscriptions);
|
||||
}
|
||||
return shared_msg;
|
||||
@@ -273,7 +274,7 @@ public:
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
|
||||
|
||||
if (!sub_ids.take_shared_subscriptions.empty()) {
|
||||
this->template add_shared_msg_to_buffers<MessageT>(
|
||||
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter>(
|
||||
shared_msg,
|
||||
sub_ids.take_shared_subscriptions);
|
||||
}
|
||||
@@ -350,7 +351,10 @@ private:
|
||||
bool
|
||||
can_communicate(PublisherInfo pub_info, SubscriptionInfo sub_info) const;
|
||||
|
||||
template<typename MessageT>
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc,
|
||||
typename Deleter>
|
||||
void
|
||||
add_shared_msg_to_buffers(
|
||||
std::shared_ptr<const MessageT> message,
|
||||
@@ -363,9 +367,16 @@ private:
|
||||
}
|
||||
auto subscription_base = subscription_it->second.subscription.lock();
|
||||
if (subscription_base) {
|
||||
auto subscription = std::static_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
|
||||
auto subscription = std::dynamic_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcess<MessageT, Alloc, Deleter>
|
||||
>(subscription_base);
|
||||
if (nullptr == subscription) {
|
||||
throw std::runtime_error(
|
||||
"failed to dynamic cast SubscriptionIntraProcessBase to "
|
||||
"SubscriptionIntraProcess<MessageT, Alloc, Deleter>, which "
|
||||
"can happen when the publisher and subscription use different "
|
||||
"allocator types, which is not supported");
|
||||
}
|
||||
|
||||
subscription->provide_intra_process_message(message);
|
||||
} else {
|
||||
@@ -394,9 +405,16 @@ private:
|
||||
}
|
||||
auto subscription_base = subscription_it->second.subscription.lock();
|
||||
if (subscription_base) {
|
||||
auto subscription = std::static_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
|
||||
auto subscription = std::dynamic_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcess<MessageT, Alloc, Deleter>
|
||||
>(subscription_base);
|
||||
if (nullptr == subscription) {
|
||||
throw std::runtime_error(
|
||||
"failed to dynamic cast SubscriptionIntraProcessBase to "
|
||||
"SubscriptionIntraProcess<MessageT, Alloc, Deleter>, which "
|
||||
"can happen when the publisher and subscription use different "
|
||||
"allocator types, which is not supported");
|
||||
}
|
||||
|
||||
if (std::next(it) == subscription_ids.end()) {
|
||||
// If this is the last subscription, give up ownership
|
||||
|
||||
@@ -162,6 +162,32 @@ struct same_arguments : std::is_same<
|
||||
>
|
||||
{};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename ReturnTypeT, typename ... Args>
|
||||
struct as_std_function_helper;
|
||||
|
||||
template<typename ReturnTypeT, typename ... Args>
|
||||
struct as_std_function_helper<ReturnTypeT, std::tuple<Args ...>>
|
||||
{
|
||||
using type = std::function<ReturnTypeT(Args ...)>;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template<
|
||||
typename FunctorT,
|
||||
typename FunctionTraits = function_traits<FunctorT>
|
||||
>
|
||||
struct as_std_function
|
||||
{
|
||||
using type = typename detail::as_std_function_helper<
|
||||
typename FunctionTraits::return_type,
|
||||
typename FunctionTraits::arguments
|
||||
>::type;
|
||||
};
|
||||
|
||||
} // namespace function_traits
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
127
rclcpp/include/rclcpp/generic_publisher.hpp
Normal file
127
rclcpp/include/rclcpp/generic_publisher.hpp
Normal file
@@ -0,0 +1,127 @@
|
||||
// Copyright 2018, Bosch Software Innovations GmbH.
|
||||
// Copyright 2021, Apex.AI 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__GENERIC_PUBLISHER_HPP_
|
||||
#define RCLCPP__GENERIC_PUBLISHER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rcpputils/shared_library.hpp"
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/serialized_message.hpp"
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// %Publisher for serialized messages whose type is not known at compile time.
|
||||
/**
|
||||
* Since the type is not known at compile time, this is not a template, and the dynamic library
|
||||
* containing type support information has to be identified and loaded based on the type name.
|
||||
*
|
||||
* It does not support intra-process handling.
|
||||
*/
|
||||
class GenericPublisher : public rclcpp::PublisherBase
|
||||
{
|
||||
public:
|
||||
// cppcheck-suppress unknownMacro
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericPublisher)
|
||||
|
||||
/// Constructor.
|
||||
/**
|
||||
* In order to properly publish to a topic, this publisher needs to be added to
|
||||
* the node_topic_interface of the node passed into this constructor.
|
||||
*
|
||||
* \sa rclcpp::Node::create_generic_publisher() or rclcpp::create_generic_publisher() for
|
||||
* creating an instance of this class and adding it to the node_topic_interface.
|
||||
*
|
||||
* \param node_base Pointer to parent node's NodeBaseInterface
|
||||
* \param ts_lib Type support library, needs to correspond to topic_type
|
||||
* \param topic_name Topic name
|
||||
* \param topic_type Topic type
|
||||
* \param qos %QoS settings
|
||||
* \param callback Callback for new messages of serialized form
|
||||
* \param options %Publisher options.
|
||||
* Not all publisher options are currently respected, the only relevant options for this
|
||||
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
GenericPublisher(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
std::shared_ptr<rcpputils::SharedLibrary> ts_lib,
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
|
||||
: rclcpp::PublisherBase(
|
||||
node_base,
|
||||
topic_name,
|
||||
*rclcpp::get_typesupport_handle(topic_type, "rosidl_typesupport_cpp", *ts_lib),
|
||||
options.template to_rcl_publisher_options<rclcpp::SerializedMessage>(qos)),
|
||||
ts_lib_(ts_lib)
|
||||
{
|
||||
// This is unfortunately duplicated with the code in publisher.hpp.
|
||||
// TODO(nnmm): Deduplicate by moving this into PublisherBase.
|
||||
if (options.event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.deadline_callback,
|
||||
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
|
||||
}
|
||||
if (options.event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.liveliness_callback,
|
||||
RCL_PUBLISHER_LIVELINESS_LOST);
|
||||
}
|
||||
if (options.event_callbacks.incompatible_qos_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.incompatible_qos_callback,
|
||||
RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
|
||||
} else if (options.use_default_callbacks) {
|
||||
// Register default callback when not specified
|
||||
try {
|
||||
this->add_event_handler(
|
||||
[this](QOSOfferedIncompatibleQoSInfo & info) {
|
||||
this->default_incompatible_qos_callback(info);
|
||||
},
|
||||
RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
|
||||
} catch (UnsupportedEventTypeException & /*exc*/) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~GenericPublisher() = default;
|
||||
|
||||
/// Publish a rclcpp::SerializedMessage.
|
||||
RCLCPP_PUBLIC
|
||||
void publish(const rclcpp::SerializedMessage & message);
|
||||
|
||||
private:
|
||||
// The type support library should stay loaded, so it is stored in the GenericPublisher
|
||||
std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__GENERIC_PUBLISHER_HPP_
|
||||
161
rclcpp/include/rclcpp/generic_subscription.hpp
Normal file
161
rclcpp/include/rclcpp/generic_subscription.hpp
Normal file
@@ -0,0 +1,161 @@
|
||||
// Copyright 2018, Bosch Software Innovations GmbH.
|
||||
// Copyright 2021, Apex.AI 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__GENERIC_SUBSCRIPTION_HPP_
|
||||
#define RCLCPP__GENERIC_SUBSCRIPTION_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rcpputils/shared_library.hpp"
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/serialized_message.hpp"
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// %Subscription for serialized messages whose type is not known at compile time.
|
||||
/**
|
||||
* Since the type is not known at compile time, this is not a template, and the dynamic library
|
||||
* containing type support information has to be identified and loaded based on the type name.
|
||||
*
|
||||
* It does not support intra-process handling.
|
||||
*/
|
||||
class GenericSubscription : public rclcpp::SubscriptionBase
|
||||
{
|
||||
public:
|
||||
// cppcheck-suppress unknownMacro
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericSubscription)
|
||||
|
||||
/// Constructor.
|
||||
/**
|
||||
* In order to properly subscribe to a topic, this subscription needs to be added to
|
||||
* the node_topic_interface of the node passed into this constructor.
|
||||
*
|
||||
* \sa rclcpp::Node::create_generic_subscription() or rclcpp::create_generic_subscription() for
|
||||
* creating an instance of this class and adding it to the node_topic_interface.
|
||||
*
|
||||
* \param node_base Pointer to parent node's NodeBaseInterface
|
||||
* \param ts_lib Type support library, needs to correspond to topic_type
|
||||
* \param topic_name Topic name
|
||||
* \param topic_type Topic type
|
||||
* \param qos %QoS settings
|
||||
* \param callback Callback for new messages of serialized form
|
||||
* \param options %Subscription options.
|
||||
* Not all subscription options are currently respected, the only relevant options for this
|
||||
* subscription are `event_callbacks`, `use_default_callbacks`, `ignore_local_publications`, and
|
||||
* `%callback_group`.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
GenericSubscription(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::shared_ptr<rcpputils::SharedLibrary> ts_lib,
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
// TODO(nnmm): Add variant for callback with message info. See issue #1604.
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
|
||||
: SubscriptionBase(
|
||||
node_base,
|
||||
*rclcpp::get_typesupport_handle(topic_type, "rosidl_typesupport_cpp", *ts_lib),
|
||||
topic_name,
|
||||
options.template to_rcl_subscription_options<rclcpp::SerializedMessage>(qos),
|
||||
true),
|
||||
callback_(callback),
|
||||
ts_lib_(ts_lib)
|
||||
{
|
||||
// This is unfortunately duplicated with the code in subscription.hpp.
|
||||
// TODO(nnmm): Deduplicate by moving this into SubscriptionBase.
|
||||
if (options.event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.deadline_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
|
||||
}
|
||||
if (options.event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.liveliness_callback,
|
||||
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
|
||||
}
|
||||
if (options.event_callbacks.incompatible_qos_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.incompatible_qos_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
|
||||
} else if (options.use_default_callbacks) {
|
||||
// Register default callback when not specified
|
||||
try {
|
||||
this->add_event_handler(
|
||||
[this](QOSRequestedIncompatibleQoSInfo & info) {
|
||||
this->default_incompatible_qos_callback(info);
|
||||
},
|
||||
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
|
||||
} catch (UnsupportedEventTypeException & /*exc*/) {
|
||||
// pass
|
||||
}
|
||||
}
|
||||
if (options.event_callbacks.message_lost_callback) {
|
||||
this->add_event_handler(
|
||||
options.event_callbacks.message_lost_callback,
|
||||
RCL_SUBSCRIPTION_MESSAGE_LOST);
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~GenericSubscription() = default;
|
||||
|
||||
// Same as create_serialized_message() as the subscription is to serialized_messages only
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void> create_message() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rclcpp::SerializedMessage> create_serialized_message() override;
|
||||
|
||||
/// Cast the message to a rclcpp::SerializedMessage and call the callback.
|
||||
RCLCPP_PUBLIC
|
||||
void handle_message(
|
||||
std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) override;
|
||||
|
||||
/// This function is currently not implemented.
|
||||
RCLCPP_PUBLIC
|
||||
void handle_loaned_message(
|
||||
void * loaned_message, const rclcpp::MessageInfo & message_info) override;
|
||||
|
||||
// Same as return_serialized_message() as the subscription is to serialized_messages only
|
||||
RCLCPP_PUBLIC
|
||||
void return_message(std::shared_ptr<void> & message) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(GenericSubscription)
|
||||
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback_;
|
||||
// The type support library should stay loaded, so it is stored in the GenericSubscription
|
||||
std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__GENERIC_SUBSCRIPTION_HPP_
|
||||
115
rclcpp/include/rclcpp/network_flow_endpoint.hpp
Normal file
115
rclcpp/include/rclcpp/network_flow_endpoint.hpp
Normal file
@@ -0,0 +1,115 @@
|
||||
// Copyright 2020 Ericsson AB
|
||||
//
|
||||
// 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__NETWORK_FLOW_ENDPOINT_HPP_
|
||||
#define RCLCPP__NETWORK_FLOW_ENDPOINT_HPP_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
|
||||
#include "rcl/network_flow_endpoints.h"
|
||||
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Forward declaration
|
||||
class NetworkFlowEndpoint;
|
||||
|
||||
/// Check if two NetworkFlowEndpoint instances are equal
|
||||
RCLCPP_PUBLIC
|
||||
bool operator==(const NetworkFlowEndpoint & left, const NetworkFlowEndpoint & right);
|
||||
|
||||
/// Check if two NetworkFlowEndpoint instances are not equal
|
||||
RCLCPP_PUBLIC
|
||||
bool operator!=(const NetworkFlowEndpoint & left, const NetworkFlowEndpoint & right);
|
||||
|
||||
/// Streaming helper for NetworkFlowEndpoint
|
||||
RCLCPP_PUBLIC
|
||||
std::ostream & operator<<(std::ostream & os, const NetworkFlowEndpoint & network_flow_endpoint);
|
||||
|
||||
/**
|
||||
* Class describes a network flow endpoint based on the counterpart definition
|
||||
* in the RMW layer.
|
||||
*/
|
||||
class NetworkFlowEndpoint
|
||||
{
|
||||
public:
|
||||
/// Construct from rcl_network_flow_endpoint_t
|
||||
RCLCPP_PUBLIC
|
||||
explicit NetworkFlowEndpoint(const rcl_network_flow_endpoint_t & network_flow_endpoint)
|
||||
: transport_protocol_(
|
||||
rcl_network_flow_endpoint_get_transport_protocol_string(network_flow_endpoint.
|
||||
transport_protocol)),
|
||||
internet_protocol_(
|
||||
rcl_network_flow_endpoint_get_internet_protocol_string(
|
||||
network_flow_endpoint.internet_protocol)),
|
||||
transport_port_(network_flow_endpoint.transport_port),
|
||||
flow_label_(network_flow_endpoint.flow_label),
|
||||
dscp_(network_flow_endpoint.dscp),
|
||||
internet_address_(network_flow_endpoint.internet_address)
|
||||
{
|
||||
}
|
||||
|
||||
/// Get transport protocol
|
||||
RCLCPP_PUBLIC
|
||||
const std::string & transport_protocol() const;
|
||||
|
||||
/// Get internet protocol
|
||||
RCLCPP_PUBLIC
|
||||
const std::string & internet_protocol() const;
|
||||
|
||||
/// Get transport port
|
||||
RCLCPP_PUBLIC
|
||||
uint16_t transport_port() const;
|
||||
|
||||
/// Get flow label
|
||||
RCLCPP_PUBLIC
|
||||
uint32_t flow_label() const;
|
||||
|
||||
/// Get DSCP
|
||||
RCLCPP_PUBLIC
|
||||
uint8_t dscp() const;
|
||||
|
||||
/// Get internet address
|
||||
RCLCPP_PUBLIC
|
||||
const std::string & internet_address() const;
|
||||
|
||||
/// Compare two NetworkFlowEndpoint instances
|
||||
friend bool rclcpp::operator==(
|
||||
const NetworkFlowEndpoint & left,
|
||||
const NetworkFlowEndpoint & right);
|
||||
friend bool rclcpp::operator!=(
|
||||
const NetworkFlowEndpoint & left,
|
||||
const NetworkFlowEndpoint & right);
|
||||
|
||||
/// Streaming helper
|
||||
friend std::ostream & rclcpp::operator<<(
|
||||
std::ostream & os,
|
||||
const NetworkFlowEndpoint & network_flow_endpoint);
|
||||
|
||||
private:
|
||||
std::string transport_protocol_;
|
||||
std::string internet_protocol_;
|
||||
uint16_t transport_port_;
|
||||
uint32_t flow_label_;
|
||||
uint8_t dscp_;
|
||||
std::string internet_address_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NETWORK_FLOW_ENDPOINT_HPP_
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
@@ -41,6 +42,8 @@
|
||||
#include "rclcpp/clock.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/event.hpp"
|
||||
#include "rclcpp/generic_publisher.hpp"
|
||||
#include "rclcpp/generic_subscription.hpp"
|
||||
#include "rclcpp/logger.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/message_memory_strategy.hpp"
|
||||
@@ -150,6 +153,17 @@ public:
|
||||
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
|
||||
get_callback_groups() const;
|
||||
|
||||
/// Iterate over the callback groups in the node, calling the given function on each valid one.
|
||||
/**
|
||||
* This method is called in a thread-safe way, and also makes sure to only call the given
|
||||
* function on those items that are still valid.
|
||||
*
|
||||
* \param[in] func The callback function to call on each valid callback group.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
for_each_callback_group(const node_interfaces::NodeBaseInterface::CallbackGroupFunction & func);
|
||||
|
||||
/// Create and return a Publisher.
|
||||
/**
|
||||
* The rclcpp::QoS has several convenient constructors, including a
|
||||
@@ -266,6 +280,55 @@ public:
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Create and return a GenericPublisher.
|
||||
/**
|
||||
* The returned pointer will never be empty, but this function can throw various exceptions, for
|
||||
* instance when the message's package can not be found on the AMENT_PREFIX_PATH.
|
||||
*
|
||||
* \param[in] topic_name Topic name
|
||||
* \param[in] topic_type Topic type
|
||||
* \param[in] qos %QoS settings
|
||||
* \param options %Publisher options.
|
||||
* Not all publisher options are currently respected, the only relevant options for this
|
||||
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
|
||||
* \return Shared pointer to the created generic publisher.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
std::shared_ptr<rclcpp::GenericPublisher> create_generic_publisher(
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
);
|
||||
|
||||
/// Create and return a GenericSubscription.
|
||||
/**
|
||||
* The returned pointer will never be empty, but this function can throw various exceptions, for
|
||||
* instance when the message's package can not be found on the AMENT_PREFIX_PATH.
|
||||
*
|
||||
* \param[in] topic_name Topic name
|
||||
* \param[in] topic_type Topic type
|
||||
* \param[in] qos %QoS settings
|
||||
* \param[in] callback Callback for new messages of serialized form
|
||||
* \param[in] options %Subscription options.
|
||||
* Not all subscription options are currently respected, the only relevant options for this
|
||||
* subscription are `event_callbacks`, `use_default_callbacks`, `ignore_local_publications`, and
|
||||
* `%callback_group`.
|
||||
* \return Shared pointer to the created generic subscription.
|
||||
*/
|
||||
template<typename AllocatorT = std::allocator<void>>
|
||||
std::shared_ptr<rclcpp::GenericSubscription> create_generic_subscription(
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
);
|
||||
|
||||
/// Declare and initialize a parameter, return the effective value.
|
||||
/**
|
||||
* This method is used to declare that a parameter exists on this node.
|
||||
@@ -932,12 +995,15 @@ public:
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_service_names_and_types() const;
|
||||
|
||||
/// Return the number of publishers that are advertised on a given topic.
|
||||
/// Return a map of existing service names to list of service types for a specific node.
|
||||
/**
|
||||
* \param[in] node_name the node_name on which to count the publishers.
|
||||
* \param[in] namespace_ the namespace of the node associated with the name
|
||||
* \return number of publishers that are advertised on a given topic.
|
||||
* \throws std::runtime_error if publishers could not be counted
|
||||
* This function only considers services - not clients.
|
||||
* The returned names are the actual names used and do not have remap rules applied.
|
||||
*
|
||||
* \param[in] node_name name of the node.
|
||||
* \param[in] namespace_ namespace of the node.
|
||||
* \return a map of existing service names to list of service types.
|
||||
* \throws std::runtime_error anything that rcl_error can throw.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
|
||||
@@ -36,10 +36,12 @@
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/create_client.hpp"
|
||||
#include "rclcpp/create_generic_publisher.hpp"
|
||||
#include "rclcpp/create_generic_subscription.hpp"
|
||||
#include "rclcpp/create_publisher.hpp"
|
||||
#include "rclcpp/create_service.hpp"
|
||||
#include "rclcpp/create_timer.hpp"
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/create_timer.hpp"
|
||||
#include "rclcpp/detail/resolve_enable_topic_statistics.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
@@ -152,6 +154,43 @@ Node::create_service(
|
||||
group);
|
||||
}
|
||||
|
||||
template<typename AllocatorT>
|
||||
std::shared_ptr<rclcpp::GenericPublisher>
|
||||
Node::create_generic_publisher(
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
|
||||
{
|
||||
return rclcpp::create_generic_publisher(
|
||||
node_topics_,
|
||||
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
|
||||
topic_type,
|
||||
qos,
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
template<typename AllocatorT>
|
||||
std::shared_ptr<rclcpp::GenericSubscription>
|
||||
Node::create_generic_subscription(
|
||||
const std::string & topic_name,
|
||||
const std::string & topic_type,
|
||||
const rclcpp::QoS & qos,
|
||||
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
|
||||
{
|
||||
return rclcpp::create_generic_subscription(
|
||||
node_topics_,
|
||||
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
|
||||
topic_type,
|
||||
qos,
|
||||
std::move(callback),
|
||||
options
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
template<typename ParameterT>
|
||||
auto
|
||||
Node::declare_parameter(
|
||||
|
||||
@@ -16,10 +16,13 @@
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_BASE_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/node.h"
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
@@ -30,12 +33,34 @@ namespace rclcpp
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void global_for_each_callback_group(
|
||||
NodeBaseInterface * node_base_interface,
|
||||
const NodeBaseInterface::CallbackGroupFunction & func);
|
||||
|
||||
// Class to hold the global map of mutexes
|
||||
class map_of_mutexes final
|
||||
{
|
||||
public:
|
||||
// Methods need to be protected by internal mutex
|
||||
void create_mutex_of_nodebase(const NodeBaseInterface * nodebase);
|
||||
std::shared_ptr<std::mutex>
|
||||
get_mutex_of_nodebase(const NodeBaseInterface * nodebase);
|
||||
void delete_mutex_of_nodebase(const NodeBaseInterface * nodebase);
|
||||
|
||||
private:
|
||||
std::unordered_map<const NodeBaseInterface *, std::shared_ptr<std::mutex>> data_;
|
||||
std::mutex internal_mutex_;
|
||||
};
|
||||
|
||||
/// Implementation of the NodeBase part of the Node API.
|
||||
class NodeBase : public NodeBaseInterface
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBase)
|
||||
|
||||
static map_of_mutexes map_object;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
NodeBase(
|
||||
const std::string & node_name,
|
||||
|
||||
@@ -38,6 +38,8 @@ class NodeBaseInterface
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
|
||||
|
||||
using CallbackGroupFunction = std::function<void (rclcpp::CallbackGroup::SharedPtr)>;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeBaseInterface() = default;
|
||||
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
set_parameters(const std::vector<rclcpp::Parameter> & parameters) = 0;
|
||||
|
||||
/// Set and initialize a parameter, all at once.
|
||||
/// Set one or more parameters, all at once.
|
||||
/**
|
||||
* \sa rclcpp::Node::set_parameters_atomically
|
||||
*/
|
||||
|
||||
@@ -31,11 +31,14 @@
|
||||
#include "rcl_interfaces/srv/list_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/set_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/set_parameters_atomically.hpp"
|
||||
#include "rcl_yaml_param_parser/parser.h"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/parameter_map.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
@@ -154,6 +157,42 @@ public:
|
||||
void(std::shared_future<rcl_interfaces::msg::SetParametersResult>)
|
||||
> callback = nullptr);
|
||||
|
||||
/// Delete several parameters at once.
|
||||
/**
|
||||
* This function behaves like command-line tool `ros2 param delete` would.
|
||||
*
|
||||
* \param parameters_names vector of parameters names
|
||||
* \return the future of the set_parameter service used to delete the parameters
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
|
||||
delete_parameters(
|
||||
const std::vector<std::string> & parameters_names);
|
||||
|
||||
/// Load parameters from yaml file.
|
||||
/**
|
||||
* This function behaves like command-line tool `ros2 param load` would.
|
||||
*
|
||||
* \param yaml_filename the full name of the yaml file
|
||||
* \return the future of the set_parameter service used to load the parameters
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
|
||||
load_parameters(
|
||||
const std::string & yaml_filename);
|
||||
|
||||
/// Load parameters from parameter map.
|
||||
/**
|
||||
* This function filters the parameters to be set based on the node name.
|
||||
*
|
||||
* \param yaml_filename the full name of the yaml file
|
||||
* \return the future of the set_parameter service used to load the parameters
|
||||
* \throw InvalidParametersException if there is no parameter to set
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
|
||||
load_parameters(const rclcpp::ParameterMap & parameter_map);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<rcl_interfaces::msg::ListParametersResult>
|
||||
list_parameters(
|
||||
@@ -444,6 +483,46 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
/// Delete several parameters at once.
|
||||
/**
|
||||
* This function behaves like command-line tool `ros2 param delete` would.
|
||||
*
|
||||
* \param parameters_names vector of parameters names
|
||||
* \param timeout for the spin used to make it synchronous
|
||||
* \return the future of the set_parameter service used to delete the parameters
|
||||
*/
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
delete_parameters(
|
||||
const std::vector<std::string> & parameters_names,
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return delete_parameters(
|
||||
parameters_names,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
/// Load parameters from yaml file.
|
||||
/**
|
||||
* This function behaves like command-line tool `ros2 param load` would.
|
||||
*
|
||||
* \param yaml_filename the full name of the yaml file
|
||||
* \param timeout for the spin used to make it synchronous
|
||||
* \return the future of the set_parameter service used to load the parameters
|
||||
*/
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
load_parameters(
|
||||
const std::string & yaml_filename,
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return load_parameters(
|
||||
yaml_filename,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
list_parameters(
|
||||
@@ -524,6 +603,18 @@ protected:
|
||||
const std::vector<rclcpp::Parameter> & parameters,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
delete_parameters(
|
||||
const std::vector<std::string> & parameters_names,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
load_parameters(
|
||||
const std::string & yaml_filename,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
set_parameters_atomically(
|
||||
|
||||
@@ -52,7 +52,7 @@ struct ParameterEventCallbackHandle
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ParameterEventCallbackHandle)
|
||||
|
||||
using ParameterEventCallbackType =
|
||||
std::function<void (const rcl_interfaces::msg::ParameterEvent::SharedPtr &)>;
|
||||
std::function<void (const rcl_interfaces::msg::ParameterEvent &)>;
|
||||
|
||||
ParameterEventCallbackType callback;
|
||||
};
|
||||
@@ -115,16 +115,16 @@ struct ParameterEventCallbackHandle
|
||||
* For example:
|
||||
*
|
||||
* auto cb3 =
|
||||
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent::SharedPtr & event) {
|
||||
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent & event) {
|
||||
* // Look for any updates to parameters in "/a_namespace" as well as any parameter changes
|
||||
* // to our own node ("this_node")
|
||||
* std::regex re("(/a_namespace/.*)|(/this_node)");
|
||||
* if (regex_match(event->node, re)) {
|
||||
* if (regex_match(event.node, re)) {
|
||||
* // Now that we know the event matches the regular expression we scanned for, we can
|
||||
* // use 'get_parameter_from_event' to get a specific parameter name that we're looking for
|
||||
* rclcpp::Parameter p;
|
||||
* if (rclcpp::ParameterEventsSubscriber::get_parameter_from_event(
|
||||
* *event, p, remote_param_name, fqn))
|
||||
* event, p, remote_param_name, fqn))
|
||||
* {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
@@ -136,7 +136,7 @@ struct ParameterEventCallbackHandle
|
||||
*
|
||||
* // You can also use 'get_parameter*s*_from_event' to enumerate all changes that came
|
||||
* // in on this event
|
||||
* auto params = rclcpp::ParameterEventsSubscriber::get_parameters_from_event(*event);
|
||||
* auto params = rclcpp::ParameterEventsSubscriber::get_parameters_from_event(event);
|
||||
* for (auto & p : params) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
@@ -288,7 +288,7 @@ protected:
|
||||
/// Callback for parameter events subscriptions.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
event_callback(const rcl_interfaces::msg::ParameterEvent::SharedPtr event);
|
||||
event_callback(const rcl_interfaces::msg::ParameterEvent & event);
|
||||
|
||||
// Utility function for resolving node path.
|
||||
std::string resolve_path(const std::string & path);
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ParameterEventsFilter)
|
||||
enum class EventType {NEW, DELETED, CHANGED}; ///< An enum for the type of event.
|
||||
/// Used for the listed results
|
||||
using EventPair = std::pair<EventType, rcl_interfaces::msg::Parameter *>;
|
||||
using EventPair = std::pair<EventType, const rcl_interfaces::msg::Parameter *>;
|
||||
|
||||
/// Construct a filtered view of a parameter event.
|
||||
/**
|
||||
@@ -60,7 +60,7 @@ public:
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
ParameterEventsFilter(
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr event,
|
||||
std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event,
|
||||
const std::vector<std::string> & names,
|
||||
const std::vector<EventType> & types);
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
private:
|
||||
// access only allowed via const accessor.
|
||||
std::vector<EventPair> result_; ///< Storage of the resultant vector
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr event_; ///< Keep event in scope
|
||||
std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event_; ///< Keep event in scope
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP__PARAMETER_MAP_HPP_
|
||||
#define RCLCPP__PARAMETER_MAP_HPP_
|
||||
|
||||
#include <rcl_yaml_param_parser/parser.h>
|
||||
#include <rcl_yaml_param_parser/types.h>
|
||||
|
||||
#include <string>
|
||||
@@ -48,6 +49,14 @@ RCLCPP_PUBLIC
|
||||
ParameterValue
|
||||
parameter_value_from(const rcl_variant_t * const c_value);
|
||||
|
||||
/// Get the ParameterMap from a yaml file.
|
||||
/// \param[in] yaml_filename full name of the yaml file.
|
||||
/// \returns an instance of a parameter map
|
||||
/// \throws from rcl error of rcl_parse_yaml_file()
|
||||
RCLCPP_PUBLIC
|
||||
ParameterMap
|
||||
parameter_map_from_yaml_file(const std::string & yaml_filename);
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__PARAMETER_MAP_HPP_
|
||||
|
||||
@@ -40,6 +40,8 @@
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "tracetools/tracetools.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
@@ -279,6 +281,10 @@ protected:
|
||||
void
|
||||
do_inter_process_publish(const MessageT & msg)
|
||||
{
|
||||
TRACEPOINT(
|
||||
rclcpp_publish,
|
||||
static_cast<const void *>(publisher_handle_.get()),
|
||||
static_cast<const void *>(&msg));
|
||||
auto status = rcl_publish(publisher_handle_.get(), &msg, nullptr);
|
||||
|
||||
if (RCL_RET_PUBLISHER_INVALID == status) {
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "rcl/publisher.h"
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/network_flow_endpoint.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
@@ -193,6 +194,15 @@ public:
|
||||
uint64_t intra_process_publisher_id,
|
||||
IntraProcessManagerSharedPtr ipm);
|
||||
|
||||
/// Get network flow endpoints
|
||||
/**
|
||||
* Describes network flow endpoints that this publisher is sending messages out on
|
||||
* \return vector of NetworkFlowEndpoint
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::NetworkFlowEndpoint>
|
||||
get_network_flow_endpoints() const;
|
||||
|
||||
protected:
|
||||
template<typename EventCallbackT>
|
||||
void
|
||||
|
||||
@@ -45,6 +45,11 @@ struct PublisherOptionsBase
|
||||
/// Whether or not to use default callbacks when user doesn't supply any in event_callbacks
|
||||
bool use_default_callbacks = true;
|
||||
|
||||
/// Require middleware to generate unique network flow endpoints
|
||||
/// Disabled by default
|
||||
rmw_unique_network_flow_endpoints_requirement_t require_unique_network_flow_endpoints =
|
||||
RMW_UNIQUE_NETWORK_FLOW_ENDPOINTS_NOT_REQUIRED;
|
||||
|
||||
/// Callback group in which the waitable items from the publisher should be placed.
|
||||
std::shared_ptr<rclcpp::CallbackGroup> callback_group;
|
||||
|
||||
@@ -80,6 +85,8 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
|
||||
result.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(*message_alloc);
|
||||
result.qos = qos.get_rmw_qos_profile();
|
||||
result.rmw_publisher_options.require_unique_network_flow_endpoints =
|
||||
this->require_unique_network_flow_endpoints;
|
||||
|
||||
// Apply payload to rcl_publisher_options if necessary.
|
||||
if (rmw_implementation_payload && rmw_implementation_payload->has_been_customized()) {
|
||||
|
||||
@@ -103,8 +103,8 @@ struct RCLCPP_PUBLIC KeepLast : public rclcpp::QoSInitialization
|
||||
* and other entities, and includes things like how data is sent or resent,
|
||||
* how data is buffered on the publishing and subscribing side, and other things.
|
||||
* See:
|
||||
* <a href="https://index.ros.org/doc/ros2/Concepts/About-Quality-of-Service-Settings">
|
||||
* https://index.ros.org/doc/ros2/Concepts/About-Quality-of-Service-Settings
|
||||
* <a href="https://docs.ros.org/en/rolling/Concepts/About-Quality-of-Service-Settings.html">
|
||||
* https://docs.ros.org/en/rolling/Concepts/About-Quality-of-Service-Settings.html
|
||||
* </a>
|
||||
*/
|
||||
class RCLCPP_PUBLIC QoS
|
||||
|
||||
@@ -117,6 +117,15 @@
|
||||
* - Allocator related items:
|
||||
* - rclcpp/allocator/allocator_common.hpp
|
||||
* - rclcpp/allocator/allocator_deleter.hpp
|
||||
* - Generic publisher
|
||||
* - rclcpp::Node::create_generic_publisher()
|
||||
* - rclcpp::GenericPublisher
|
||||
* - rclcpp::GenericPublisher::publish()
|
||||
* - rclcpp/generic_publisher.hpp
|
||||
* - Generic subscription
|
||||
* - rclcpp::Node::create_generic_subscription()
|
||||
* - rclcpp::GenericSubscription
|
||||
* - rclcpp/generic_subscription.hpp
|
||||
* - Memory management tools:
|
||||
* - rclcpp/memory_strategies.hpp
|
||||
* - rclcpp/memory_strategy.hpp
|
||||
@@ -134,6 +143,7 @@
|
||||
* - rclcpp/scope_exit.hpp
|
||||
* - rclcpp/time.hpp
|
||||
* - rclcpp/utilities.hpp
|
||||
* - rclcpp/typesupport_helpers.hpp
|
||||
* - rclcpp/visibility_control.hpp
|
||||
*/
|
||||
|
||||
|
||||
@@ -177,25 +177,16 @@ public:
|
||||
using rosidl_typesupport_cpp::get_service_type_support_handle;
|
||||
auto service_type_support_handle = get_service_type_support_handle<ServiceT>();
|
||||
|
||||
std::weak_ptr<rcl_node_t> weak_node_handle(node_handle_);
|
||||
// rcl does the static memory allocation here
|
||||
service_handle_ = std::shared_ptr<rcl_service_t>(
|
||||
new rcl_service_t, [weak_node_handle, service_name](rcl_service_t * service)
|
||||
new rcl_service_t, [handle = node_handle_, service_name](rcl_service_t * service)
|
||||
{
|
||||
auto handle = weak_node_handle.lock();
|
||||
if (handle) {
|
||||
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
|
||||
"Error in destruction of rcl service handle: %s",
|
||||
rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
} else {
|
||||
RCLCPP_ERROR_STREAM(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Error in destruction of rcl service handle " << service_name <<
|
||||
": the Node Handle was destructed too early. You will leak memory");
|
||||
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
|
||||
"Error in destruction of rcl service handle: %s",
|
||||
rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
delete service;
|
||||
});
|
||||
@@ -349,15 +340,6 @@ public:
|
||||
send_response(*request_header, *response);
|
||||
}
|
||||
|
||||
[[deprecated("use the send_response() which takes references instead of shared pointers")]]
|
||||
void
|
||||
send_response(
|
||||
std::shared_ptr<rmw_request_id_t> req_id,
|
||||
std::shared_ptr<typename ServiceT::Response> response)
|
||||
{
|
||||
send_response(*req_id, *response);
|
||||
}
|
||||
|
||||
void
|
||||
send_response(rmw_request_id_t & req_id, typename ServiceT::Response & response)
|
||||
{
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/message_info.hpp"
|
||||
#include "rclcpp/network_flow_endpoint.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/serialized_message.hpp"
|
||||
@@ -61,8 +62,11 @@ class SubscriptionBase : public std::enable_shared_from_this<SubscriptionBase>
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
|
||||
|
||||
/// Default constructor.
|
||||
/// Constructor.
|
||||
/**
|
||||
* This accepts rcl_subscription_options_t instead of rclcpp::SubscriptionOptions because
|
||||
* rclcpp::SubscriptionOptions::to_rcl_subscription_options depends on the message type.
|
||||
*
|
||||
* \param[in] node_base NodeBaseInterface pointer used in parts of the setup.
|
||||
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
|
||||
* \param[in] topic_name Name of the topic to subscribe to.
|
||||
@@ -77,7 +81,7 @@ public:
|
||||
const rcl_subscription_options_t & subscription_options,
|
||||
bool is_serialized = false);
|
||||
|
||||
/// Default destructor.
|
||||
/// Destructor.
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~SubscriptionBase();
|
||||
|
||||
@@ -263,6 +267,15 @@ public:
|
||||
bool
|
||||
exchange_in_use_by_wait_set_state(void * pointer_to_subscription_part, bool in_use_state);
|
||||
|
||||
/// Get network flow endpoints
|
||||
/**
|
||||
* Describes network flow endpoints that this subscription is receiving messages on
|
||||
* \return vector of NetworkFlowEndpoint
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::NetworkFlowEndpoint>
|
||||
get_network_flow_endpoints() const;
|
||||
|
||||
protected:
|
||||
template<typename EventCallbackT>
|
||||
void
|
||||
|
||||
@@ -93,7 +93,7 @@ create_subscription_factory(
|
||||
auto allocator = options.get_allocator();
|
||||
|
||||
using rclcpp::AnySubscriptionCallback;
|
||||
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_subscription_callback(allocator);
|
||||
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_subscription_callback(*allocator);
|
||||
any_subscription_callback.set(std::forward<CallbackT>(callback));
|
||||
|
||||
SubscriptionFactory factory {
|
||||
|
||||
@@ -45,6 +45,11 @@ struct SubscriptionOptionsBase
|
||||
/// True to ignore local publications.
|
||||
bool ignore_local_publications = false;
|
||||
|
||||
/// Require middleware to generate unique network flow endpoints
|
||||
/// Disabled by default
|
||||
rmw_unique_network_flow_endpoints_requirement_t require_unique_network_flow_endpoints =
|
||||
RMW_UNIQUE_NETWORK_FLOW_ENDPOINTS_NOT_REQUIRED;
|
||||
|
||||
/// The callback group for this subscription. NULL to use the default callback group.
|
||||
rclcpp::CallbackGroup::SharedPtr callback_group = nullptr;
|
||||
|
||||
@@ -108,6 +113,8 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
result.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc);
|
||||
result.qos = qos.get_rmw_qos_profile();
|
||||
result.rmw_subscription_options.ignore_local_publications = this->ignore_local_publications;
|
||||
result.rmw_subscription_options.require_unique_network_flow_endpoints =
|
||||
this->require_unique_network_flow_endpoints;
|
||||
|
||||
// Apply payload to rcl_subscription_options if necessary.
|
||||
if (rmw_implementation_payload && rmw_implementation_payload->has_been_customized()) {
|
||||
|
||||
@@ -64,7 +64,7 @@ struct is_serialized_callback
|
||||
template<typename MessageT>
|
||||
struct extract_message_type
|
||||
{
|
||||
using type = typename std::remove_cv<MessageT>::type;
|
||||
using type = typename std::remove_cv_t<std::remove_reference_t<MessageT>>;
|
||||
};
|
||||
|
||||
template<typename MessageT>
|
||||
|
||||
@@ -152,11 +152,11 @@ private:
|
||||
std::shared_ptr<SubscriptionT> clock_subscription_{nullptr};
|
||||
std::mutex clock_sub_lock_;
|
||||
rclcpp::CallbackGroup::SharedPtr clock_callback_group_;
|
||||
rclcpp::executors::SingleThreadedExecutor clock_executor_;
|
||||
rclcpp::executors::SingleThreadedExecutor::SharedPtr clock_executor_;
|
||||
std::promise<void> cancel_clock_executor_promise_;
|
||||
|
||||
// The clock callback itself
|
||||
void clock_cb(const rosgraph_msgs::msg::Clock::SharedPtr msg);
|
||||
void clock_cb(std::shared_ptr<const rosgraph_msgs::msg::Clock> msg);
|
||||
|
||||
// Create the subscription for the clock topic
|
||||
void create_clock_sub();
|
||||
@@ -170,7 +170,7 @@ private:
|
||||
std::shared_ptr<ParamSubscriptionT> parameter_subscription_;
|
||||
|
||||
// Callback for parameter updates
|
||||
void on_parameter_event(const rcl_interfaces::msg::ParameterEvent::SharedPtr event);
|
||||
void on_parameter_event(std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event);
|
||||
|
||||
// An enum to hold the parameter state
|
||||
enum UseSimTimeParameterState {UNSET, SET_TRUE, SET_FALSE};
|
||||
@@ -191,7 +191,7 @@ private:
|
||||
// This is needed when new clocks are added.
|
||||
bool ros_time_active_{false};
|
||||
// Last set message to be passed to newly registered clocks
|
||||
rosgraph_msgs::msg::Clock::SharedPtr last_msg_set_;
|
||||
std::shared_ptr<const rosgraph_msgs::msg::Clock> last_msg_set_;
|
||||
|
||||
// A lock to protect iterating the associated_clocks_ field.
|
||||
std::mutex clock_list_lock_;
|
||||
|
||||
57
rclcpp/include/rclcpp/typesupport_helpers.hpp
Normal file
57
rclcpp/include/rclcpp/typesupport_helpers.hpp
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2018, Bosch Software Innovations GmbH.
|
||||
// Copyright 2021, Apex.AI 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__TYPESUPPORT_HELPERS_HPP_
|
||||
#define RCLCPP__TYPESUPPORT_HELPERS_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
|
||||
#include "rcpputils/shared_library.hpp"
|
||||
#include "rosidl_runtime_cpp/message_type_support_decl.hpp"
|
||||
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
/// Load the type support library for the given type.
|
||||
/**
|
||||
* \param[in] type The topic type, e.g. "std_msgs/msg/String"
|
||||
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
|
||||
* \return A shared library
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rcpputils::SharedLibrary>
|
||||
get_typesupport_library(const std::string & type, const std::string & typesupport_identifier);
|
||||
|
||||
/// Extract the type support handle from the library.
|
||||
/**
|
||||
* The library needs to match the topic type. The shared library must stay loaded for the lifetime of the result.
|
||||
* \param[in] type The topic type, e.g. "std_msgs/msg/String"
|
||||
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
|
||||
* \param[in] library The shared type support library
|
||||
* \return A type support handle
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_message_type_support_t *
|
||||
get_typesupport_handle(
|
||||
const std::string & type,
|
||||
const std::string & typesupport_identifier,
|
||||
rcpputils::SharedLibrary & library);
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__TYPESUPPORT_HELPERS_HPP_
|
||||
@@ -143,21 +143,6 @@ RCLCPP_PUBLIC
|
||||
bool
|
||||
ok(rclcpp::Context::SharedPtr context = nullptr);
|
||||
|
||||
/// Return true if init() has already been called for the given context.
|
||||
/**
|
||||
* If nullptr is given for the context, then the global context is used, i.e.
|
||||
* the context initialized by rclcpp::init().
|
||||
*
|
||||
* Deprecated, as it is no longer different from rcl_ok().
|
||||
*
|
||||
* \param[in] context Optional check for initialization of this Context.
|
||||
* \return true if the context is initialized, and false otherwise
|
||||
*/
|
||||
[[deprecated("use the function ok() instead, which has the same usage.")]]
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_initialized(rclcpp::Context::SharedPtr context = nullptr);
|
||||
|
||||
/// Shutdown rclcpp context, invalidating it for derived entities.
|
||||
/**
|
||||
* If nullptr is given for the context, then the global context is used, i.e.
|
||||
|
||||
100
rclcpp/include/rclcpp/wait_for_message.hpp
Normal file
100
rclcpp/include/rclcpp/wait_for_message.hpp
Normal file
@@ -0,0 +1,100 @@
|
||||
// Copyright 2021 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__WAIT_FOR_MESSAGE_HPP_
|
||||
#define RCLCPP__WAIT_FOR_MESSAGE_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/wait_set.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
/// Wait for the next incoming message.
|
||||
/**
|
||||
* Given an already initialized subscription,
|
||||
* wait for the next incoming message to arrive before the specified timeout.
|
||||
*
|
||||
* \param[out] out is the message to be filled when a new message is arriving.
|
||||
* \param[in] subscription shared pointer to a previously initialized subscription.
|
||||
* \param[in] context shared pointer to a context to watch for SIGINT requests.
|
||||
* \param[in] time_to_wait parameter specifying the timeout before returning.
|
||||
* \return true if a message was successfully received, false if message could not
|
||||
* be obtained or shutdown was triggered asynchronously on the context.
|
||||
*/
|
||||
template<class MsgT, class Rep = int64_t, class Period = std::milli>
|
||||
bool wait_for_message(
|
||||
MsgT & out,
|
||||
std::shared_ptr<rclcpp::Subscription<MsgT>> subscription,
|
||||
std::shared_ptr<rclcpp::Context> context,
|
||||
std::chrono::duration<Rep, Period> time_to_wait = std::chrono::duration<Rep, Period>(-1))
|
||||
{
|
||||
auto gc = std::make_shared<rclcpp::GuardCondition>(context);
|
||||
auto shutdown_callback_handle = context->add_on_shutdown_callback(
|
||||
[weak_gc = std::weak_ptr<rclcpp::GuardCondition>{gc}]() {
|
||||
auto strong_gc = weak_gc.lock();
|
||||
if (strong_gc) {
|
||||
strong_gc->trigger();
|
||||
}
|
||||
});
|
||||
|
||||
rclcpp::WaitSet wait_set;
|
||||
wait_set.add_subscription(subscription);
|
||||
wait_set.add_guard_condition(gc);
|
||||
auto ret = wait_set.wait(time_to_wait);
|
||||
if (ret.kind() != rclcpp::WaitResultKind::Ready) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wait_set.get_rcl_wait_set().guard_conditions[0]) {
|
||||
return false;
|
||||
}
|
||||
|
||||
rclcpp::MessageInfo info;
|
||||
if (!subscription->take(out, info)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Wait for the next incoming message.
|
||||
/**
|
||||
* Wait for the next incoming message to arrive on a specified topic before the specified timeout.
|
||||
*
|
||||
* \param[out] out is the message to be filled when a new message is arriving.
|
||||
* \param[in] node the node pointer to initialize the subscription on.
|
||||
* \param[in] topic the topic to wait for messages.
|
||||
* \param[in] time_to_wait parameter specifying the timeout before returning.
|
||||
* \return true if a message was successfully received, false if message could not
|
||||
* be obtained or shutdown was triggered asynchronously on the context.
|
||||
*/
|
||||
template<class MsgT, class Rep = int64_t, class Period = std::milli>
|
||||
bool wait_for_message(
|
||||
MsgT & out,
|
||||
rclcpp::Node::SharedPtr node,
|
||||
const std::string & topic,
|
||||
std::chrono::duration<Rep, Period> time_to_wait = std::chrono::duration<Rep, Period>(-1))
|
||||
{
|
||||
auto sub = node->create_subscription<MsgT>(topic, 1, [](const std::shared_ptr<MsgT>) {});
|
||||
return wait_for_message<MsgT, Rep, Period>(
|
||||
out, sub, node->get_node_options().context(), time_to_wait);
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__WAIT_FOR_MESSAGE_HPP_
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp</name>
|
||||
<version>8.2.0</version>
|
||||
<version>9.2.0</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
|
||||
@@ -12,12 +12,14 @@
|
||||
|
||||
<buildtool_depend>ament_cmake_ros</buildtool_depend>
|
||||
|
||||
<build_depend>ament_index_cpp</build_depend>
|
||||
<build_depend>builtin_interfaces</build_depend>
|
||||
<build_depend>rcl_interfaces</build_depend>
|
||||
<build_depend>rosgraph_msgs</build_depend>
|
||||
<build_depend>rosidl_runtime_cpp</build_depend>
|
||||
<build_depend>rosidl_typesupport_c</build_depend>
|
||||
<build_depend>rosidl_typesupport_cpp</build_depend>
|
||||
<build_export_depend>ament_index_cpp</build_export_depend>
|
||||
<build_export_depend>builtin_interfaces</build_export_depend>
|
||||
<build_export_depend>rcl_interfaces</build_export_depend>
|
||||
<build_export_depend>rosgraph_msgs</build_export_depend>
|
||||
|
||||
@@ -315,9 +315,13 @@ Context::shutdown(const std::string & reason)
|
||||
// set shutdown reason
|
||||
shutdown_reason_ = reason;
|
||||
// call each shutdown callback
|
||||
for (const auto & callback : on_shutdown_callbacks_) {
|
||||
callback();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
|
||||
for (const auto & callback : on_shutdown_callbacks_) {
|
||||
(*callback)();
|
||||
}
|
||||
}
|
||||
|
||||
// interrupt all blocking sleep_for() and all blocking executors or wait sets
|
||||
this->interrupt_all_sleep_for();
|
||||
// remove self from the global contexts
|
||||
@@ -344,20 +348,48 @@ Context::shutdown(const std::string & reason)
|
||||
rclcpp::Context::OnShutdownCallback
|
||||
Context::on_shutdown(OnShutdownCallback callback)
|
||||
{
|
||||
on_shutdown_callbacks_.push_back(callback);
|
||||
add_on_shutdown_callback(callback);
|
||||
return callback;
|
||||
}
|
||||
|
||||
const std::vector<rclcpp::Context::OnShutdownCallback> &
|
||||
Context::get_on_shutdown_callbacks() const
|
||||
rclcpp::OnShutdownCallbackHandle
|
||||
Context::add_on_shutdown_callback(OnShutdownCallback callback)
|
||||
{
|
||||
return on_shutdown_callbacks_;
|
||||
auto callback_shared_ptr = std::make_shared<OnShutdownCallback>(callback);
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
|
||||
on_shutdown_callbacks_.emplace(callback_shared_ptr);
|
||||
}
|
||||
|
||||
OnShutdownCallbackHandle callback_handle;
|
||||
callback_handle.callback = callback_shared_ptr;
|
||||
return callback_handle;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Context::OnShutdownCallback> &
|
||||
Context::get_on_shutdown_callbacks()
|
||||
bool
|
||||
Context::remove_on_shutdown_callback(const OnShutdownCallbackHandle & callback_handle)
|
||||
{
|
||||
return on_shutdown_callbacks_;
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
|
||||
auto callback_shared_ptr = callback_handle.callback.lock();
|
||||
if (callback_shared_ptr == nullptr) {
|
||||
return false;
|
||||
}
|
||||
return on_shutdown_callbacks_.erase(callback_shared_ptr) == 1;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Context::OnShutdownCallback>
|
||||
Context::get_on_shutdown_callbacks() const
|
||||
{
|
||||
std::vector<OnShutdownCallback> callbacks;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
|
||||
for (auto & iter : on_shutdown_callbacks_) {
|
||||
callbacks.emplace_back(*iter);
|
||||
}
|
||||
}
|
||||
|
||||
return callbacks;
|
||||
}
|
||||
|
||||
std::shared_ptr<rcl_context_t>
|
||||
|
||||
@@ -31,13 +31,31 @@ HighPriorityLockable::HighPriorityLockable(MutexTwoPriorities & parent)
|
||||
void
|
||||
HighPriorityLockable::lock()
|
||||
{
|
||||
parent_.data_.lock();
|
||||
std::unique_lock<std::mutex> guard{parent_.cv_mutex_};
|
||||
if (parent_.data_taken_) {
|
||||
++parent_.hp_waiting_count_;
|
||||
while (parent_.data_taken_) {
|
||||
parent_.hp_cv_.wait(guard);
|
||||
}
|
||||
--parent_.hp_waiting_count_;
|
||||
}
|
||||
parent_.data_taken_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
HighPriorityLockable::unlock()
|
||||
{
|
||||
parent_.data_.unlock();
|
||||
bool notify_lp{false};
|
||||
{
|
||||
std::lock_guard<std::mutex> guard{parent_.cv_mutex_};
|
||||
parent_.data_taken_ = false;
|
||||
notify_lp = 0u == parent_.hp_waiting_count_;
|
||||
}
|
||||
if (notify_lp) {
|
||||
parent_.lp_cv_.notify_one();
|
||||
} else {
|
||||
parent_.hp_cv_.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
LowPriorityLockable::LowPriorityLockable(MutexTwoPriorities & parent)
|
||||
@@ -47,16 +65,27 @@ LowPriorityLockable::LowPriorityLockable(MutexTwoPriorities & parent)
|
||||
void
|
||||
LowPriorityLockable::lock()
|
||||
{
|
||||
std::unique_lock<std::mutex> barrier_guard{parent_.barrier_};
|
||||
parent_.data_.lock();
|
||||
barrier_guard.release();
|
||||
std::unique_lock<std::mutex> guard{parent_.cv_mutex_};
|
||||
while (parent_.data_taken_ || parent_.hp_waiting_count_) {
|
||||
parent_.lp_cv_.wait(guard);
|
||||
}
|
||||
parent_.data_taken_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
LowPriorityLockable::unlock()
|
||||
{
|
||||
std::lock_guard<std::mutex> barrier_guard{parent_.barrier_, std::adopt_lock};
|
||||
parent_.data_.unlock();
|
||||
bool notify_lp{false};
|
||||
{
|
||||
std::lock_guard<std::mutex> guard{parent_.cv_mutex_};
|
||||
parent_.data_taken_ = false;
|
||||
notify_lp = 0u == parent_.hp_waiting_count_;
|
||||
}
|
||||
if (notify_lp) {
|
||||
parent_.lp_cv_.notify_one();
|
||||
} else {
|
||||
parent_.hp_cv_.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
HighPriorityLockable
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/scope_exit.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base.hpp"
|
||||
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
@@ -56,7 +57,7 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
|
||||
throw_from_rcl_error(ret, "Failed to create interrupt guard condition in Executor constructor");
|
||||
}
|
||||
|
||||
context_->on_shutdown(
|
||||
shutdown_callback_handle_ = context_->add_on_shutdown_callback(
|
||||
[weak_gc = std::weak_ptr<rclcpp::GuardCondition>{shutdown_guard_condition_}]() {
|
||||
auto strong_gc = weak_gc.lock();
|
||||
if (strong_gc) {
|
||||
@@ -138,6 +139,14 @@ Executor::~Executor()
|
||||
}
|
||||
// Remove and release the sigint guard condition
|
||||
memory_strategy_->remove_guard_condition(&shutdown_guard_condition_->get_rcl_guard_condition());
|
||||
|
||||
// Remove shutdown callback handle registered to Context
|
||||
if (!context_->remove_on_shutdown_callback(shutdown_callback_handle_)) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"failed to remove registered on_shutdown callback");
|
||||
rcl_reset_error();
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
@@ -182,14 +191,13 @@ Executor::add_callback_groups_from_nodes_associated_to_executor()
|
||||
for (auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (node) {
|
||||
auto group_ptrs = node->get_callback_groups();
|
||||
std::for_each(
|
||||
group_ptrs.begin(), group_ptrs.end(),
|
||||
[this, node](rclcpp::CallbackGroup::WeakPtr group_ptr)
|
||||
rclcpp::node_interfaces::global_for_each_callback_group(
|
||||
node.get(),
|
||||
[this, node](rclcpp::CallbackGroup::SharedPtr shared_group_ptr)
|
||||
{
|
||||
auto shared_group_ptr = group_ptr.lock();
|
||||
if (shared_group_ptr && shared_group_ptr->automatically_add_to_executor_with_node() &&
|
||||
!shared_group_ptr->get_associated_with_executor_atomic().load())
|
||||
if (
|
||||
shared_group_ptr->automatically_add_to_executor_with_node() &&
|
||||
!shared_group_ptr->get_associated_with_executor_atomic().load())
|
||||
{
|
||||
this->add_callback_group_to_map(
|
||||
shared_group_ptr,
|
||||
@@ -263,18 +271,21 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
|
||||
throw std::runtime_error("Node has already been added to an executor.");
|
||||
}
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
for (auto & weak_group : node_ptr->get_callback_groups()) {
|
||||
auto group_ptr = weak_group.lock();
|
||||
if (group_ptr != nullptr && !group_ptr->get_associated_with_executor_atomic().load() &&
|
||||
group_ptr->automatically_add_to_executor_with_node())
|
||||
rclcpp::node_interfaces::global_for_each_callback_group(
|
||||
node_ptr.get(),
|
||||
[this, node_ptr, notify](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
this->add_callback_group_to_map(
|
||||
group_ptr,
|
||||
node_ptr,
|
||||
weak_groups_to_nodes_associated_with_executor_,
|
||||
notify);
|
||||
}
|
||||
}
|
||||
if (!group_ptr->get_associated_with_executor_atomic().load() &&
|
||||
group_ptr->automatically_add_to_executor_with_node())
|
||||
{
|
||||
this->add_callback_group_to_map(
|
||||
group_ptr,
|
||||
node_ptr,
|
||||
weak_groups_to_nodes_associated_with_executor_,
|
||||
notify);
|
||||
}
|
||||
});
|
||||
|
||||
weak_nodes_.push_back(node_ptr);
|
||||
}
|
||||
|
||||
@@ -610,16 +621,18 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
|
||||
return true;
|
||||
},
|
||||
[&]() {subscription->handle_loaned_message(loaned_msg, message_info);});
|
||||
rcl_ret_t ret = rcl_return_loaned_message_from_subscription(
|
||||
subscription->get_subscription_handle().get(),
|
||||
loaned_msg);
|
||||
if (RCL_RET_OK != ret) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"rcl_return_loaned_message_from_subscription() failed for subscription on topic '%s': %s",
|
||||
subscription->get_topic_name(), rcl_get_error_string().str);
|
||||
if (nullptr != loaned_msg) {
|
||||
rcl_ret_t ret = rcl_return_loaned_message_from_subscription(
|
||||
subscription->get_subscription_handle().get(),
|
||||
loaned_msg);
|
||||
if (RCL_RET_OK != ret) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"rcl_return_loaned_message_from_subscription() failed for subscription on topic '%s': %s",
|
||||
subscription->get_topic_name(), rcl_get_error_string().str);
|
||||
}
|
||||
loaned_msg = nullptr;
|
||||
}
|
||||
loaned_msg = nullptr;
|
||||
} else {
|
||||
// This case is taking a copy of the message data from the middleware via
|
||||
// inter-process communication.
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
#include "rclcpp/executors/static_single_threaded_executor.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base.hpp"
|
||||
|
||||
using rclcpp::executors::StaticExecutorEntitiesCollector;
|
||||
|
||||
@@ -76,9 +77,13 @@ StaticExecutorEntitiesCollector::init(
|
||||
|
||||
// Add executor's guard condition
|
||||
memory_strategy_->add_guard_condition(executor_guard_condition);
|
||||
|
||||
// Get memory strategy and executable list. Prepare wait_set_
|
||||
std::shared_ptr<void> shared_ptr;
|
||||
execute(shared_ptr);
|
||||
|
||||
// The entities collector is now initialized
|
||||
initialized_ = true;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -286,18 +291,21 @@ StaticExecutorEntitiesCollector::add_node(
|
||||
if (has_executor.exchange(true)) {
|
||||
throw std::runtime_error("Node has already been added to an executor.");
|
||||
}
|
||||
for (const auto & weak_group : node_ptr->get_callback_groups()) {
|
||||
auto group_ptr = weak_group.lock();
|
||||
if (group_ptr != nullptr && !group_ptr->get_associated_with_executor_atomic().load() &&
|
||||
group_ptr->automatically_add_to_executor_with_node())
|
||||
rclcpp::node_interfaces::global_for_each_callback_group(
|
||||
node_ptr.get(),
|
||||
[this, node_ptr, &is_new_node](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
is_new_node = (add_callback_group(
|
||||
if (
|
||||
!group_ptr->get_associated_with_executor_atomic().load() &&
|
||||
group_ptr->automatically_add_to_executor_with_node())
|
||||
{
|
||||
is_new_node = (add_callback_group(
|
||||
group_ptr,
|
||||
node_ptr,
|
||||
weak_groups_to_nodes_associated_with_executor_) ||
|
||||
is_new_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
weak_nodes_.push_back(node_ptr);
|
||||
return is_new_node;
|
||||
}
|
||||
@@ -463,13 +471,11 @@ StaticExecutorEntitiesCollector::add_callback_groups_from_nodes_associated_to_ex
|
||||
for (const auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (node) {
|
||||
auto group_ptrs = node->get_callback_groups();
|
||||
std::for_each(
|
||||
group_ptrs.begin(), group_ptrs.end(),
|
||||
[this, node](rclcpp::CallbackGroup::WeakPtr group_ptr)
|
||||
rclcpp::node_interfaces::global_for_each_callback_group(
|
||||
node.get(),
|
||||
[this, node](rclcpp::CallbackGroup::SharedPtr shared_group_ptr)
|
||||
{
|
||||
auto shared_group_ptr = group_ptr.lock();
|
||||
if (shared_group_ptr && shared_group_ptr->automatically_add_to_executor_with_node() &&
|
||||
if (shared_group_ptr->automatically_add_to_executor_with_node() &&
|
||||
!shared_group_ptr->get_associated_with_executor_atomic().load())
|
||||
{
|
||||
add_callback_group(
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "rclcpp/executors/static_single_threaded_executor.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -29,7 +30,12 @@ StaticSingleThreadedExecutor::StaticSingleThreadedExecutor(
|
||||
entities_collector_ = std::make_shared<StaticExecutorEntitiesCollector>();
|
||||
}
|
||||
|
||||
StaticSingleThreadedExecutor::~StaticSingleThreadedExecutor() {}
|
||||
StaticSingleThreadedExecutor::~StaticSingleThreadedExecutor()
|
||||
{
|
||||
if (entities_collector_->is_init()) {
|
||||
entities_collector_->fini();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StaticSingleThreadedExecutor::spin()
|
||||
@@ -42,7 +48,6 @@ StaticSingleThreadedExecutor::spin()
|
||||
// Set memory_strategy_ and exec_list_ based on weak_nodes_
|
||||
// Prepare wait_set_ based on memory_strategy_
|
||||
entities_collector_->init(&wait_set_, memory_strategy_, &interrupt_guard_condition_);
|
||||
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
|
||||
|
||||
while (rclcpp::ok(this->context_) && spinning.load()) {
|
||||
// Refresh wait set and wait for work
|
||||
@@ -51,6 +56,79 @@ StaticSingleThreadedExecutor::spin()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StaticSingleThreadedExecutor::spin_some(std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
// In this context a 0 input max_duration means no duration limit
|
||||
if (std::chrono::nanoseconds(0) == max_duration) {
|
||||
max_duration = std::chrono::nanoseconds::max();
|
||||
}
|
||||
|
||||
return this->spin_some_impl(max_duration, false);
|
||||
}
|
||||
|
||||
void
|
||||
StaticSingleThreadedExecutor::spin_all(std::chrono::nanoseconds max_duration)
|
||||
{
|
||||
if (max_duration <= std::chrono::nanoseconds(0)) {
|
||||
throw std::invalid_argument("max_duration must be positive");
|
||||
}
|
||||
return this->spin_some_impl(max_duration, true);
|
||||
}
|
||||
|
||||
void
|
||||
StaticSingleThreadedExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
|
||||
{
|
||||
// Make sure the entities collector has been initialized
|
||||
if (!entities_collector_->is_init()) {
|
||||
entities_collector_->init(&wait_set_, memory_strategy_, &interrupt_guard_condition_);
|
||||
}
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
auto max_duration_not_elapsed = [max_duration, start]() {
|
||||
if (std::chrono::nanoseconds(0) == max_duration) {
|
||||
// told to spin forever if need be
|
||||
return true;
|
||||
} else if (std::chrono::steady_clock::now() - start < max_duration) {
|
||||
// told to spin only for some maximum amount of time
|
||||
return true;
|
||||
}
|
||||
// spun too long
|
||||
return false;
|
||||
};
|
||||
|
||||
if (spinning.exchange(true)) {
|
||||
throw std::runtime_error("spin_some() called while already spinning");
|
||||
}
|
||||
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
|
||||
|
||||
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
|
||||
// Get executables that are ready now
|
||||
entities_collector_->refresh_wait_set(std::chrono::milliseconds::zero());
|
||||
// Execute ready executables
|
||||
bool work_available = execute_ready_executables();
|
||||
if (!work_available || !exhaustive) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StaticSingleThreadedExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
// Make sure the entities collector has been initialized
|
||||
if (!entities_collector_->is_init()) {
|
||||
entities_collector_->init(&wait_set_, memory_strategy_, &interrupt_guard_condition_);
|
||||
}
|
||||
|
||||
if (rclcpp::ok(context_) && spinning.load()) {
|
||||
// Wait until we have a ready entity or timeout expired
|
||||
entities_collector_->refresh_wait_set(timeout);
|
||||
// Execute ready executables
|
||||
execute_ready_executables(true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
StaticSingleThreadedExecutor::add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
@@ -138,14 +216,20 @@ StaticSingleThreadedExecutor::remove_node(std::shared_ptr<rclcpp::Node> node_ptr
|
||||
this->remove_node(node_ptr->get_node_base_interface(), notify);
|
||||
}
|
||||
|
||||
void
|
||||
StaticSingleThreadedExecutor::execute_ready_executables()
|
||||
bool
|
||||
StaticSingleThreadedExecutor::execute_ready_executables(bool spin_once)
|
||||
{
|
||||
bool any_ready_executable = false;
|
||||
|
||||
// Execute all the ready subscriptions
|
||||
for (size_t i = 0; i < wait_set_.size_of_subscriptions; ++i) {
|
||||
if (i < entities_collector_->get_number_of_subscriptions()) {
|
||||
if (wait_set_.subscriptions[i]) {
|
||||
execute_subscription(entities_collector_->get_subscription(i));
|
||||
if (spin_once) {
|
||||
return true;
|
||||
}
|
||||
any_ready_executable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,6 +238,10 @@ StaticSingleThreadedExecutor::execute_ready_executables()
|
||||
if (i < entities_collector_->get_number_of_timers()) {
|
||||
if (wait_set_.timers[i] && entities_collector_->get_timer(i)->is_ready()) {
|
||||
execute_timer(entities_collector_->get_timer(i));
|
||||
if (spin_once) {
|
||||
return true;
|
||||
}
|
||||
any_ready_executable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -162,6 +250,10 @@ StaticSingleThreadedExecutor::execute_ready_executables()
|
||||
if (i < entities_collector_->get_number_of_services()) {
|
||||
if (wait_set_.services[i]) {
|
||||
execute_service(entities_collector_->get_service(i));
|
||||
if (spin_once) {
|
||||
return true;
|
||||
}
|
||||
any_ready_executable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -170,6 +262,10 @@ StaticSingleThreadedExecutor::execute_ready_executables()
|
||||
if (i < entities_collector_->get_number_of_clients()) {
|
||||
if (wait_set_.clients[i]) {
|
||||
execute_client(entities_collector_->get_client(i));
|
||||
if (spin_once) {
|
||||
return true;
|
||||
}
|
||||
any_ready_executable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -179,6 +275,11 @@ StaticSingleThreadedExecutor::execute_ready_executables()
|
||||
if (waitable->is_ready(&wait_set_)) {
|
||||
auto data = waitable->take_data();
|
||||
waitable->execute(data);
|
||||
if (spin_once) {
|
||||
return true;
|
||||
}
|
||||
any_ready_executable = true;
|
||||
}
|
||||
}
|
||||
return any_ready_executable;
|
||||
}
|
||||
|
||||
34
rclcpp/src/rclcpp/generic_publisher.cpp
Normal file
34
rclcpp/src/rclcpp/generic_publisher.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2018, Bosch Software Innovations GmbH.
|
||||
// Copyright 2021, Apex.AI 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/generic_publisher.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
void GenericPublisher::publish(const rclcpp::SerializedMessage & message)
|
||||
{
|
||||
auto return_code = rcl_publish_serialized_message(
|
||||
get_publisher_handle().get(), &message.get_rcl_serialized_message(), NULL);
|
||||
|
||||
if (return_code != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(return_code, "failed to publish serialized message");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
67
rclcpp/src/rclcpp/generic_subscription.cpp
Normal file
67
rclcpp/src/rclcpp/generic_subscription.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2018, Bosch Software Innovations GmbH.
|
||||
// Copyright 2021, Apex.AI 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/generic_subscription.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rcl/subscription.h"
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
std::shared_ptr<void> GenericSubscription::create_message()
|
||||
{
|
||||
return create_serialized_message();
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::SerializedMessage> GenericSubscription::create_serialized_message()
|
||||
{
|
||||
return std::make_shared<rclcpp::SerializedMessage>(0);
|
||||
}
|
||||
|
||||
void GenericSubscription::handle_message(
|
||||
std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
(void) message_info;
|
||||
auto typed_message = std::static_pointer_cast<rclcpp::SerializedMessage>(message);
|
||||
callback_(typed_message);
|
||||
}
|
||||
|
||||
void GenericSubscription::handle_loaned_message(
|
||||
void * message, const rclcpp::MessageInfo & message_info)
|
||||
{
|
||||
(void) message;
|
||||
(void) message_info;
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"handle_loaned_message is not implemented for GenericSubscription");
|
||||
}
|
||||
|
||||
void GenericSubscription::return_message(std::shared_ptr<void> & message)
|
||||
{
|
||||
auto typed_message = std::static_pointer_cast<rclcpp::SerializedMessage>(message);
|
||||
return_serialized_message(typed_message);
|
||||
}
|
||||
|
||||
void GenericSubscription::return_serialized_message(
|
||||
std::shared_ptr<rclcpp::SerializedMessage> & message)
|
||||
{
|
||||
message.reset();
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
84
rclcpp/src/rclcpp/network_flow_endpoint.cpp
Normal file
84
rclcpp/src/rclcpp/network_flow_endpoint.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2020 Ericsson AB
|
||||
//
|
||||
// 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 <string>
|
||||
|
||||
#include "rclcpp/network_flow_endpoint.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
const std::string &
|
||||
NetworkFlowEndpoint::transport_protocol() const
|
||||
{
|
||||
return transport_protocol_;
|
||||
}
|
||||
|
||||
const std::string &
|
||||
NetworkFlowEndpoint::internet_protocol() const
|
||||
{
|
||||
return internet_protocol_;
|
||||
}
|
||||
|
||||
uint16_t NetworkFlowEndpoint::transport_port() const
|
||||
{
|
||||
return transport_port_;
|
||||
}
|
||||
|
||||
uint32_t NetworkFlowEndpoint::flow_label() const
|
||||
{
|
||||
return flow_label_;
|
||||
}
|
||||
|
||||
uint8_t NetworkFlowEndpoint::dscp() const
|
||||
{
|
||||
return dscp_;
|
||||
}
|
||||
|
||||
const std::string &
|
||||
NetworkFlowEndpoint::internet_address() const
|
||||
{
|
||||
return internet_address_;
|
||||
}
|
||||
|
||||
bool operator==(const NetworkFlowEndpoint & left, const NetworkFlowEndpoint & right)
|
||||
{
|
||||
return left.transport_protocol_ == right.transport_protocol_ &&
|
||||
left.internet_protocol_ == right.internet_protocol_ &&
|
||||
left.transport_port_ == right.transport_port_ &&
|
||||
left.flow_label_ == right.flow_label_ &&
|
||||
left.dscp_ == right.dscp_ &&
|
||||
left.internet_address_ == right.internet_address_;
|
||||
}
|
||||
|
||||
bool operator!=(const NetworkFlowEndpoint & left, const NetworkFlowEndpoint & right)
|
||||
{
|
||||
return !(left == right);
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, const NetworkFlowEndpoint & network_flow_endpoint)
|
||||
{
|
||||
// Stream out in JSON-like format
|
||||
os << "{" <<
|
||||
"\"transportProtocol\": \"" << network_flow_endpoint.transport_protocol_ << "\", " <<
|
||||
"\"internetProtocol\": \"" << network_flow_endpoint.internet_protocol_ << "\", " <<
|
||||
"\"transportPort\": \"" << network_flow_endpoint.transport_port_ << "\", " <<
|
||||
"\"flowLabel\": \"" << std::to_string(network_flow_endpoint.flow_label_) << "\", " <<
|
||||
"\"dscp\": \"" << std::to_string(network_flow_endpoint.dscp_) << "\", " <<
|
||||
"\"internetAddress\": \"" << network_flow_endpoint.internet_address_ << "\"" <<
|
||||
"}";
|
||||
return os;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -605,3 +605,9 @@ Node::get_node_options() const
|
||||
{
|
||||
return this->node_options_;
|
||||
}
|
||||
|
||||
void Node::for_each_callback_group(
|
||||
const node_interfaces::NodeBaseInterface::CallbackGroupFunction & func)
|
||||
{
|
||||
rclcpp::node_interfaces::global_for_each_callback_group(node_base_.get(), func);
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ using rclcpp::exceptions::throw_from_rcl_error;
|
||||
|
||||
using rclcpp::node_interfaces::NodeBase;
|
||||
|
||||
using rclcpp::node_interfaces::map_of_mutexes;
|
||||
|
||||
NodeBase::NodeBase(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
@@ -46,6 +48,9 @@ NodeBase::NodeBase(
|
||||
associated_with_executor_(false),
|
||||
notify_guard_condition_is_valid_(false)
|
||||
{
|
||||
// Generate a mutex for this instance of NodeBase
|
||||
NodeBase::map_object.create_mutex_of_nodebase(this);
|
||||
|
||||
// Setup the guard condition that is notified when changes occur in the graph.
|
||||
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
@@ -166,6 +171,8 @@ NodeBase::~NodeBase()
|
||||
"failed to destroy guard condition: %s", rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
|
||||
NodeBase::map_object.delete_mutex_of_nodebase(this);
|
||||
}
|
||||
|
||||
const char *
|
||||
@@ -221,12 +228,11 @@ NodeBase::create_callback_group(
|
||||
rclcpp::CallbackGroupType group_type,
|
||||
bool automatically_add_to_executor_with_node)
|
||||
{
|
||||
using rclcpp::CallbackGroup;
|
||||
using rclcpp::CallbackGroupType;
|
||||
auto group = CallbackGroup::SharedPtr(
|
||||
new CallbackGroup(
|
||||
group_type,
|
||||
automatically_add_to_executor_with_node));
|
||||
auto group = std::make_shared<rclcpp::CallbackGroup>(
|
||||
group_type,
|
||||
automatically_add_to_executor_with_node);
|
||||
auto mutex_ptr = NodeBase::map_object.get_mutex_of_nodebase(this);
|
||||
std::lock_guard<std::mutex> lock(*mutex_ptr);
|
||||
callback_groups_.push_back(group);
|
||||
return group;
|
||||
}
|
||||
@@ -240,14 +246,16 @@ NodeBase::get_default_callback_group()
|
||||
bool
|
||||
NodeBase::callback_group_in_node(rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
bool group_belongs_to_this_node = false;
|
||||
auto mutex_ptr = NodeBase::map_object.get_mutex_of_nodebase(this);
|
||||
std::lock_guard<std::mutex> lock(*mutex_ptr);
|
||||
|
||||
for (auto & weak_group : this->callback_groups_) {
|
||||
auto cur_group = weak_group.lock();
|
||||
if (cur_group && (cur_group == group)) {
|
||||
group_belongs_to_this_node = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return group_belongs_to_this_node;
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::vector<rclcpp::CallbackGroup::WeakPtr> &
|
||||
@@ -310,3 +318,41 @@ NodeBase::resolve_topic_or_service_name(
|
||||
allocator.deallocate(output_cstr, allocator.state);
|
||||
return output;
|
||||
}
|
||||
|
||||
map_of_mutexes NodeBase::map_object = map_of_mutexes();
|
||||
|
||||
void map_of_mutexes::create_mutex_of_nodebase(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface * nodebase)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(this->internal_mutex_);
|
||||
this->data_.emplace(nodebase, std::make_shared<std::mutex>() );
|
||||
}
|
||||
|
||||
std::shared_ptr<std::mutex> map_of_mutexes::get_mutex_of_nodebase(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface * nodebase)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(this->internal_mutex_);
|
||||
return this->data_[nodebase];
|
||||
}
|
||||
|
||||
void map_of_mutexes::delete_mutex_of_nodebase(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface * nodebase)
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(this->internal_mutex_);
|
||||
this->data_.erase(nodebase);
|
||||
}
|
||||
|
||||
// For each callback group free function implementation
|
||||
void rclcpp::node_interfaces::global_for_each_callback_group(
|
||||
NodeBaseInterface * node_base_interface, const NodeBaseInterface::CallbackGroupFunction & func)
|
||||
{
|
||||
auto mutex_ptr = NodeBase::map_object.get_mutex_of_nodebase(node_base_interface);
|
||||
std::lock_guard<std::mutex> lock(*mutex_ptr);
|
||||
|
||||
for (const auto & weak_group : node_base_interface->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (group) {
|
||||
func(group);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -299,14 +299,15 @@ __set_parameters_atomically_common(
|
||||
const OnParametersSetCallbackType & callback,
|
||||
bool allow_undeclared = false)
|
||||
{
|
||||
// Call the user callback to see if the new value(s) are allowed.
|
||||
rcl_interfaces::msg::SetParametersResult result =
|
||||
__call_on_parameters_set_callbacks(parameters, callback_container, callback);
|
||||
// Check if the value being set complies with the descriptor.
|
||||
rcl_interfaces::msg::SetParametersResult result = __check_parameters(
|
||||
parameter_infos, parameters, allow_undeclared);
|
||||
if (!result.successful) {
|
||||
return result;
|
||||
}
|
||||
// Check if the value being set complies with the descriptor.
|
||||
result = __check_parameters(parameter_infos, parameters, allow_undeclared);
|
||||
// Call the user callback to see if the new value(s) are allowed.
|
||||
result =
|
||||
__call_on_parameters_set_callbacks(parameters, callback_container, callback);
|
||||
if (!result.successful) {
|
||||
return result;
|
||||
}
|
||||
@@ -348,6 +349,21 @@ __declare_parameter_common(
|
||||
initial_value = &overrides_it->second;
|
||||
}
|
||||
|
||||
// If there is no initial value, then skip initialization
|
||||
if (initial_value->get_type() == rclcpp::PARAMETER_NOT_SET) {
|
||||
// Add declared parameters to storage (without a value)
|
||||
parameter_infos[name].descriptor.name = name;
|
||||
if (parameter_descriptor.dynamic_typing) {
|
||||
parameter_infos[name].descriptor.type = rclcpp::PARAMETER_NOT_SET;
|
||||
} else {
|
||||
parameter_infos[name].descriptor.type = parameter_descriptor.type;
|
||||
}
|
||||
parameters_out[name] = parameter_infos.at(name);
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
result.successful = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
// Check with the user's callback to see if the initial value can be set.
|
||||
std::vector<rclcpp::Parameter> parameter_wrappers {rclcpp::Parameter(name, *initial_value)};
|
||||
// This function also takes care of default vs initial value.
|
||||
@@ -412,14 +428,6 @@ declare_parameter_helper(
|
||||
parameter_descriptor.type = static_cast<uint8_t>(type);
|
||||
}
|
||||
|
||||
if (
|
||||
rclcpp::PARAMETER_NOT_SET == default_value.get_type() &&
|
||||
overrides.find(name) == overrides.end() &&
|
||||
parameter_descriptor.dynamic_typing == false)
|
||||
{
|
||||
throw rclcpp::exceptions::NoParameterOverrideProvided(name);
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ParameterEvent parameter_event;
|
||||
auto result = __declare_parameter_common(
|
||||
name,
|
||||
@@ -805,14 +813,21 @@ NodeParameters::get_parameters(const std::vector<std::string> & names) const
|
||||
rclcpp::Parameter
|
||||
NodeParameters::get_parameter(const std::string & name) const
|
||||
{
|
||||
rclcpp::Parameter parameter;
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
|
||||
if (get_parameter(name, parameter)) {
|
||||
return parameter;
|
||||
auto param_iter = parameters_.find(name);
|
||||
if (
|
||||
parameters_.end() != param_iter &&
|
||||
(param_iter->second.value.get_type() != rclcpp::ParameterType::PARAMETER_NOT_SET ||
|
||||
param_iter->second.descriptor.dynamic_typing))
|
||||
{
|
||||
return rclcpp::Parameter{name, param_iter->second.value};
|
||||
} else if (this->allow_undeclared_) {
|
||||
return parameter;
|
||||
} else {
|
||||
return rclcpp::Parameter{};
|
||||
} else if (parameters_.end() == param_iter) {
|
||||
throw rclcpp::exceptions::ParameterNotDeclaredException(name);
|
||||
} else {
|
||||
throw rclcpp::exceptions::ParameterUninitializedException(name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -954,22 +969,6 @@ NodeParameters::list_parameters(const std::vector<std::string> & prefixes, uint6
|
||||
return result;
|
||||
}
|
||||
|
||||
struct HandleCompare
|
||||
: public std::unary_function<OnSetParametersCallbackHandle::WeakPtr, bool>
|
||||
{
|
||||
explicit HandleCompare(const OnSetParametersCallbackHandle * const base)
|
||||
: base_(base) {}
|
||||
bool operator()(const OnSetParametersCallbackHandle::WeakPtr & handle)
|
||||
{
|
||||
auto shared_handle = handle.lock();
|
||||
if (base_ == shared_handle.get()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const OnSetParametersCallbackHandle * const base_;
|
||||
};
|
||||
|
||||
void
|
||||
NodeParameters::remove_on_set_parameters_callback(
|
||||
const OnSetParametersCallbackHandle * const handle)
|
||||
@@ -980,7 +979,9 @@ NodeParameters::remove_on_set_parameters_callback(
|
||||
auto it = std::find_if(
|
||||
on_parameters_set_callback_container_.begin(),
|
||||
on_parameters_set_callback_container_.end(),
|
||||
HandleCompare(handle));
|
||||
[handle](const auto & weak_handle) {
|
||||
return handle == weak_handle.lock().get();
|
||||
});
|
||||
if (it != on_parameters_set_callback_container_.end()) {
|
||||
on_parameters_set_callback_container_.erase(it);
|
||||
} else {
|
||||
|
||||
@@ -42,7 +42,7 @@ AsyncParametersClient::AsyncParametersClient(
|
||||
if (remote_node_name != "") {
|
||||
remote_node_name_ = remote_node_name;
|
||||
} else {
|
||||
remote_node_name_ = node_base_interface->get_name();
|
||||
remote_node_name_ = node_base_interface->get_fully_qualified_name();
|
||||
}
|
||||
|
||||
rcl_client_options_t options = rcl_client_get_default_options();
|
||||
@@ -273,6 +273,55 @@ AsyncParametersClient::set_parameters_atomically(
|
||||
return future_result;
|
||||
}
|
||||
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
|
||||
AsyncParametersClient::delete_parameters(
|
||||
const std::vector<std::string> & parameters_names)
|
||||
{
|
||||
std::vector<rclcpp::Parameter> parameters;
|
||||
for (const std::string & name : parameters_names) {
|
||||
parameters.push_back(rclcpp::Parameter(name));
|
||||
}
|
||||
auto future_result = set_parameters(parameters);
|
||||
|
||||
return future_result;
|
||||
}
|
||||
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
|
||||
AsyncParametersClient::load_parameters(
|
||||
const std::string & yaml_filename)
|
||||
{
|
||||
rclcpp::ParameterMap parameter_map = rclcpp::parameter_map_from_yaml_file(yaml_filename);
|
||||
return this->load_parameters(parameter_map);
|
||||
}
|
||||
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
|
||||
AsyncParametersClient::load_parameters(
|
||||
const rclcpp::ParameterMap & parameter_map)
|
||||
{
|
||||
std::vector<rclcpp::Parameter> parameters;
|
||||
std::string remote_name = remote_node_name_.substr(remote_node_name_.substr(1).find("/") + 2);
|
||||
for (const auto & params : parameter_map) {
|
||||
std::string node_full_name = params.first;
|
||||
std::string node_name = node_full_name.substr(node_full_name.find("/*/") + 3);
|
||||
if (node_full_name == remote_node_name_ ||
|
||||
node_full_name == "/**" ||
|
||||
(node_name == remote_name))
|
||||
{
|
||||
for (const auto & param : params.second) {
|
||||
parameters.push_back(param);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parameters.size() == 0) {
|
||||
throw rclcpp::exceptions::InvalidParametersException("No valid parameter");
|
||||
}
|
||||
auto future_result = set_parameters(parameters);
|
||||
|
||||
return future_result;
|
||||
}
|
||||
|
||||
|
||||
std::shared_future<rcl_interfaces::msg::ListParametersResult>
|
||||
AsyncParametersClient::list_parameters(
|
||||
const std::vector<std::string> & prefixes,
|
||||
@@ -420,6 +469,42 @@ SyncParametersClient::set_parameters(
|
||||
return std::vector<rcl_interfaces::msg::SetParametersResult>();
|
||||
}
|
||||
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
SyncParametersClient::delete_parameters(
|
||||
const std::vector<std::string> & parameters_names,
|
||||
std::chrono::nanoseconds timeout)
|
||||
{
|
||||
auto f = async_parameters_client_->delete_parameters(parameters_names);
|
||||
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (
|
||||
spin_node_until_future_complete(
|
||||
*executor_, node_base_interface_, f,
|
||||
timeout) == rclcpp::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
}
|
||||
return std::vector<rcl_interfaces::msg::SetParametersResult>();
|
||||
}
|
||||
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
SyncParametersClient::load_parameters(
|
||||
const std::string & yaml_filename,
|
||||
std::chrono::nanoseconds timeout)
|
||||
{
|
||||
auto f = async_parameters_client_->load_parameters(yaml_filename);
|
||||
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (
|
||||
spin_node_until_future_complete(
|
||||
*executor_, node_base_interface_, f,
|
||||
timeout) == rclcpp::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
}
|
||||
return std::vector<rcl_interfaces::msg::SetParametersResult>();
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
SyncParametersClient::set_parameters_atomically(
|
||||
const std::vector<rclcpp::Parameter> & parameters,
|
||||
|
||||
@@ -37,23 +37,6 @@ ParameterEventHandler::add_parameter_event_callback(
|
||||
return handle;
|
||||
}
|
||||
|
||||
template<typename CallbackHandleT>
|
||||
struct HandleCompare
|
||||
: public std::unary_function<typename CallbackHandleT::WeakPtr, bool>
|
||||
{
|
||||
explicit HandleCompare(const CallbackHandleT * const base)
|
||||
: base_(base) {}
|
||||
bool operator()(const typename CallbackHandleT::WeakPtr & handle)
|
||||
{
|
||||
auto shared_handle = handle.lock();
|
||||
if (base_ == shared_handle.get()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
const CallbackHandleT * const base_;
|
||||
};
|
||||
|
||||
void
|
||||
ParameterEventHandler::remove_parameter_event_callback(
|
||||
ParameterEventCallbackHandle::SharedPtr callback_handle)
|
||||
@@ -62,7 +45,9 @@ ParameterEventHandler::remove_parameter_event_callback(
|
||||
auto it = std::find_if(
|
||||
event_callbacks_.begin(),
|
||||
event_callbacks_.end(),
|
||||
HandleCompare<ParameterEventCallbackHandle>(callback_handle.get()));
|
||||
[callback_handle](const auto & weak_handle) {
|
||||
return callback_handle.get() == weak_handle.lock().get();
|
||||
});
|
||||
if (it != event_callbacks_.end()) {
|
||||
event_callbacks_.erase(it);
|
||||
} else {
|
||||
@@ -99,7 +84,9 @@ ParameterEventHandler::remove_parameter_callback(
|
||||
auto it = std::find_if(
|
||||
container.begin(),
|
||||
container.end(),
|
||||
HandleCompare<ParameterCallbackHandle>(handle));
|
||||
[handle](const auto & weak_handle) {
|
||||
return handle == weak_handle.lock().get();
|
||||
});
|
||||
if (it != container.end()) {
|
||||
container.erase(it);
|
||||
if (container.empty()) {
|
||||
@@ -171,14 +158,13 @@ ParameterEventHandler::get_parameters_from_event(
|
||||
}
|
||||
|
||||
void
|
||||
ParameterEventHandler::event_callback(
|
||||
const rcl_interfaces::msg::ParameterEvent::SharedPtr event)
|
||||
ParameterEventHandler::event_callback(const rcl_interfaces::msg::ParameterEvent & event)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex_);
|
||||
|
||||
for (auto it = parameter_callbacks_.begin(); it != parameter_callbacks_.end(); ++it) {
|
||||
rclcpp::Parameter p;
|
||||
if (get_parameter_from_event(*event, p, it->first.first, it->first.second)) {
|
||||
if (get_parameter_from_event(event, p, it->first.first, it->first.second)) {
|
||||
for (auto cb = it->second.begin(); cb != it->second.end(); ++cb) {
|
||||
auto shared_handle = cb->lock();
|
||||
if (nullptr != shared_handle) {
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "rclcpp/parameter_events_filter.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -22,7 +23,7 @@ using EventType = rclcpp::ParameterEventsFilter::EventType;
|
||||
using EventPair = rclcpp::ParameterEventsFilter::EventPair;
|
||||
|
||||
ParameterEventsFilter::ParameterEventsFilter(
|
||||
rcl_interfaces::msg::ParameterEvent::SharedPtr event,
|
||||
std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event,
|
||||
const std::vector<std::string> & names,
|
||||
const std::vector<EventType> & types)
|
||||
: event_(event)
|
||||
|
||||
@@ -126,3 +126,15 @@ rclcpp::parameter_value_from(const rcl_variant_t * const c_param_value)
|
||||
|
||||
throw InvalidParameterValueException("No parameter value set");
|
||||
}
|
||||
|
||||
ParameterMap
|
||||
rclcpp::parameter_map_from_yaml_file(const std::string & yaml_filename)
|
||||
{
|
||||
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
||||
rcl_params_t * rcl_parameters = rcl_yaml_node_struct_init(allocator);
|
||||
const char * path = yaml_filename.c_str();
|
||||
if (!rcl_parse_yaml_file(path, rcl_parameters)) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(RCL_RET_ERROR);
|
||||
}
|
||||
return rclcpp::parameter_map_from(rcl_parameters);
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "rclcpp/experimental/intra_process_manager.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/network_flow_endpoint.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
|
||||
@@ -268,3 +269,39 @@ PublisherBase::default_incompatible_qos_callback(
|
||||
get_topic_name(),
|
||||
policy_name.c_str());
|
||||
}
|
||||
|
||||
std::vector<rclcpp::NetworkFlowEndpoint> PublisherBase::get_network_flow_endpoints() const
|
||||
{
|
||||
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
||||
rcl_network_flow_endpoint_array_t network_flow_endpoint_array =
|
||||
rcl_get_zero_initialized_network_flow_endpoint_array();
|
||||
rcl_ret_t ret = rcl_publisher_get_network_flow_endpoints(
|
||||
publisher_handle_.get(), &allocator, &network_flow_endpoint_array);
|
||||
if (RCL_RET_OK != ret) {
|
||||
auto error_msg = std::string("error obtaining network flows of publisher: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
if (RCL_RET_OK !=
|
||||
rcl_network_flow_endpoint_array_fini(&network_flow_endpoint_array))
|
||||
{
|
||||
error_msg += std::string(", also error cleaning up network flow array: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, error_msg);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::NetworkFlowEndpoint> network_flow_endpoint_vector;
|
||||
for (size_t i = 0; i < network_flow_endpoint_array.size; ++i) {
|
||||
network_flow_endpoint_vector.push_back(
|
||||
rclcpp::NetworkFlowEndpoint(
|
||||
network_flow_endpoint_array.network_flow_endpoint[i]));
|
||||
}
|
||||
|
||||
ret = rcl_network_flow_endpoint_array_fini(&network_flow_endpoint_array);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error cleaning up network flow array");
|
||||
}
|
||||
|
||||
return network_flow_endpoint_vector;
|
||||
}
|
||||
|
||||
@@ -288,3 +288,40 @@ SubscriptionBase::exchange_in_use_by_wait_set_state(
|
||||
}
|
||||
throw std::runtime_error("given pointer_to_subscription_part does not match any part");
|
||||
}
|
||||
|
||||
std::vector<rclcpp::NetworkFlowEndpoint> SubscriptionBase::get_network_flow_endpoints() const
|
||||
{
|
||||
rcutils_allocator_t allocator = rcutils_get_default_allocator();
|
||||
rcl_network_flow_endpoint_array_t network_flow_endpoint_array =
|
||||
rcl_get_zero_initialized_network_flow_endpoint_array();
|
||||
rcl_ret_t ret = rcl_subscription_get_network_flow_endpoints(
|
||||
subscription_handle_.get(), &allocator, &network_flow_endpoint_array);
|
||||
if (RCL_RET_OK != ret) {
|
||||
auto error_msg = std::string("Error obtaining network flows of subscription: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
if (RCL_RET_OK !=
|
||||
rcl_network_flow_endpoint_array_fini(&network_flow_endpoint_array))
|
||||
{
|
||||
error_msg += std::string(". Also error cleaning up network flow array: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, error_msg);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::NetworkFlowEndpoint> network_flow_endpoint_vector;
|
||||
for (size_t i = 0; i < network_flow_endpoint_array.size; ++i) {
|
||||
network_flow_endpoint_vector.push_back(
|
||||
rclcpp::NetworkFlowEndpoint(
|
||||
network_flow_endpoint_array.
|
||||
network_flow_endpoint[i]));
|
||||
}
|
||||
|
||||
ret = rcl_network_flow_endpoint_array_fini(&network_flow_endpoint_array);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error cleaning up network flow array");
|
||||
}
|
||||
|
||||
return network_flow_endpoint_vector;
|
||||
}
|
||||
|
||||
@@ -216,7 +216,7 @@ void TimeSource::set_clock(
|
||||
}
|
||||
}
|
||||
|
||||
void TimeSource::clock_cb(const rosgraph_msgs::msg::Clock::SharedPtr msg)
|
||||
void TimeSource::clock_cb(std::shared_ptr<const rosgraph_msgs::msg::Clock> msg)
|
||||
{
|
||||
if (!this->ros_time_active_ && SET_TRUE == this->parameter_state_) {
|
||||
enable_ros_time();
|
||||
@@ -256,13 +256,17 @@ void TimeSource::create_clock_sub()
|
||||
false
|
||||
);
|
||||
options.callback_group = clock_callback_group_;
|
||||
rclcpp::ExecutorOptions exec_options;
|
||||
exec_options.context = node_base_->get_context();
|
||||
clock_executor_ =
|
||||
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(exec_options);
|
||||
if (!clock_executor_thread_.joinable()) {
|
||||
cancel_clock_executor_promise_ = std::promise<void>{};
|
||||
clock_executor_thread_ = std::thread(
|
||||
[this]() {
|
||||
cancel_clock_executor_promise_ = std::promise<void>{};
|
||||
auto future = cancel_clock_executor_promise_.get_future();
|
||||
clock_executor_.add_callback_group(clock_callback_group_, node_base_);
|
||||
clock_executor_.spin_until_future_complete(future);
|
||||
clock_executor_->add_callback_group(clock_callback_group_, node_base_);
|
||||
clock_executor_->spin_until_future_complete(future);
|
||||
}
|
||||
);
|
||||
}
|
||||
@@ -283,14 +287,15 @@ void TimeSource::destroy_clock_sub()
|
||||
std::lock_guard<std::mutex> guard(clock_sub_lock_);
|
||||
if (clock_executor_thread_.joinable()) {
|
||||
cancel_clock_executor_promise_.set_value();
|
||||
clock_executor_.cancel();
|
||||
clock_executor_->cancel();
|
||||
clock_executor_thread_.join();
|
||||
clock_executor_.remove_callback_group(clock_callback_group_);
|
||||
clock_executor_->remove_callback_group(clock_callback_group_);
|
||||
}
|
||||
clock_subscription_.reset();
|
||||
}
|
||||
|
||||
void TimeSource::on_parameter_event(const rcl_interfaces::msg::ParameterEvent::SharedPtr event)
|
||||
void TimeSource::on_parameter_event(
|
||||
std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event)
|
||||
{
|
||||
// Filter out events on 'use_sim_time' parameter instances in other nodes.
|
||||
if (event->node != node_base_->get_fully_qualified_name()) {
|
||||
|
||||
136
rclcpp/src/rclcpp/typesupport_helpers.cpp
Normal file
136
rclcpp/src/rclcpp/typesupport_helpers.cpp
Normal file
@@ -0,0 +1,136 @@
|
||||
// Copyright 2018, Bosch Software Innovations GmbH.
|
||||
// Copyright 2021, Apex.AI 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/typesupport_helpers.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "ament_index_cpp/get_package_prefix.hpp"
|
||||
#include "ament_index_cpp/get_resources.hpp"
|
||||
#include "rcpputils/shared_library.hpp"
|
||||
#include "rcpputils/find_library.hpp"
|
||||
#include "rosidl_runtime_cpp/message_type_support_decl.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
// Look for the library in the ament prefix paths.
|
||||
std::string get_typesupport_library_path(
|
||||
const std::string & package_name, const std::string & typesupport_identifier)
|
||||
{
|
||||
const char * dynamic_library_folder;
|
||||
#ifdef _WIN32
|
||||
dynamic_library_folder = "/bin/";
|
||||
#elif __APPLE__
|
||||
dynamic_library_folder = "/lib/";
|
||||
#else
|
||||
dynamic_library_folder = "/lib/";
|
||||
#endif
|
||||
|
||||
std::string package_prefix;
|
||||
try {
|
||||
package_prefix = ament_index_cpp::get_package_prefix(package_name);
|
||||
} catch (ament_index_cpp::PackageNotFoundError & e) {
|
||||
throw std::runtime_error(e.what());
|
||||
}
|
||||
|
||||
const std::string library_path = rcpputils::path_for_library(
|
||||
package_prefix + dynamic_library_folder,
|
||||
package_name + "__" + typesupport_identifier);
|
||||
if (library_path.empty()) {
|
||||
throw std::runtime_error(
|
||||
"Typesupport library for " + package_name + " does not exist in '" + package_prefix +
|
||||
"'.");
|
||||
}
|
||||
return library_path;
|
||||
}
|
||||
|
||||
std::tuple<std::string, std::string, std::string>
|
||||
extract_type_identifier(const std::string & full_type)
|
||||
{
|
||||
char type_separator = '/';
|
||||
auto sep_position_back = full_type.find_last_of(type_separator);
|
||||
auto sep_position_front = full_type.find_first_of(type_separator);
|
||||
if (sep_position_back == std::string::npos ||
|
||||
sep_position_back == 0 ||
|
||||
sep_position_back == full_type.length() - 1)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Message type is not of the form package/type and cannot be processed");
|
||||
}
|
||||
|
||||
std::string package_name = full_type.substr(0, sep_position_front);
|
||||
std::string middle_module = "";
|
||||
if (sep_position_back - sep_position_front > 0) {
|
||||
middle_module =
|
||||
full_type.substr(sep_position_front + 1, sep_position_back - sep_position_front - 1);
|
||||
}
|
||||
std::string type_name = full_type.substr(sep_position_back + 1);
|
||||
|
||||
return std::make_tuple(package_name, middle_module, type_name);
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
std::shared_ptr<rcpputils::SharedLibrary>
|
||||
get_typesupport_library(const std::string & type, const std::string & typesupport_identifier)
|
||||
{
|
||||
auto package_name = std::get<0>(extract_type_identifier(type));
|
||||
auto library_path = get_typesupport_library_path(package_name, typesupport_identifier);
|
||||
return std::make_shared<rcpputils::SharedLibrary>(library_path);
|
||||
}
|
||||
|
||||
const rosidl_message_type_support_t *
|
||||
get_typesupport_handle(
|
||||
const std::string & type,
|
||||
const std::string & typesupport_identifier,
|
||||
rcpputils::SharedLibrary & library)
|
||||
{
|
||||
std::string package_name;
|
||||
std::string middle_module;
|
||||
std::string type_name;
|
||||
std::tie(package_name, middle_module, type_name) = extract_type_identifier(type);
|
||||
|
||||
auto mk_error = [&package_name, &type_name](auto reason) {
|
||||
std::stringstream rcutils_dynamic_loading_error;
|
||||
rcutils_dynamic_loading_error <<
|
||||
"Something went wrong loading the typesupport library for message type " << package_name <<
|
||||
"/" << type_name << ". " << reason;
|
||||
return rcutils_dynamic_loading_error.str();
|
||||
};
|
||||
|
||||
try {
|
||||
std::string symbol_name = typesupport_identifier + "__get_message_type_support_handle__" +
|
||||
package_name + "__" + (middle_module.empty() ? "msg" : middle_module) + "__" + type_name;
|
||||
|
||||
const rosidl_message_type_support_t * (* get_ts)() = nullptr;
|
||||
// This will throw runtime_error if the symbol was not found.
|
||||
get_ts = reinterpret_cast<decltype(get_ts)>(library.get_symbol(symbol_name));
|
||||
return get_ts();
|
||||
} catch (std::runtime_error &) {
|
||||
throw std::runtime_error{mk_error("Library could not be found.")};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
nodes[i]->create_publisher<test_msgs::msg::Empty>(
|
||||
"/empty_msgs_" + std::to_string(i), rclcpp::QoS(10)));
|
||||
|
||||
auto callback = [this](test_msgs::msg::Empty::SharedPtr) {this->callback_count++;};
|
||||
auto callback = [this](test_msgs::msg::Empty::ConstSharedPtr) {this->callback_count++;};
|
||||
subscriptions.push_back(
|
||||
nodes[i]->create_subscription<test_msgs::msg::Empty>(
|
||||
"/empty_msgs_" + std::to_string(i), rclcpp::QoS(10), std::move(callback)));
|
||||
|
||||
@@ -143,6 +143,13 @@ if(TARGET test_intra_process_manager)
|
||||
)
|
||||
target_link_libraries(test_intra_process_manager ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gmock(test_intra_process_manager_with_allocators test_intra_process_manager_with_allocators.cpp)
|
||||
if(TARGET test_intra_process_manager_with_allocators)
|
||||
ament_target_dependencies(test_intra_process_manager_with_allocators
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_intra_process_manager_with_allocators ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_ring_buffer_implementation test_ring_buffer_implementation.cpp)
|
||||
if(TARGET test_ring_buffer_implementation)
|
||||
ament_target_dependencies(test_ring_buffer_implementation
|
||||
@@ -368,6 +375,21 @@ if(TARGET test_qos)
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
function(test_generic_pubsub_for_rmw_implementation)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
ament_add_gmock(test_generic_pubsub${target_suffix} test_generic_pubsub.cpp
|
||||
ENV ${rmw_implementation_env_var}
|
||||
)
|
||||
if(TARGET test_generic_pubsub${target_suffix})
|
||||
target_link_libraries(test_generic_pubsub${target_suffix} ${PROJECT_NAME})
|
||||
ament_target_dependencies(test_generic_pubsub${target_suffix}
|
||||
"rcpputils"
|
||||
"rosidl_typesupport_cpp"
|
||||
"test_msgs"
|
||||
)
|
||||
endif()
|
||||
endfunction()
|
||||
call_for_each_rmw_implementation(test_generic_pubsub_for_rmw_implementation)
|
||||
ament_add_gtest(test_qos_event test_qos_event.cpp)
|
||||
if(TARGET test_qos_event)
|
||||
ament_target_dependencies(test_qos_event
|
||||
@@ -473,6 +495,10 @@ if(TARGET test_type_support)
|
||||
)
|
||||
target_link_libraries(test_type_support ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gmock(test_typesupport_helpers test_typesupport_helpers.cpp)
|
||||
if(TARGET test_typesupport_helpers)
|
||||
target_link_libraries(test_typesupport_helpers ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_find_weak_nodes test_find_weak_nodes.cpp)
|
||||
if(TARGET test_find_weak_nodes)
|
||||
ament_target_dependencies(test_find_weak_nodes
|
||||
@@ -539,12 +565,11 @@ if(TARGET test_utilities)
|
||||
target_link_libraries(test_utilities ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_init test_init.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_init)
|
||||
ament_target_dependencies(test_init
|
||||
"rcl")
|
||||
target_link_libraries(test_init ${PROJECT_NAME})
|
||||
ament_add_gtest(test_wait_for_message test_wait_for_message.cpp)
|
||||
if(TARGET test_wait_for_message)
|
||||
ament_target_dependencies(test_wait_for_message
|
||||
"test_msgs")
|
||||
target_link_libraries(test_wait_for_message ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_interface_traits test_interface_traits.cpp
|
||||
|
||||
@@ -62,7 +62,7 @@ public:
|
||||
|
||||
const std::string topic_name = std::string("topic_") + test_name.str();
|
||||
publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, rclcpp::QoS(10));
|
||||
auto callback = [this](test_msgs::msg::Empty::SharedPtr) {this->callback_count++;};
|
||||
auto callback = [this](test_msgs::msg::Empty::ConstSharedPtr) {this->callback_count++;};
|
||||
subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, rclcpp::QoS(10), std::move(callback));
|
||||
@@ -349,6 +349,190 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteWithTimeout) {
|
||||
spinner.join();
|
||||
}
|
||||
|
||||
class TestWaitable : public rclcpp::Waitable
|
||||
{
|
||||
public:
|
||||
TestWaitable()
|
||||
{
|
||||
rcl_guard_condition_options_t guard_condition_options =
|
||||
rcl_guard_condition_get_default_options();
|
||||
|
||||
gc_ = rcl_get_zero_initialized_guard_condition();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&gc_,
|
||||
rclcpp::contexts::get_global_default_context()->get_rcl_context().get(),
|
||||
guard_condition_options);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
}
|
||||
|
||||
~TestWaitable()
|
||||
{
|
||||
rcl_ret_t ret = rcl_guard_condition_fini(&gc_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
fprintf(stderr, "failed to call rcl_guard_condition_fini\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) override
|
||||
{
|
||||
rcl_ret_t ret = rcl_wait_set_add_guard_condition(wait_set, &gc_, NULL);
|
||||
return RCL_RET_OK == ret;
|
||||
}
|
||||
|
||||
bool trigger()
|
||||
{
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(&gc_);
|
||||
return RCL_RET_OK == ret;
|
||||
}
|
||||
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set) override
|
||||
{
|
||||
(void)wait_set;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override
|
||||
{
|
||||
(void) data;
|
||||
count_++;
|
||||
std::this_thread::sleep_for(3ms);
|
||||
}
|
||||
|
||||
size_t
|
||||
get_number_of_ready_guard_conditions() override {return 1;}
|
||||
|
||||
size_t
|
||||
get_count()
|
||||
{
|
||||
return count_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t count_ = 0;
|
||||
rcl_guard_condition_t gc_;
|
||||
};
|
||||
|
||||
TYPED_TEST(TestExecutors, spinAll) {
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
auto waitable_interfaces = this->node->get_node_waitables_interface();
|
||||
auto my_waitable = std::make_shared<TestWaitable>();
|
||||
waitable_interfaces->add_waitable(my_waitable, nullptr);
|
||||
executor.add_node(this->node);
|
||||
|
||||
// Long timeout, but should not block test if spin_all works as expected as we cancel the
|
||||
// executor.
|
||||
bool spin_exited = false;
|
||||
std::thread spinner([&spin_exited, &executor, this]() {
|
||||
executor.spin_all(1s);
|
||||
executor.remove_node(this->node, true);
|
||||
spin_exited = true;
|
||||
});
|
||||
|
||||
// Do some work until sufficient calls to the waitable occur
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (
|
||||
my_waitable->get_count() <= 1 &&
|
||||
!spin_exited &&
|
||||
(std::chrono::steady_clock::now() - start < 1s))
|
||||
{
|
||||
my_waitable->trigger();
|
||||
this->publisher->publish(test_msgs::msg::Empty());
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
executor.cancel();
|
||||
start = std::chrono::steady_clock::now();
|
||||
while (!spin_exited && (std::chrono::steady_clock::now() - start) < 1s) {
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
EXPECT_LT(1u, my_waitable->get_count());
|
||||
waitable_interfaces->remove_waitable(my_waitable, nullptr);
|
||||
ASSERT_TRUE(spin_exited);
|
||||
spinner.join();
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutors, spinSome) {
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
auto waitable_interfaces = this->node->get_node_waitables_interface();
|
||||
auto my_waitable = std::make_shared<TestWaitable>();
|
||||
waitable_interfaces->add_waitable(my_waitable, nullptr);
|
||||
executor.add_node(this->node);
|
||||
|
||||
// Long timeout, doesn't block test from finishing because spin_some should exit after the
|
||||
// first one completes.
|
||||
bool spin_exited = false;
|
||||
std::thread spinner([&spin_exited, &executor, this]() {
|
||||
executor.spin_some(1s);
|
||||
executor.remove_node(this->node, true);
|
||||
spin_exited = true;
|
||||
});
|
||||
|
||||
// Do some work until sufficient calls to the waitable occur, but keep going until either
|
||||
// count becomes too large, spin exits, or the 1 second timeout completes.
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (
|
||||
my_waitable->get_count() <= 1 &&
|
||||
!spin_exited &&
|
||||
(std::chrono::steady_clock::now() - start < 1s))
|
||||
{
|
||||
my_waitable->trigger();
|
||||
this->publisher->publish(test_msgs::msg::Empty());
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
EXPECT_EQ(1u, my_waitable->get_count());
|
||||
waitable_interfaces->remove_waitable(my_waitable, nullptr);
|
||||
EXPECT_TRUE(spin_exited);
|
||||
// Cancel if it hasn't exited already.
|
||||
executor.cancel();
|
||||
|
||||
spinner.join();
|
||||
}
|
||||
|
||||
// Check spin_node_until_future_complete with node base pointer
|
||||
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodeBasePtr) {
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
|
||||
auto shared_future = future.share();
|
||||
auto ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, this->node->get_node_base_interface(), shared_future, 1s);
|
||||
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
|
||||
}
|
||||
|
||||
// Check spin_node_until_future_complete with node pointer
|
||||
TYPED_TEST(TestExecutors, testSpinNodeUntilFutureCompleteNodePtr) {
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
|
||||
auto shared_future = future.share();
|
||||
auto ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, this->node, shared_future, 1s);
|
||||
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
|
||||
}
|
||||
|
||||
// Check spin_until_future_complete can be properly interrupted.
|
||||
TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted) {
|
||||
using ExecutorType = TypeParam;
|
||||
@@ -392,186 +576,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted) {
|
||||
spinner.join();
|
||||
}
|
||||
|
||||
class TestWaitable : public rclcpp::Waitable
|
||||
{
|
||||
public:
|
||||
TestWaitable()
|
||||
{
|
||||
rcl_guard_condition_options_t guard_condition_options =
|
||||
rcl_guard_condition_get_default_options();
|
||||
|
||||
gc_ = rcl_get_zero_initialized_guard_condition();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&gc_,
|
||||
rclcpp::contexts::get_global_default_context()->get_rcl_context().get(),
|
||||
guard_condition_options);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
}
|
||||
|
||||
~TestWaitable()
|
||||
{
|
||||
rcl_ret_t ret = rcl_guard_condition_fini(&gc_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
fprintf(stderr, "failed to call rcl_guard_condition_fini\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) override
|
||||
{
|
||||
rcl_ret_t ret = rcl_wait_set_add_guard_condition(wait_set, &gc_, NULL);
|
||||
if (RCL_RET_OK != ret) {
|
||||
return false;
|
||||
}
|
||||
ret = rcl_trigger_guard_condition(&gc_);
|
||||
return RCL_RET_OK == ret;
|
||||
}
|
||||
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set) override
|
||||
{
|
||||
(void)wait_set;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override
|
||||
{
|
||||
(void) data;
|
||||
count_++;
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
size_t
|
||||
get_number_of_ready_guard_conditions() override {return 1;}
|
||||
|
||||
size_t
|
||||
get_count()
|
||||
{
|
||||
return count_;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t count_ = 0;
|
||||
rcl_guard_condition_t gc_;
|
||||
};
|
||||
|
||||
TYPED_TEST(TestExecutorsStable, spinAll) {
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
auto waitable_interfaces = this->node->get_node_waitables_interface();
|
||||
auto my_waitable = std::make_shared<TestWaitable>();
|
||||
waitable_interfaces->add_waitable(my_waitable, nullptr);
|
||||
executor.add_node(this->node);
|
||||
|
||||
// Long timeout, but should not block test if spin_all works as expected as we cancel the
|
||||
// executor.
|
||||
bool spin_exited = false;
|
||||
std::thread spinner([&spin_exited, &executor, this]() {
|
||||
executor.spin_all(1s);
|
||||
executor.remove_node(this->node, true);
|
||||
spin_exited = true;
|
||||
});
|
||||
|
||||
// Do some work until sufficient calls to the waitable occur
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (
|
||||
my_waitable->get_count() <= 1 &&
|
||||
!spin_exited &&
|
||||
(std::chrono::steady_clock::now() - start < 1s))
|
||||
{
|
||||
this->publisher->publish(test_msgs::msg::Empty());
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
executor.cancel();
|
||||
start = std::chrono::steady_clock::now();
|
||||
while (!spin_exited && (std::chrono::steady_clock::now() - start) < 1s) {
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
EXPECT_LT(1u, my_waitable->get_count());
|
||||
waitable_interfaces->remove_waitable(my_waitable, nullptr);
|
||||
ASSERT_TRUE(spin_exited);
|
||||
spinner.join();
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutorsStable, spinSome) {
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
auto waitable_interfaces = this->node->get_node_waitables_interface();
|
||||
auto my_waitable = std::make_shared<TestWaitable>();
|
||||
waitable_interfaces->add_waitable(my_waitable, nullptr);
|
||||
executor.add_node(this->node);
|
||||
|
||||
// Long timeout, doesn't block test from finishing because spin_some should exit after the
|
||||
// first one completes.
|
||||
bool spin_exited = false;
|
||||
std::thread spinner([&spin_exited, &executor, this]() {
|
||||
executor.spin_some(1s);
|
||||
executor.remove_node(this->node, true);
|
||||
spin_exited = true;
|
||||
});
|
||||
|
||||
// Do some work until sufficient calls to the waitable occur, but keep going until either
|
||||
// count becomes too large, spin exits, or the 1 second timeout completes.
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (
|
||||
my_waitable->get_count() <= 1 &&
|
||||
!spin_exited &&
|
||||
(std::chrono::steady_clock::now() - start < 1s))
|
||||
{
|
||||
this->publisher->publish(test_msgs::msg::Empty());
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
EXPECT_EQ(1u, my_waitable->get_count());
|
||||
waitable_interfaces->remove_waitable(my_waitable, nullptr);
|
||||
EXPECT_TRUE(spin_exited);
|
||||
// Cancel if it hasn't exited already.
|
||||
executor.cancel();
|
||||
|
||||
spinner.join();
|
||||
}
|
||||
|
||||
// Check spin_node_until_future_complete with node base pointer
|
||||
TYPED_TEST(TestExecutorsStable, testSpinNodeUntilFutureCompleteNodeBasePtr) {
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
|
||||
auto shared_future = future.share();
|
||||
auto ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, this->node->get_node_base_interface(), shared_future, 1s);
|
||||
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
|
||||
}
|
||||
|
||||
// Check spin_node_until_future_complete with node pointer
|
||||
TYPED_TEST(TestExecutorsStable, testSpinNodeUntilFutureCompleteNodePtr) {
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
|
||||
auto shared_future = future.share();
|
||||
auto ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, this->node, shared_future, 1s);
|
||||
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
|
||||
}
|
||||
|
||||
// Check spin_until_future_complete with node base pointer (instantiates its own executor)
|
||||
TEST(TestExecutors, testSpinUntilFutureCompleteNodeBasePtr) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
@@ -41,38 +41,38 @@ struct NumberOfEntities
|
||||
std::unique_ptr<NumberOfEntities> get_number_of_default_entities(rclcpp::Node::SharedPtr node)
|
||||
{
|
||||
auto number_of_entities = std::make_unique<NumberOfEntities>();
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
EXPECT_NE(nullptr, group);
|
||||
if (!group || !group->can_be_taken_from().load()) {
|
||||
return nullptr;
|
||||
}
|
||||
group->find_subscription_ptrs_if(
|
||||
[&number_of_entities](rclcpp::SubscriptionBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->subscriptions++; return false;
|
||||
});
|
||||
group->find_timer_ptrs_if(
|
||||
[&number_of_entities](rclcpp::TimerBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->timers++; return false;
|
||||
});
|
||||
group->find_service_ptrs_if(
|
||||
[&number_of_entities](rclcpp::ServiceBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->services++; return false;
|
||||
});
|
||||
group->find_client_ptrs_if(
|
||||
[&number_of_entities](rclcpp::ClientBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->clients++; return false;
|
||||
});
|
||||
group->find_waitable_ptrs_if(
|
||||
[&number_of_entities](rclcpp::Waitable::SharedPtr &)
|
||||
{
|
||||
number_of_entities->waitables++; return false;
|
||||
});
|
||||
}
|
||||
node->for_each_callback_group(
|
||||
[&number_of_entities](rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
if (!group->can_be_taken_from().load()) {
|
||||
return;
|
||||
}
|
||||
group->find_subscription_ptrs_if(
|
||||
[&number_of_entities](rclcpp::SubscriptionBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->subscriptions++; return false;
|
||||
});
|
||||
group->find_timer_ptrs_if(
|
||||
[&number_of_entities](rclcpp::TimerBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->timers++; return false;
|
||||
});
|
||||
group->find_service_ptrs_if(
|
||||
[&number_of_entities](rclcpp::ServiceBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->services++; return false;
|
||||
});
|
||||
group->find_client_ptrs_if(
|
||||
[&number_of_entities](rclcpp::ClientBase::SharedPtr &)
|
||||
{
|
||||
number_of_entities->clients++; return false;
|
||||
});
|
||||
group->find_waitable_ptrs_if(
|
||||
[&number_of_entities](rclcpp::Waitable::SharedPtr &)
|
||||
{
|
||||
number_of_entities->waitables++; return false;
|
||||
});
|
||||
});
|
||||
|
||||
return number_of_entities;
|
||||
}
|
||||
@@ -255,7 +255,7 @@ TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node_with_entities) {
|
||||
// Create 1 of each entity type
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", rclcpp::QoS(10), [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", rclcpp::QoS(10), [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
auto timer =
|
||||
node->create_wall_timer(std::chrono::seconds(60), []() {});
|
||||
auto service =
|
||||
@@ -455,7 +455,7 @@ TEST_F(TestStaticExecutorEntitiesCollector, refresh_wait_set_add_handles_to_wait
|
||||
// Create 1 of each entity type
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", rclcpp::QoS(10), [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", rclcpp::QoS(10), [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
auto timer =
|
||||
node->create_wall_timer(std::chrono::seconds(60), []() {});
|
||||
auto service =
|
||||
|
||||
@@ -45,16 +45,6 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, check_unimplemented) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
executor.add_node(node);
|
||||
|
||||
EXPECT_THROW(executor.spin_some(), rclcpp::exceptions::UnimplementedError);
|
||||
EXPECT_THROW(executor.spin_all(0ns), rclcpp::exceptions::UnimplementedError);
|
||||
EXPECT_THROW(executor.spin_once(0ns), rclcpp::exceptions::UnimplementedError);
|
||||
}
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, add_callback_group_trigger_guard_failed) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
|
||||
@@ -335,7 +335,7 @@ TEST_F(TestNodeGraph, get_info_by_topic)
|
||||
{
|
||||
const rclcpp::QoS publisher_qos(1);
|
||||
auto publisher = node()->create_publisher<test_msgs::msg::Empty>("topic", publisher_qos);
|
||||
auto callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
|
||||
const rclcpp::QoS subscriber_qos(10);
|
||||
auto subscription =
|
||||
@@ -364,28 +364,10 @@ TEST_F(TestNodeGraph, get_info_by_topic)
|
||||
EXPECT_EQ(rclcpp::EndpointType::Publisher, const_publisher_endpoint_info.endpoint_type());
|
||||
|
||||
rclcpp::QoS actual_qos = publisher_endpoint_info.qos_profile();
|
||||
switch (actual_qos.get_rmw_qos_profile().history) {
|
||||
case RMW_QOS_POLICY_HISTORY_KEEP_LAST:
|
||||
EXPECT_EQ(1u, actual_qos.get_rmw_qos_profile().depth);
|
||||
break;
|
||||
case RMW_QOS_POLICY_HISTORY_UNKNOWN:
|
||||
EXPECT_EQ(0u, actual_qos.get_rmw_qos_profile().depth);
|
||||
break;
|
||||
default:
|
||||
ADD_FAILURE() << "unexpected history";
|
||||
}
|
||||
EXPECT_EQ(actual_qos.reliability(), rclcpp::ReliabilityPolicy::Reliable);
|
||||
|
||||
rclcpp::QoS const_actual_qos = const_publisher_endpoint_info.qos_profile();
|
||||
switch (const_actual_qos.get_rmw_qos_profile().history) {
|
||||
case RMW_QOS_POLICY_HISTORY_KEEP_LAST:
|
||||
EXPECT_EQ(1u, const_actual_qos.get_rmw_qos_profile().depth);
|
||||
break;
|
||||
case RMW_QOS_POLICY_HISTORY_UNKNOWN:
|
||||
EXPECT_EQ(0u, const_actual_qos.get_rmw_qos_profile().depth);
|
||||
break;
|
||||
default:
|
||||
ADD_FAILURE() << "unexpected history";
|
||||
}
|
||||
EXPECT_EQ(const_actual_qos.reliability(), rclcpp::ReliabilityPolicy::Reliable);
|
||||
|
||||
auto endpoint_gid = publisher_endpoint_info.endpoint_gid();
|
||||
auto const_endpoint_gid = const_publisher_endpoint_info.endpoint_gid();
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "rclcpp/scope_exit.hpp"
|
||||
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base.hpp"
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
|
||||
@@ -114,18 +115,17 @@ protected:
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>(name, "ns");
|
||||
|
||||
for (auto & group_weak_ptr : node->get_callback_groups()) {
|
||||
auto group = group_weak_ptr.lock();
|
||||
if (group) {
|
||||
node->for_each_callback_group(
|
||||
[](rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
group->can_be_taken_from() = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
return node;
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::Node> create_node_with_subscription(const std::string & name)
|
||||
{
|
||||
auto subscription_callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto subscription_callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
const rclcpp::QoS qos(10);
|
||||
auto node_with_subscription = create_node_with_disabled_callback_groups(name);
|
||||
|
||||
@@ -201,15 +201,13 @@ protected:
|
||||
const RclWaitSetSizes & expected)
|
||||
{
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes,
|
||||
&node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -234,15 +232,13 @@ protected:
|
||||
const RclWaitSetSizes & insufficient_capacities)
|
||||
{
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes,
|
||||
&node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -316,26 +312,22 @@ protected:
|
||||
{
|
||||
auto basic_node = create_node_with_disabled_callback_groups("basic_node");
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
|
||||
auto callback_groups = basic_node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes,
|
||||
&basic_node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
basic_node->for_each_callback_group(
|
||||
[basic_node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
basic_node->get_node_base_interface()));
|
||||
});
|
||||
auto callback_groups1 = node_with_entity1->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups1.begin(), callback_groups1.end(),
|
||||
[&weak_groups_to_nodes,
|
||||
&node_with_entity1](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node_with_entity1->for_each_callback_group(
|
||||
[node_with_entity1, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node_with_entity1->get_node_base_interface()));
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -348,26 +340,23 @@ protected:
|
||||
|
||||
auto basic_node2 = std::make_shared<rclcpp::Node>("basic_node2", "ns");
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_uncollected_nodes;
|
||||
auto callback_groups2 = basic_node2->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups2.begin(), callback_groups2.end(),
|
||||
[&weak_groups_to_uncollected_nodes,
|
||||
&basic_node2](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
basic_node2->for_each_callback_group(
|
||||
[basic_node2, &weak_groups_to_uncollected_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_uncollected_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
basic_node2->get_node_base_interface()));
|
||||
});
|
||||
auto callback_groups3 = node_with_entity2->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups3.begin(), callback_groups3.end(),
|
||||
[&weak_groups_to_uncollected_nodes,
|
||||
&node_with_entity2](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node_with_entity2->for_each_callback_group(
|
||||
[node_with_entity2,
|
||||
&weak_groups_to_uncollected_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_uncollected_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node_with_entity2->get_node_base_interface()));
|
||||
});
|
||||
rclcpp::AnyExecutable failed_result = get_next_entity_func(weak_groups_to_uncollected_nodes);
|
||||
@@ -388,24 +377,24 @@ protected:
|
||||
auto basic_node_base = basic_node->get_node_base_interface();
|
||||
auto node_base = node_with_entity->get_node_base_interface();
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
|
||||
auto callback_groups = basic_node_base->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &basic_node_base](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
rclcpp::node_interfaces::global_for_each_callback_group(
|
||||
basic_node_base.get(),
|
||||
[basic_node_base, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
basic_node_base));
|
||||
});
|
||||
callback_groups = node_base->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node_base](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
rclcpp::node_interfaces::global_for_each_callback_group(
|
||||
node_base.get(),
|
||||
[node_base, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node_base));
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -447,14 +436,13 @@ private:
|
||||
TEST_F(TestAllocatorMemoryStrategy, construct_destruct) {
|
||||
auto basic_node = create_node_with_disabled_callback_groups("basic_node");
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
|
||||
auto callback_groups = basic_node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &basic_node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
basic_node->for_each_callback_group(
|
||||
[basic_node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
basic_node->get_node_base_interface()));
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -511,8 +499,10 @@ TEST_F(TestAllocatorMemoryStrategy, number_of_entities_with_subscription) {
|
||||
RclWaitSetSizes expected_sizes = {};
|
||||
expected_sizes.size_of_subscriptions = 1;
|
||||
const std::string implementation_identifier = rmw_get_implementation_identifier();
|
||||
if (implementation_identifier == "rmw_cyclonedds_cpp") {
|
||||
// For cyclonedds, a subscription will also add an event and waitable
|
||||
if (implementation_identifier == "rmw_cyclonedds_cpp" ||
|
||||
implementation_identifier == "rmw_connextdds")
|
||||
{
|
||||
// For cyclonedds and connext, a subscription will also add an event and waitable
|
||||
expected_sizes.size_of_events += 1;
|
||||
expected_sizes.size_of_waitables += 1;
|
||||
}
|
||||
@@ -544,14 +534,13 @@ TEST_F(TestAllocatorMemoryStrategy, number_of_entities_with_timer) {
|
||||
TEST_F(TestAllocatorMemoryStrategy, add_handles_to_wait_set_bad_arguments) {
|
||||
auto node = create_node_with_subscription("subscription_node");
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -778,21 +767,20 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_subscription_out_of_scope) {
|
||||
|
||||
subscription_options.callback_group = callback_group;
|
||||
|
||||
auto subscription_callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto subscription_callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
const rclcpp::QoS qos(10);
|
||||
|
||||
auto subscription = node->create_subscription<
|
||||
test_msgs::msg::Empty, decltype(subscription_callback)>(
|
||||
"topic", qos, std::move(subscription_callback), subscription_options);
|
||||
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -819,14 +807,13 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_service_out_of_scope) {
|
||||
auto service = node->create_service<test_msgs::srv::Empty>(
|
||||
"service", std::move(service_callback), rmw_qos_profile_services_default, callback_group);
|
||||
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -841,14 +828,13 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_service_out_of_scope) {
|
||||
TEST_F(TestAllocatorMemoryStrategy, get_next_client_out_of_scope) {
|
||||
auto node = create_node_with_disabled_callback_groups("node");
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes;
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
// Force client to go out of scope and cleanup after collecting entities.
|
||||
@@ -884,14 +870,13 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_timer_out_of_scope) {
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
auto timer = node->create_wall_timer(
|
||||
std::chrono::seconds(10), []() {}, callback_group);
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -911,14 +896,13 @@ TEST_F(TestAllocatorMemoryStrategy, get_next_waitable_out_of_scope) {
|
||||
auto callback_group =
|
||||
node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
|
||||
@@ -96,7 +96,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups) {
|
||||
|
||||
const rclcpp::QoS qos(10);
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
auto callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp2 = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
options.callback_group = cb_grp2;
|
||||
@@ -139,7 +139,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, remove_callback_groups) {
|
||||
executor.add_callback_group(cb_grp, node->get_node_base_interface());
|
||||
const rclcpp::QoS qos(10);
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
auto callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp2 = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
options.callback_group = cb_grp2;
|
||||
@@ -222,7 +222,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_unallowable_callback_groups)
|
||||
|
||||
const rclcpp::QoS qos(10);
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
auto callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp2 = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive, false);
|
||||
options.callback_group = cb_grp2;
|
||||
@@ -256,7 +256,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, one_node_many_callback_groups_many_e
|
||||
timer_executor.add_callback_group(cb_grp, node->get_node_base_interface());
|
||||
const rclcpp::QoS qos(10);
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
auto callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
rclcpp::CallbackGroup::SharedPtr cb_grp2 = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive, false);
|
||||
options.callback_group = cb_grp2;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
@@ -25,42 +26,57 @@
|
||||
class TestAnySubscriptionCallback : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
TestAnySubscriptionCallback()
|
||||
: allocator_(std::make_shared<std::allocator<void>>()),
|
||||
any_subscription_callback_(allocator_) {}
|
||||
void SetUp()
|
||||
TestAnySubscriptionCallback() {}
|
||||
|
||||
static
|
||||
std::unique_ptr<test_msgs::msg::Empty>
|
||||
get_unique_ptr_msg()
|
||||
{
|
||||
msg_shared_ptr_ = std::make_shared<test_msgs::msg::Empty>();
|
||||
msg_const_shared_ptr_ = std::make_shared<const test_msgs::msg::Empty>();
|
||||
msg_unique_ptr_ = std::make_unique<test_msgs::msg::Empty>();
|
||||
return std::make_unique<test_msgs::msg::Empty>();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<std::allocator<void>> allocator_;
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty, std::allocator<void>>
|
||||
any_subscription_callback_;
|
||||
|
||||
std::shared_ptr<test_msgs::msg::Empty> msg_shared_ptr_;
|
||||
std::shared_ptr<const test_msgs::msg::Empty> msg_const_shared_ptr_;
|
||||
std::unique_ptr<test_msgs::msg::Empty> msg_unique_ptr_;
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> any_subscription_callback_;
|
||||
std::shared_ptr<test_msgs::msg::Empty> msg_shared_ptr_{std::make_shared<test_msgs::msg::Empty>()};
|
||||
rclcpp::MessageInfo message_info_;
|
||||
};
|
||||
|
||||
void construct_with_null_allocator()
|
||||
{
|
||||
// suppress deprecated function warning
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
// We need to wrap this in a function because `EXPECT_THROW` is a macro, and thinks
|
||||
// that the comma in here splits macro arguments, not the template arguments.
|
||||
rclcpp::AnySubscriptionCallback<
|
||||
test_msgs::msg::Empty, std::allocator<void>> any_subscription_callback_(nullptr);
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> any_subscription_callback(nullptr);
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
|
||||
TEST(AnySubscription, null_allocator) {
|
||||
TEST(AnySubscriptionCallback, null_allocator) {
|
||||
EXPECT_THROW(
|
||||
construct_with_null_allocator(),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, construct_destruct) {
|
||||
// Default constructor.
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc1;
|
||||
|
||||
// Constructor with allocator.
|
||||
std::allocator<void> allocator;
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc2(allocator);
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, unset_dispatch_throw) {
|
||||
@@ -68,153 +84,285 @@ TEST_F(TestAnySubscriptionCallback, unset_dispatch_throw) {
|
||||
any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_),
|
||||
std::runtime_error);
|
||||
EXPECT_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(msg_const_shared_ptr_, message_info_),
|
||||
any_subscription_callback_.dispatch_intra_process(msg_shared_ptr_, message_info_),
|
||||
std::runtime_error);
|
||||
EXPECT_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(std::move(msg_unique_ptr_), message_info_),
|
||||
any_subscription_callback_.dispatch_intra_process(get_unique_ptr_msg(), message_info_),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, set_dispatch_shared_ptr) {
|
||||
int callback_count = 0;
|
||||
auto shared_ptr_callback = [&callback_count](
|
||||
const std::shared_ptr<test_msgs::msg::Empty>) {
|
||||
callback_count++;
|
||||
};
|
||||
//
|
||||
// Parameterized test to test across all callback types and dispatch types.
|
||||
//
|
||||
|
||||
any_subscription_callback_.set(shared_ptr_callback);
|
||||
EXPECT_NO_THROW(any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
class InstanceContextImpl
|
||||
{
|
||||
public:
|
||||
InstanceContextImpl() = default;
|
||||
virtual ~InstanceContextImpl() = default;
|
||||
|
||||
// Can't convert ConstSharedPtr to SharedPtr
|
||||
EXPECT_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(msg_const_shared_ptr_, message_info_),
|
||||
std::runtime_error);
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
explicit InstanceContextImpl(rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc)
|
||||
: any_subscription_callback_(asc)
|
||||
{}
|
||||
|
||||
// Promotes Unique into SharedPtr
|
||||
EXPECT_NO_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(std::move(msg_unique_ptr_), message_info_));
|
||||
EXPECT_EQ(callback_count, 2);
|
||||
virtual
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>
|
||||
get_any_subscription_callback_to_test() const
|
||||
{
|
||||
return any_subscription_callback_;
|
||||
}
|
||||
|
||||
protected:
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> any_subscription_callback_;
|
||||
};
|
||||
|
||||
class InstanceContext
|
||||
{
|
||||
public:
|
||||
InstanceContext(const std::string & name, std::shared_ptr<InstanceContextImpl> impl)
|
||||
: name(name), impl_(impl)
|
||||
{}
|
||||
|
||||
InstanceContext(
|
||||
const std::string & name,
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc)
|
||||
: name(name), impl_(std::make_shared<InstanceContextImpl>(asc))
|
||||
{}
|
||||
|
||||
InstanceContext(const InstanceContext & other)
|
||||
: InstanceContext(other.name, other.impl_) {}
|
||||
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>
|
||||
get_any_subscription_callback_to_test() const
|
||||
{
|
||||
return impl_->get_any_subscription_callback_to_test();
|
||||
}
|
||||
|
||||
std::string name;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<InstanceContextImpl> impl_;
|
||||
};
|
||||
|
||||
class DispatchTests
|
||||
: public TestAnySubscriptionCallback,
|
||||
public ::testing::WithParamInterface<InstanceContext>
|
||||
{};
|
||||
|
||||
auto
|
||||
format_parameter(const ::testing::TestParamInfo<DispatchTests::ParamType> & info)
|
||||
{
|
||||
return info.param.name;
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, set_dispatch_shared_ptr_w_info) {
|
||||
int callback_count = 0;
|
||||
auto shared_ptr_w_info_callback = [&callback_count](
|
||||
const std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {
|
||||
callback_count++;
|
||||
};
|
||||
|
||||
any_subscription_callback_.set(shared_ptr_w_info_callback);
|
||||
|
||||
EXPECT_NO_THROW(any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
|
||||
// Can't convert ConstSharedPtr to SharedPtr
|
||||
EXPECT_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(msg_const_shared_ptr_, message_info_),
|
||||
std::runtime_error);
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
|
||||
// Promotes Unique into SharedPtr
|
||||
EXPECT_NO_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(std::move(msg_unique_ptr_), message_info_));
|
||||
EXPECT_EQ(callback_count, 2);
|
||||
// Testing dispatch with shared_ptr<MessageT> as input
|
||||
TEST_P(DispatchTests, test_inter_shared_dispatch) {
|
||||
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test();
|
||||
any_subscription_callback_to_test.dispatch(msg_shared_ptr_, message_info_);
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, set_dispatch_const_shared_ptr) {
|
||||
int callback_count = 0;
|
||||
auto const_shared_ptr_callback = [&callback_count](
|
||||
std::shared_ptr<const test_msgs::msg::Empty>) {
|
||||
callback_count++;
|
||||
};
|
||||
|
||||
any_subscription_callback_.set(const_shared_ptr_callback);
|
||||
|
||||
// Ok to promote shared_ptr to ConstSharedPtr
|
||||
EXPECT_NO_THROW(any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(msg_const_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 2);
|
||||
|
||||
// Not allowed to convert unique_ptr to const shared_ptr
|
||||
EXPECT_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(std::move(msg_unique_ptr_), message_info_),
|
||||
std::runtime_error);
|
||||
EXPECT_EQ(callback_count, 2);
|
||||
// Testing dispatch with shared_ptr<const MessageT> as input
|
||||
TEST_P(DispatchTests, test_intra_shared_dispatch) {
|
||||
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test();
|
||||
any_subscription_callback_to_test.dispatch_intra_process(msg_shared_ptr_, message_info_);
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, set_dispatch_const_shared_ptr_w_info) {
|
||||
int callback_count = 0;
|
||||
auto const_shared_ptr_callback = [&callback_count](
|
||||
std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {
|
||||
callback_count++;
|
||||
};
|
||||
|
||||
any_subscription_callback_.set(
|
||||
std::move(const_shared_ptr_callback));
|
||||
|
||||
// Ok to promote shared_ptr to ConstSharedPtr
|
||||
EXPECT_NO_THROW(any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(msg_const_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 2);
|
||||
|
||||
// Not allowed to convert unique_ptr to const shared_ptr
|
||||
EXPECT_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(std::move(msg_unique_ptr_), message_info_),
|
||||
std::runtime_error);
|
||||
EXPECT_EQ(callback_count, 2);
|
||||
// Testing dispatch with unique_ptr<MessageT> as input
|
||||
TEST_P(DispatchTests, test_intra_unique_dispatch) {
|
||||
auto any_subscription_callback_to_test = GetParam().get_any_subscription_callback_to_test();
|
||||
any_subscription_callback_to_test.dispatch_intra_process(get_unique_ptr_msg(), message_info_);
|
||||
}
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, set_dispatch_unique_ptr) {
|
||||
int callback_count = 0;
|
||||
auto unique_ptr_callback = [&callback_count](
|
||||
std::unique_ptr<test_msgs::msg::Empty>) {
|
||||
callback_count++;
|
||||
};
|
||||
// Generic classes for testing callbacks using std::bind to class methods.
|
||||
template<typename ... CallbackArgs>
|
||||
class BindContextImpl : public InstanceContextImpl
|
||||
{
|
||||
static constexpr size_t number_of_callback_args{sizeof...(CallbackArgs)};
|
||||
|
||||
any_subscription_callback_.set(unique_ptr_callback);
|
||||
public:
|
||||
using InstanceContextImpl::InstanceContextImpl;
|
||||
virtual ~BindContextImpl() = default;
|
||||
|
||||
// Message is copied into unique_ptr
|
||||
EXPECT_NO_THROW(any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
void on_message(CallbackArgs ...) const {}
|
||||
|
||||
EXPECT_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(msg_const_shared_ptr_, message_info_),
|
||||
std::runtime_error);
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>
|
||||
get_any_subscription_callback_to_test() const override
|
||||
{
|
||||
if constexpr (number_of_callback_args == 1) {
|
||||
return rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
std::bind(&BindContextImpl::on_message, this, std::placeholders::_1)
|
||||
);
|
||||
} else {
|
||||
return rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
std::bind(&BindContextImpl::on_message, this, std::placeholders::_1, std::placeholders::_2)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Unique_ptr is_moved
|
||||
EXPECT_NO_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(std::move(msg_unique_ptr_), message_info_));
|
||||
EXPECT_EQ(callback_count, 2);
|
||||
}
|
||||
template<typename ... CallbackArgs>
|
||||
class BindContext : public InstanceContext
|
||||
{
|
||||
public:
|
||||
explicit BindContext(const std::string & name)
|
||||
: InstanceContext(name, std::make_shared<BindContextImpl<CallbackArgs ...>>())
|
||||
{}
|
||||
};
|
||||
|
||||
TEST_F(TestAnySubscriptionCallback, set_dispatch_unique_ptr_w_info) {
|
||||
int callback_count = 0;
|
||||
auto unique_ptr_callback = [&callback_count](
|
||||
std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {
|
||||
callback_count++;
|
||||
};
|
||||
//
|
||||
// Versions of `const MessageT &`
|
||||
//
|
||||
void const_ref_free_func(const test_msgs::msg::Empty &) {}
|
||||
void const_ref_w_info_free_func(const test_msgs::msg::Empty &, const rclcpp::MessageInfo &) {}
|
||||
|
||||
any_subscription_callback_.set(unique_ptr_callback);
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ConstRefCallbackTests,
|
||||
DispatchTests,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext{"lambda", rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
[](const test_msgs::msg::Empty &) {})},
|
||||
InstanceContext{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
[](const test_msgs::msg::Empty &, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext{"free_function", rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
const_ref_free_func)},
|
||||
InstanceContext{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
const_ref_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<const test_msgs::msg::Empty &>("bind_method"),
|
||||
BindContext<const test_msgs::msg::Empty &, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
// Message is copied into unique_ptr
|
||||
EXPECT_NO_THROW(any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
//
|
||||
// Versions of `std::unique_ptr<MessageT, MessageDeleter>`
|
||||
//
|
||||
void unique_ptr_free_func(std::unique_ptr<test_msgs::msg::Empty>) {}
|
||||
void unique_ptr_w_info_free_func(
|
||||
std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &)
|
||||
{}
|
||||
|
||||
EXPECT_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(msg_const_shared_ptr_, message_info_),
|
||||
std::runtime_error);
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UniquePtrCallbackTests,
|
||||
DispatchTests,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext{"lambda", rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
[](std::unique_ptr<test_msgs::msg::Empty>) {})},
|
||||
InstanceContext{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
[](std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext{"free_function", rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
unique_ptr_free_func)},
|
||||
InstanceContext{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
unique_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<std::unique_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
// Unique_ptr is_moved
|
||||
EXPECT_NO_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(std::move(msg_unique_ptr_), message_info_));
|
||||
EXPECT_EQ(callback_count, 2);
|
||||
}
|
||||
//
|
||||
// Versions of `std::shared_ptr<const MessageT>`
|
||||
//
|
||||
void shared_const_ptr_free_func(std::shared_ptr<const test_msgs::msg::Empty>) {}
|
||||
void shared_const_ptr_w_info_free_func(
|
||||
std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &)
|
||||
{}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SharedConstPtrCallbackTests,
|
||||
DispatchTests,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext{"lambda", rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
[](std::shared_ptr<const test_msgs::msg::Empty>) {})},
|
||||
InstanceContext{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
[](std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext{"free_function", rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
shared_const_ptr_free_func)},
|
||||
InstanceContext{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
shared_const_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<std::shared_ptr<const test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
//
|
||||
// Versions of `const std::shared_ptr<const MessageT> &`
|
||||
//
|
||||
void const_ref_shared_const_ptr_free_func(const std::shared_ptr<const test_msgs::msg::Empty> &) {}
|
||||
void const_ref_shared_const_ptr_w_info_free_func(
|
||||
const std::shared_ptr<const test_msgs::msg::Empty> &, const rclcpp::MessageInfo &)
|
||||
{}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ConstRefSharedConstPtrCallbackTests,
|
||||
DispatchTests,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext{"lambda", rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
[](const std::shared_ptr<const test_msgs::msg::Empty> &) {})},
|
||||
InstanceContext{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
[](const std::shared_ptr<const test_msgs::msg::Empty> &, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext{"free_function", rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
const_ref_shared_const_ptr_free_func)},
|
||||
InstanceContext{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
const_ref_shared_const_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<const std::shared_ptr<const test_msgs::msg::Empty> &>("bind_method"),
|
||||
BindContext<const std::shared_ptr<const test_msgs::msg::Empty> &, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
//
|
||||
// Versions of `std::shared_ptr<MessageT>`
|
||||
//
|
||||
void shared_ptr_free_func(std::shared_ptr<test_msgs::msg::Empty>) {}
|
||||
void shared_ptr_w_info_free_func(
|
||||
std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &)
|
||||
{}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SharedPtrCallbackTests,
|
||||
DispatchTests,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext{"lambda", rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
[](std::shared_ptr<test_msgs::msg::Empty>) {})},
|
||||
InstanceContext{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
[](std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext{"free_function", rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
shared_ptr_free_func)},
|
||||
InstanceContext{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty>().set(
|
||||
shared_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<std::shared_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
@@ -42,7 +42,7 @@ TEST_F(TestCreateSubscription, create) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
const rclcpp::QoS qos(10);
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
auto callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
auto subscription =
|
||||
rclcpp::create_subscription<test_msgs::msg::Empty>(node, "topic_name", qos, callback, options);
|
||||
|
||||
@@ -55,7 +55,7 @@ TEST_F(TestCreateSubscription, create_with_overriding_options) {
|
||||
const rclcpp::QoS qos(10);
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
options.qos_overriding_options = rclcpp::QosOverridingOptions::with_default_policies();
|
||||
auto callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
auto subscription =
|
||||
rclcpp::create_subscription<test_msgs::msg::Empty>(node, "topic_name", qos, callback, options);
|
||||
|
||||
@@ -67,7 +67,7 @@ TEST_F(TestCreateSubscription, create_separated_node_topics_and_parameters) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
const rclcpp::QoS qos(10);
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
auto callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
|
||||
auto node_parameters = node->get_node_parameters_interface();
|
||||
auto node_topics = node->get_node_topics_interface();
|
||||
@@ -86,7 +86,7 @@ TEST_F(TestCreateSubscription, create_with_statistics) {
|
||||
options.topic_stats_options.publish_topic = "topic_statistics";
|
||||
options.topic_stats_options.publish_period = 5min;
|
||||
|
||||
auto callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
auto subscription =
|
||||
rclcpp::create_subscription<test_msgs::msg::Empty>(node, "topic_name", qos, callback, options);
|
||||
|
||||
|
||||
196
rclcpp/test/rclcpp/test_generic_pubsub.cpp
Normal file
196
rclcpp/test/rclcpp/test_generic_pubsub.cpp
Normal file
@@ -0,0 +1,196 @@
|
||||
// Copyright 2018, Bosch Software Innovations GmbH.
|
||||
// Copyright 2021, Apex.AI 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 <gmock/gmock.h>
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "test_msgs/message_fixtures.hpp"
|
||||
#include "test_msgs/msg/basic_types.hpp"
|
||||
|
||||
#include "rcl/graph.h"
|
||||
|
||||
#include "rclcpp/generic_publisher.hpp"
|
||||
#include "rclcpp/generic_subscription.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp/serialization.hpp"
|
||||
#include "rclcpp/serialized_message.hpp"
|
||||
|
||||
using namespace ::testing; // NOLINT
|
||||
using namespace rclcpp; // NOLINT
|
||||
|
||||
class RclcppGenericNodeFixture : public Test
|
||||
{
|
||||
public:
|
||||
RclcppGenericNodeFixture()
|
||||
{
|
||||
node_ = std::make_shared<rclcpp::Node>("pubsub");
|
||||
publisher_node_ = std::make_shared<rclcpp::Node>(
|
||||
"publisher_node",
|
||||
rclcpp::NodeOptions().start_parameter_event_publisher(false));
|
||||
}
|
||||
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void create_publisher(const std::string & topic)
|
||||
{
|
||||
auto publisher = publisher_node_->create_publisher<test_msgs::msg::Strings>(topic, 10);
|
||||
publishers_.push_back(publisher);
|
||||
}
|
||||
|
||||
std::vector<std::string> subscribe_raw_messages(
|
||||
size_t expected_messages_number, const std::string & topic_name, const std::string & type)
|
||||
{
|
||||
std::vector<std::string> messages;
|
||||
size_t counter = 0;
|
||||
auto subscription = node_->create_generic_subscription(
|
||||
topic_name, type, rclcpp::QoS(1),
|
||||
[&counter, &messages](std::shared_ptr<rclcpp::SerializedMessage> message) {
|
||||
test_msgs::msg::Strings string_message;
|
||||
rclcpp::Serialization<test_msgs::msg::Strings> serializer;
|
||||
serializer.deserialize_message(message.get(), &string_message);
|
||||
messages.push_back(string_message.string_value);
|
||||
counter++;
|
||||
});
|
||||
|
||||
while (counter < expected_messages_number) {
|
||||
rclcpp::spin_some(node_);
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
|
||||
rclcpp::SerializedMessage serialize_string_message(const std::string & message)
|
||||
{
|
||||
test_msgs::msg::Strings string_message;
|
||||
string_message.string_value = message;
|
||||
rclcpp::Serialization<test_msgs::msg::Strings> ser;
|
||||
SerializedMessage result;
|
||||
ser.serialize_message(&string_message, &result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void sleep_to_allow_topics_discovery()
|
||||
{
|
||||
// This is a short sleep to allow the node some time to discover the topic
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000));
|
||||
}
|
||||
|
||||
template<typename Condition, typename Duration>
|
||||
bool wait_for(const Condition & condition, const Duration & timeout)
|
||||
{
|
||||
using clock = std::chrono::system_clock;
|
||||
auto start = clock::now();
|
||||
while (!condition()) {
|
||||
if ((clock::now() - start) > timeout) {
|
||||
return false;
|
||||
}
|
||||
rclcpp::spin_some(node_);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::Node> node_;
|
||||
rclcpp::Node::SharedPtr publisher_node_;
|
||||
std::vector<std::shared_ptr<rclcpp::PublisherBase>> publishers_;
|
||||
};
|
||||
|
||||
|
||||
TEST_F(RclcppGenericNodeFixture, publisher_and_subscriber_work)
|
||||
{
|
||||
// We currently publish more messages because they can get lost
|
||||
std::vector<std::string> test_messages = {"Hello World", "Hello World"};
|
||||
std::string topic_name = "/string_topic";
|
||||
std::string type = "test_msgs/msg/Strings";
|
||||
|
||||
auto publisher = node_->create_generic_publisher(
|
||||
topic_name, type, rclcpp::QoS(1));
|
||||
|
||||
auto subscriber_future_ = std::async(
|
||||
std::launch::async, [this, topic_name, type] {
|
||||
return subscribe_raw_messages(1, topic_name, type);
|
||||
});
|
||||
|
||||
// TODO(karsten1987): Port 'wait_for_sub' to rclcpp
|
||||
auto allocator = node_->get_node_options().allocator();
|
||||
auto success = false;
|
||||
auto ret = rcl_wait_for_subscribers(
|
||||
node_->get_node_base_interface()->get_rcl_node_handle(),
|
||||
&allocator,
|
||||
topic_name.c_str(),
|
||||
1u,
|
||||
static_cast<rcutils_duration_value_t>(1e9),
|
||||
&success);
|
||||
ASSERT_EQ(RCL_RET_OK, ret) << rcl_get_error_string().str;
|
||||
ASSERT_TRUE(success);
|
||||
|
||||
for (const auto & message : test_messages) {
|
||||
publisher->publish(serialize_string_message(message));
|
||||
}
|
||||
|
||||
auto subscribed_messages = subscriber_future_.get();
|
||||
EXPECT_THAT(subscribed_messages, SizeIs(Not(0)));
|
||||
EXPECT_THAT(subscribed_messages[0], StrEq("Hello World"));
|
||||
}
|
||||
|
||||
TEST_F(RclcppGenericNodeFixture, generic_subscription_uses_qos)
|
||||
{
|
||||
// If the GenericSubscription does not use the provided QoS profile,
|
||||
// its request will be incompatible with the Publisher's offer and no messages will be passed.
|
||||
using namespace std::chrono_literals;
|
||||
std::string topic_name = "string_topic";
|
||||
std::string topic_type = "test_msgs/msg/Strings";
|
||||
rclcpp::QoS qos = rclcpp::SensorDataQoS();
|
||||
|
||||
auto publisher = node_->create_publisher<test_msgs::msg::Strings>(topic_name, qos);
|
||||
auto subscription = node_->create_generic_subscription(
|
||||
topic_name, topic_type, qos,
|
||||
[](std::shared_ptr<rclcpp::SerializedMessage>/* message */) {});
|
||||
auto connected = [publisher, subscription]() -> bool {
|
||||
return publisher->get_subscription_count() && subscription->get_publisher_count();
|
||||
};
|
||||
// It normally takes < 20ms, 5s chosen as "a very long time"
|
||||
ASSERT_TRUE(wait_for(connected, 5s));
|
||||
}
|
||||
|
||||
TEST_F(RclcppGenericNodeFixture, generic_publisher_uses_qos)
|
||||
{
|
||||
// If the GenericPublisher does not use the provided QoS profile,
|
||||
// its offer will be incompatible with the Subscription's request and no messages will be passed.
|
||||
using namespace std::chrono_literals;
|
||||
std::string topic_name = "string_topic";
|
||||
std::string topic_type = "test_msgs/msg/Strings";
|
||||
rclcpp::QoS qos = rclcpp::QoS(1).transient_local();
|
||||
|
||||
auto publisher = node_->create_generic_publisher(topic_name, topic_type, qos);
|
||||
auto subscription = node_->create_subscription<test_msgs::msg::Strings>(
|
||||
topic_name, qos,
|
||||
[](std::shared_ptr<test_msgs::msg::Strings>/* message */) {});
|
||||
auto connected = [publisher, subscription]() -> bool {
|
||||
return publisher->get_subscription_count() && subscription->get_publisher_count();
|
||||
};
|
||||
// It normally takes < 20ms, 5s chosen as "a very long time"
|
||||
ASSERT_TRUE(wait_for(connected, 5s));
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
// Copyright 2018 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/exceptions.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
TEST(TestInit, is_initialized) {
|
||||
EXPECT_FALSE(rclcpp::is_initialized());
|
||||
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
EXPECT_TRUE(rclcpp::is_initialized());
|
||||
|
||||
rclcpp::shutdown();
|
||||
|
||||
EXPECT_FALSE(rclcpp::is_initialized());
|
||||
}
|
||||
|
||||
TEST(TestInit, initialize_with_ros_args) {
|
||||
EXPECT_FALSE(rclcpp::is_initialized());
|
||||
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
EXPECT_TRUE(rclcpp::is_initialized());
|
||||
|
||||
rclcpp::shutdown();
|
||||
|
||||
EXPECT_FALSE(rclcpp::is_initialized());
|
||||
}
|
||||
|
||||
TEST(TestInit, initialize_with_unknown_ros_args) {
|
||||
EXPECT_FALSE(rclcpp::is_initialized());
|
||||
|
||||
const char * const argv[] = {"--ros-args", "unknown"};
|
||||
const int argc = static_cast<int>(sizeof(argv) / sizeof(const char *));
|
||||
EXPECT_THROW({rclcpp::init(argc, argv);}, rclcpp::exceptions::UnknownROSArgsError);
|
||||
|
||||
EXPECT_FALSE(rclcpp::is_initialized());
|
||||
}
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
@@ -216,7 +216,10 @@ public:
|
||||
const char * topic_name;
|
||||
};
|
||||
|
||||
template<typename MessageT>
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>>
|
||||
class SubscriptionIntraProcess : public SubscriptionIntraProcessBase
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -0,0 +1,276 @@
|
||||
// Copyright 2021 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 <gmock/gmock.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
|
||||
|
||||
// For demonstration purposes only, not necessary for allocator_traits
|
||||
static uint32_t num_allocs = 0;
|
||||
static uint32_t num_deallocs = 0;
|
||||
// A very simple custom allocator. Counts calls to allocate and deallocate.
|
||||
template<typename T = void>
|
||||
struct MyAllocator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
using size_type = std::size_t;
|
||||
using pointer = T *;
|
||||
using const_pointer = const T *;
|
||||
using difference_type = typename std::pointer_traits<pointer>::difference_type;
|
||||
|
||||
MyAllocator() noexcept
|
||||
{
|
||||
}
|
||||
|
||||
~MyAllocator() noexcept {}
|
||||
|
||||
template<typename U>
|
||||
MyAllocator(const MyAllocator<U> &) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
T * allocate(size_t size, const void * = 0)
|
||||
{
|
||||
if (size == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
num_allocs++;
|
||||
return static_cast<T *>(std::malloc(size * sizeof(T)));
|
||||
}
|
||||
|
||||
void deallocate(T * ptr, size_t size)
|
||||
{
|
||||
(void)size;
|
||||
if (!ptr) {
|
||||
return;
|
||||
}
|
||||
num_deallocs++;
|
||||
std::free(ptr);
|
||||
}
|
||||
|
||||
template<typename U>
|
||||
struct rebind
|
||||
{
|
||||
typedef MyAllocator<U> other;
|
||||
};
|
||||
};
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool operator==(
|
||||
const MyAllocator<T> &,
|
||||
const MyAllocator<U> &) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T, typename U>
|
||||
constexpr bool operator!=(
|
||||
const MyAllocator<T> &,
|
||||
const MyAllocator<U> &) noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<
|
||||
typename PublishedMessageAllocatorT,
|
||||
typename PublisherAllocatorT,
|
||||
typename SubscribedMessageAllocatorT,
|
||||
typename SubscriptionAllocatorT,
|
||||
typename MessageMemoryStrategyAllocatorT,
|
||||
typename MemoryStrategyAllocatorT,
|
||||
typename ExpectedExceptionT
|
||||
>
|
||||
void
|
||||
do_custom_allocator_test(
|
||||
PublishedMessageAllocatorT published_message_allocator,
|
||||
PublisherAllocatorT publisher_allocator,
|
||||
SubscribedMessageAllocatorT /* subscribed_message_allocator */, // intentionally unused
|
||||
SubscriptionAllocatorT subscription_allocator,
|
||||
MessageMemoryStrategyAllocatorT message_memory_strategy,
|
||||
MemoryStrategyAllocatorT memory_strategy_allocator)
|
||||
{
|
||||
using PublishedMessageAllocTraits =
|
||||
rclcpp::allocator::AllocRebind<test_msgs::msg::Empty, PublishedMessageAllocatorT>;
|
||||
using PublishedMessageAlloc = typename PublishedMessageAllocTraits::allocator_type;
|
||||
using PublishedMessageDeleter =
|
||||
rclcpp::allocator::Deleter<PublishedMessageAlloc, test_msgs::msg::Empty>;
|
||||
|
||||
using SubscribedMessageAllocTraits =
|
||||
rclcpp::allocator::AllocRebind<test_msgs::msg::Empty, SubscribedMessageAllocatorT>;
|
||||
using SubscribedMessageAlloc = typename SubscribedMessageAllocTraits::allocator_type;
|
||||
using SubscribedMessageDeleter =
|
||||
rclcpp::allocator::Deleter<SubscribedMessageAlloc, test_msgs::msg::Empty>;
|
||||
|
||||
// init and node
|
||||
auto context = std::make_shared<rclcpp::Context>();
|
||||
context->init(0, nullptr);
|
||||
auto node = std::make_shared<rclcpp::Node>(
|
||||
"custom_allocator_test",
|
||||
rclcpp::NodeOptions().context(context).use_intra_process_comms(true));
|
||||
|
||||
// publisher
|
||||
auto shared_publisher_allocator = std::make_shared<PublisherAllocatorT>(publisher_allocator);
|
||||
rclcpp::PublisherOptionsWithAllocator<PublisherAllocatorT> publisher_options;
|
||||
publisher_options.allocator = shared_publisher_allocator;
|
||||
auto publisher =
|
||||
node->create_publisher<test_msgs::msg::Empty>("custom_allocator_test", 10, publisher_options);
|
||||
|
||||
// callback for subscription
|
||||
uint32_t counter = 0;
|
||||
std::promise<std::unique_ptr<test_msgs::msg::Empty, SubscribedMessageDeleter>> received_message;
|
||||
auto received_message_future = received_message.get_future();
|
||||
auto callback =
|
||||
[&counter, &received_message](
|
||||
std::unique_ptr<test_msgs::msg::Empty, SubscribedMessageDeleter> msg)
|
||||
{
|
||||
++counter;
|
||||
received_message.set_value(std::move(msg));
|
||||
};
|
||||
|
||||
// subscription
|
||||
auto shared_subscription_allocator =
|
||||
std::make_shared<SubscriptionAllocatorT>(subscription_allocator);
|
||||
rclcpp::SubscriptionOptionsWithAllocator<SubscriptionAllocatorT> subscription_options;
|
||||
subscription_options.allocator = shared_subscription_allocator;
|
||||
auto shared_message_strategy_allocator =
|
||||
std::make_shared<MessageMemoryStrategyAllocatorT>(message_memory_strategy);
|
||||
auto msg_mem_strat = std::make_shared<
|
||||
rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
test_msgs::msg::Empty,
|
||||
MessageMemoryStrategyAllocatorT
|
||||
>
|
||||
>(shared_message_strategy_allocator);
|
||||
using CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<decltype(callback)>::type;
|
||||
auto subscriber = node->create_subscription<
|
||||
test_msgs::msg::Empty,
|
||||
decltype(callback),
|
||||
SubscriptionAllocatorT,
|
||||
CallbackMessageT,
|
||||
rclcpp::Subscription<CallbackMessageT, SubscriptionAllocatorT>,
|
||||
rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
MessageMemoryStrategyAllocatorT
|
||||
>
|
||||
>(
|
||||
"custom_allocator_test",
|
||||
10,
|
||||
std::forward<decltype(callback)>(callback),
|
||||
subscription_options,
|
||||
msg_mem_strat);
|
||||
|
||||
// executor memory strategy
|
||||
using rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy;
|
||||
auto shared_memory_strategy_allocator = std::make_shared<MemoryStrategyAllocatorT>(
|
||||
memory_strategy_allocator);
|
||||
std::shared_ptr<rclcpp::memory_strategy::MemoryStrategy> memory_strategy =
|
||||
std::make_shared<AllocatorMemoryStrategy<MemoryStrategyAllocatorT>>(
|
||||
shared_memory_strategy_allocator);
|
||||
|
||||
// executor
|
||||
rclcpp::ExecutorOptions options;
|
||||
options.memory_strategy = memory_strategy;
|
||||
options.context = context;
|
||||
rclcpp::executors::SingleThreadedExecutor executor(options);
|
||||
|
||||
executor.add_node(node);
|
||||
|
||||
// rebind message_allocator to ensure correct type
|
||||
PublishedMessageDeleter message_deleter;
|
||||
PublishedMessageAlloc rebound_message_allocator = published_message_allocator;
|
||||
rclcpp::allocator::set_allocator_for_deleter(&message_deleter, &rebound_message_allocator);
|
||||
|
||||
// allocate a message
|
||||
auto ptr = PublishedMessageAllocTraits::allocate(rebound_message_allocator, 1);
|
||||
PublishedMessageAllocTraits::construct(rebound_message_allocator, ptr);
|
||||
std::unique_ptr<test_msgs::msg::Empty, PublishedMessageDeleter> msg(ptr, message_deleter);
|
||||
|
||||
// publisher and receive
|
||||
if constexpr (std::is_same_v<ExpectedExceptionT, void>) {
|
||||
// no exception expected
|
||||
EXPECT_NO_THROW(
|
||||
{
|
||||
publisher->publish(std::move(msg));
|
||||
executor.spin_until_future_complete(received_message_future, std::chrono::seconds(10));
|
||||
});
|
||||
EXPECT_EQ(ptr, received_message_future.get().get());
|
||||
EXPECT_EQ(1u, counter);
|
||||
} else {
|
||||
// exception expected
|
||||
EXPECT_THROW(
|
||||
{
|
||||
publisher->publish(std::move(msg));
|
||||
executor.spin_until_future_complete(received_message_future, std::chrono::seconds(10));
|
||||
}, ExpectedExceptionT);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
This tests the case where a custom allocator is used correctly, i.e. the same
|
||||
custom allocator on both sides.
|
||||
*/
|
||||
TEST(TestIntraProcessManagerWithAllocators, custom_allocator) {
|
||||
using PublishedMessageAllocatorT = std::allocator<void>;
|
||||
using PublisherAllocatorT = std::allocator<void>;
|
||||
using SubscribedMessageAllocatorT = std::allocator<void>;
|
||||
using SubscriptionAllocatorT = std::allocator<void>;
|
||||
using MessageMemoryStrategyAllocatorT = std::allocator<void>;
|
||||
using MemoryStrategyAllocatorT = std::allocator<void>;
|
||||
auto allocator = std::allocator<void>();
|
||||
do_custom_allocator_test<
|
||||
PublishedMessageAllocatorT,
|
||||
PublisherAllocatorT,
|
||||
SubscribedMessageAllocatorT,
|
||||
SubscriptionAllocatorT,
|
||||
MessageMemoryStrategyAllocatorT,
|
||||
MemoryStrategyAllocatorT,
|
||||
void // no exception expected
|
||||
>(allocator, allocator, allocator, allocator, allocator, allocator);
|
||||
}
|
||||
|
||||
/*
|
||||
This tests the case where a custom allocator is used incorrectly, i.e. different
|
||||
custom allocators on both sides.
|
||||
*/
|
||||
TEST(TestIntraProcessManagerWithAllocators, custom_allocator_wrong) {
|
||||
// explicitly use a different allocator here to provoke a failure
|
||||
using PublishedMessageAllocatorT = std::allocator<void>;
|
||||
using PublisherAllocatorT = std::allocator<void>;
|
||||
using SubscribedMessageAllocatorT = MyAllocator<void>;
|
||||
using SubscriptionAllocatorT = MyAllocator<void>;
|
||||
using MessageMemoryStrategyAllocatorT = MyAllocator<void>;
|
||||
using MemoryStrategyAllocatorT = std::allocator<void>;
|
||||
auto allocator = std::allocator<void>();
|
||||
auto my_allocator = MyAllocator<void>();
|
||||
do_custom_allocator_test<
|
||||
PublishedMessageAllocatorT,
|
||||
PublisherAllocatorT,
|
||||
SubscribedMessageAllocatorT,
|
||||
SubscriptionAllocatorT,
|
||||
MessageMemoryStrategyAllocatorT,
|
||||
MemoryStrategyAllocatorT,
|
||||
std::runtime_error // expected exception
|
||||
>(allocator, allocator, my_allocator, my_allocator, my_allocator, allocator);
|
||||
}
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base.hpp"
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
|
||||
@@ -84,21 +85,20 @@ TEST_F(TestMemoryStrategy, get_subscription_by_handle) {
|
||||
rclcpp::SubscriptionBase::SharedPtr found_subscription = nullptr;
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
{
|
||||
auto callback_group =
|
||||
node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
auto subscription_callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto subscription_callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
const rclcpp::QoS qos(10);
|
||||
|
||||
{
|
||||
@@ -131,14 +131,13 @@ TEST_F(TestMemoryStrategy, get_service_by_handle) {
|
||||
rclcpp::ServiceBase::SharedPtr found_service = nullptr;
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -184,14 +183,13 @@ TEST_F(TestMemoryStrategy, get_client_by_handle) {
|
||||
rclcpp::ClientBase::SharedPtr found_client = nullptr;
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -232,14 +230,13 @@ TEST_F(TestMemoryStrategy, get_timer_by_handle) {
|
||||
rclcpp::TimerBase::SharedPtr found_timer = nullptr;
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -281,14 +278,14 @@ TEST_F(TestMemoryStrategy, get_node_by_group) {
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
auto node_handle = node->get_node_base_interface();
|
||||
auto callback_groups = node_handle->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node_handle](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
rclcpp::node_interfaces::global_for_each_callback_group(
|
||||
node_handle.get(),
|
||||
[node_handle, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node_handle));
|
||||
});
|
||||
memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -325,14 +322,13 @@ TEST_F(TestMemoryStrategy, get_group_by_subscription) {
|
||||
rclcpp::CallbackGroup::SharedPtr callback_group = nullptr;
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -344,7 +340,7 @@ TEST_F(TestMemoryStrategy, get_group_by_subscription) {
|
||||
|
||||
callback_group =
|
||||
node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
auto subscription_callback = [](const test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto subscription_callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
const rclcpp::QoS qos(10);
|
||||
|
||||
rclcpp::SubscriptionOptions subscription_options;
|
||||
@@ -379,14 +375,13 @@ TEST_F(TestMemoryStrategy, get_group_by_service) {
|
||||
rclcpp::ServiceBase::SharedPtr service = nullptr;
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -424,14 +419,13 @@ TEST_F(TestMemoryStrategy, get_group_by_client) {
|
||||
rclcpp::ClientBase::SharedPtr client = nullptr;
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -464,14 +458,13 @@ TEST_F(TestMemoryStrategy, get_group_by_timer) {
|
||||
rclcpp::TimerBase::SharedPtr timer = nullptr;
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
@@ -504,14 +497,13 @@ TEST_F(TestMemoryStrategy, get_group_by_waitable) {
|
||||
rclcpp::Waitable::SharedPtr waitable = nullptr;
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
auto callback_groups = node->get_node_base_interface()->get_callback_groups();
|
||||
std::for_each(
|
||||
callback_groups.begin(), callback_groups.end(),
|
||||
[&weak_groups_to_nodes, &node](rclcpp::CallbackGroup::WeakPtr weak_group_ptr) {
|
||||
node->for_each_callback_group(
|
||||
[node, &weak_groups_to_nodes](rclcpp::CallbackGroup::SharedPtr group_ptr)
|
||||
{
|
||||
weak_groups_to_nodes.insert(
|
||||
std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>(
|
||||
weak_group_ptr,
|
||||
group_ptr,
|
||||
node->get_node_base_interface()));
|
||||
});
|
||||
memory_strategy()->collect_entities(weak_groups_to_nodes);
|
||||
|
||||
@@ -333,9 +333,14 @@ TEST_F(TestNode, declare_parameter_with_no_initial_values) {
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor;
|
||||
descriptor.dynamic_typing = true;
|
||||
// no default, no initial
|
||||
const std::string parameter_name = "parameter"_unq;
|
||||
rclcpp::ParameterValue value = node->declare_parameter(
|
||||
"parameter"_unq, rclcpp::ParameterValue{}, descriptor);
|
||||
parameter_name, rclcpp::ParameterValue{}, descriptor);
|
||||
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_NOT_SET);
|
||||
// Does not throw if unset before access
|
||||
EXPECT_EQ(
|
||||
rclcpp::PARAMETER_NOT_SET,
|
||||
node->get_parameter(parameter_name).get_parameter_value().get_type());
|
||||
}
|
||||
{
|
||||
// int default, no initial
|
||||
@@ -2591,7 +2596,7 @@ TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
|
||||
false
|
||||
};
|
||||
rclcpp::QoS qos2 = rclcpp::QoS(qos_initialization2, rmw_qos_profile_default2);
|
||||
auto callback = [](const test_msgs::msg::BasicTypes::SharedPtr msg) {
|
||||
auto callback = [](test_msgs::msg::BasicTypes::ConstSharedPtr msg) {
|
||||
(void)msg;
|
||||
};
|
||||
auto subscriber =
|
||||
@@ -2637,13 +2642,33 @@ TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
|
||||
|
||||
TEST_F(TestNode, callback_groups) {
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
size_t num_callback_groups_in_basic_node = node->get_callback_groups().size();
|
||||
size_t num_callback_groups_in_basic_node = 0;
|
||||
node->for_each_callback_group(
|
||||
[&num_callback_groups_in_basic_node](rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
(void)group;
|
||||
num_callback_groups_in_basic_node++;
|
||||
});
|
||||
|
||||
auto group1 = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
EXPECT_EQ(1u + num_callback_groups_in_basic_node, node->get_callback_groups().size());
|
||||
size_t num_callback_groups = 0;
|
||||
node->for_each_callback_group(
|
||||
[&num_callback_groups](rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
(void)group;
|
||||
num_callback_groups++;
|
||||
});
|
||||
EXPECT_EQ(1u + num_callback_groups_in_basic_node, num_callback_groups);
|
||||
|
||||
auto group2 = node->create_callback_group(rclcpp::CallbackGroupType::Reentrant);
|
||||
EXPECT_EQ(2u + num_callback_groups_in_basic_node, node->get_callback_groups().size());
|
||||
size_t num_callback_groups2 = 0;
|
||||
node->for_each_callback_group(
|
||||
[&num_callback_groups2](rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
(void)group;
|
||||
num_callback_groups2++;
|
||||
});
|
||||
EXPECT_EQ(2u + num_callback_groups_in_basic_node, num_callback_groups2);
|
||||
}
|
||||
|
||||
// This is tested more thoroughly in node_interfaces/test_node_graph
|
||||
@@ -2761,9 +2786,20 @@ TEST_F(TestNode, static_and_dynamic_typing) {
|
||||
EXPECT_EQ("hello!", param);
|
||||
}
|
||||
{
|
||||
auto param = node->declare_parameter("integer_override_not_given", rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(rclcpp::PARAMETER_NOT_SET, param.get_type());
|
||||
// Throws if not set before access
|
||||
EXPECT_THROW(
|
||||
node->declare_parameter("integer_override_not_given", rclcpp::PARAMETER_INTEGER),
|
||||
rclcpp::exceptions::NoParameterOverrideProvided);
|
||||
node->get_parameter("integer_override_not_given"),
|
||||
rclcpp::exceptions::ParameterUninitializedException);
|
||||
}
|
||||
{
|
||||
auto param = node->declare_parameter("integer_set_after_declare", rclcpp::PARAMETER_INTEGER);
|
||||
EXPECT_EQ(rclcpp::PARAMETER_NOT_SET, param.get_type());
|
||||
auto result = node->set_parameter(rclcpp::Parameter{"integer_set_after_declare", 44});
|
||||
ASSERT_TRUE(result.successful) << result.reason;
|
||||
auto get_param = node->get_parameter("integer_set_after_declare");
|
||||
EXPECT_EQ(44, get_param.as_int());
|
||||
}
|
||||
{
|
||||
EXPECT_THROW(
|
||||
|
||||
@@ -33,7 +33,7 @@ using namespace std::chrono_literals;
|
||||
class TestParameterClient : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void OnMessage(const rcl_interfaces::msg::ParameterEvent::SharedPtr event)
|
||||
void OnMessage(rcl_interfaces::msg::ParameterEvent::ConstSharedPtr event)
|
||||
{
|
||||
(void)event;
|
||||
}
|
||||
@@ -861,3 +861,90 @@ TEST_F(TestParameterClient, sync_parameter_describe_parameters_allow_undeclared)
|
||||
ASSERT_FALSE(parameter_descs[1].read_only);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Coverage for async delete_parameters
|
||||
*/
|
||||
TEST_F(TestParameterClient, async_parameter_delete_parameters) {
|
||||
auto asynchronous_client =
|
||||
std::make_shared<rclcpp::AsyncParametersClient>(node_with_option);
|
||||
// set parameter
|
||||
auto set_future = asynchronous_client->set_parameters({rclcpp::Parameter("foo", 4)});
|
||||
rclcpp::spin_until_future_complete(
|
||||
node_with_option, set_future, std::chrono::milliseconds(100));
|
||||
ASSERT_EQ(set_future.get()[0].successful, true);
|
||||
// delete one parameter
|
||||
auto delete_future = asynchronous_client->delete_parameters({"foo"});
|
||||
rclcpp::spin_until_future_complete(
|
||||
node_with_option, delete_future, std::chrono::milliseconds(100));
|
||||
ASSERT_EQ(delete_future.get()[0].successful, true);
|
||||
// check that deleted parameter isn't set
|
||||
auto get_future2 = asynchronous_client->get_parameters({"foo"});
|
||||
rclcpp::spin_until_future_complete(
|
||||
node_with_option, get_future2, std::chrono::milliseconds(100));
|
||||
ASSERT_EQ(
|
||||
get_future2.get()[0].get_type(),
|
||||
rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET);
|
||||
}
|
||||
/*
|
||||
Coverage for sync delete_parameters
|
||||
*/
|
||||
TEST_F(TestParameterClient, sync_parameter_delete_parameters) {
|
||||
auto synchronous_client =
|
||||
std::make_shared<rclcpp::SyncParametersClient>(node_with_option);
|
||||
// set parameter
|
||||
auto set_result = synchronous_client->set_parameters({rclcpp::Parameter("foo", 4)});
|
||||
// delete one parameter
|
||||
auto delete_result = synchronous_client->delete_parameters({"foo"});
|
||||
// check that deleted parameter isn't set
|
||||
auto get_result = synchronous_client->get_parameters({"foo"});
|
||||
ASSERT_EQ(
|
||||
get_result[0].get_type(),
|
||||
rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET);
|
||||
}
|
||||
|
||||
/*
|
||||
Coverage for async load_parameters
|
||||
*/
|
||||
TEST_F(TestParameterClient, async_parameter_load_parameters) {
|
||||
auto load_node = std::make_shared<rclcpp::Node>(
|
||||
"load_node",
|
||||
"namespace",
|
||||
rclcpp::NodeOptions().allow_undeclared_parameters(true));
|
||||
auto asynchronous_client =
|
||||
std::make_shared<rclcpp::AsyncParametersClient>(load_node, "/namespace/load_node");
|
||||
// load parameters
|
||||
rcpputils::fs::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
const std::string parameters_filepath = (
|
||||
test_resources_path / "test_node" / "load_parameters.yaml").string();
|
||||
auto load_future = asynchronous_client->load_parameters(parameters_filepath);
|
||||
auto result_code = rclcpp::spin_until_future_complete(
|
||||
load_node, load_future, std::chrono::milliseconds(100));
|
||||
ASSERT_EQ(result_code, rclcpp::FutureReturnCode::SUCCESS);
|
||||
ASSERT_EQ(load_future.get()[0].successful, true);
|
||||
// list 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));
|
||||
}
|
||||
/*
|
||||
Coverage for sync load_parameters
|
||||
*/
|
||||
TEST_F(TestParameterClient, sync_parameter_load_parameters) {
|
||||
auto load_node = std::make_shared<rclcpp::Node>(
|
||||
"load_node",
|
||||
"namespace",
|
||||
rclcpp::NodeOptions().allow_undeclared_parameters(true));
|
||||
auto synchronous_client =
|
||||
std::make_shared<rclcpp::SyncParametersClient>(load_node);
|
||||
// load parameters
|
||||
rcpputils::fs::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
const std::string parameters_filepath = (
|
||||
test_resources_path / "test_node" / "load_parameters.yaml").string();
|
||||
auto load_future = synchronous_client->load_parameters(parameters_filepath);
|
||||
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));
|
||||
}
|
||||
|
||||
@@ -27,9 +27,9 @@ public:
|
||||
: ParameterEventHandler(node)
|
||||
{}
|
||||
|
||||
void test_event(const rcl_interfaces::msg::ParameterEvent::SharedPtr event)
|
||||
void test_event(rcl_interfaces::msg::ParameterEvent::ConstSharedPtr event)
|
||||
{
|
||||
event_callback(event);
|
||||
event_callback(*event);
|
||||
}
|
||||
|
||||
size_t num_event_callbacks()
|
||||
@@ -264,20 +264,20 @@ TEST_F(TestNode, EventCallback)
|
||||
double product;
|
||||
auto cb =
|
||||
[&int_param, &double_param, &product, &received,
|
||||
this](const rcl_interfaces::msg::ParameterEvent::SharedPtr & event)
|
||||
this](const rcl_interfaces::msg::ParameterEvent & event)
|
||||
{
|
||||
auto node_name = node->get_fully_qualified_name();
|
||||
|
||||
if (event->node == node_name) {
|
||||
if (event.node == node_name) {
|
||||
received = true;
|
||||
}
|
||||
|
||||
rclcpp::Parameter p;
|
||||
if (ParameterEventHandler::get_parameter_from_event(*event, p, "my_int", node_name)) {
|
||||
if (ParameterEventHandler::get_parameter_from_event(event, p, "my_int", node_name)) {
|
||||
int_param = p.get_value<int64_t>();
|
||||
}
|
||||
try {
|
||||
p = ParameterEventHandler::get_parameter_from_event(*event, "my_double", node_name);
|
||||
p = ParameterEventHandler::get_parameter_from_event(event, "my_double", node_name);
|
||||
double_param = p.get_value<double>();
|
||||
} catch (...) {
|
||||
}
|
||||
@@ -286,12 +286,12 @@ TEST_F(TestNode, EventCallback)
|
||||
};
|
||||
|
||||
auto cb2 =
|
||||
[&bool_param, this](const rcl_interfaces::msg::ParameterEvent::SharedPtr & event)
|
||||
[&bool_param, this](const rcl_interfaces::msg::ParameterEvent & event)
|
||||
{
|
||||
rclcpp::Parameter p;
|
||||
if (event->node == diff_ns_name) {
|
||||
if (event.node == diff_ns_name) {
|
||||
if (ParameterEventHandler::get_parameter_from_event(
|
||||
*event, p, "my_bool", diff_ns_name))
|
||||
event, p, "my_bool", diff_ns_name))
|
||||
{
|
||||
bool_param = p.get_value<bool>();
|
||||
}
|
||||
@@ -405,13 +405,13 @@ TEST_F(TestNode, LastInFirstCallForParameterEventCallbacks)
|
||||
// The callbacks will log the current time for comparison purposes. Add a bit of a stall
|
||||
// to ensure that the time noted in the back-to-back calls isn't the same
|
||||
auto cb1 =
|
||||
[this, &time_1](const rcl_interfaces::msg::ParameterEvent::SharedPtr &)
|
||||
[this, &time_1](const rcl_interfaces::msg::ParameterEvent &)
|
||||
{
|
||||
time_1 = node->now();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
};
|
||||
auto cb2 =
|
||||
[this, &time_2](const rcl_interfaces::msg::ParameterEvent::SharedPtr &)
|
||||
[this, &time_2](const rcl_interfaces::msg::ParameterEvent &)
|
||||
{
|
||||
time_2 = node->now();
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
|
||||
@@ -506,3 +506,33 @@ TEST_F(TestPublisher, run_event_handlers) {
|
||||
handler->execute(data);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestPublisher, get_network_flow_endpoints_errors) {
|
||||
initialize();
|
||||
const rclcpp::QoS publisher_qos(1);
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", publisher_qos);
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_publisher_get_network_flow_endpoints, RCL_RET_ERROR);
|
||||
auto mock_network_flow_endpoint_array_fini = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_network_flow_endpoint_array_fini, RCL_RET_ERROR);
|
||||
EXPECT_THROW(
|
||||
publisher->get_network_flow_endpoints(),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
{
|
||||
auto mock_network_flow_endpoint_array_fini = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_network_flow_endpoint_array_fini, RCL_RET_ERROR);
|
||||
EXPECT_THROW(
|
||||
publisher->get_network_flow_endpoints(),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_publisher_get_network_flow_endpoints, RCL_RET_OK);
|
||||
auto mock_network_flow_endpoint_array_fini = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_network_flow_endpoint_array_fini, RCL_RET_OK);
|
||||
EXPECT_NO_THROW(publisher->get_network_flow_endpoints());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
static void OnMessage(const test_msgs::msg::Empty::SharedPtr msg)
|
||||
static void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
|
||||
@@ -42,7 +42,7 @@ protected:
|
||||
|
||||
node = std::make_shared<rclcpp::Node>("test_qos_event", "/ns");
|
||||
|
||||
message_callback = [node = node.get()](const test_msgs::msg::Empty::SharedPtr /*msg*/) {
|
||||
message_callback = [node = node.get()](test_msgs::msg::Empty::ConstSharedPtr /*msg*/) {
|
||||
RCLCPP_INFO(node->get_logger(), "Message received");
|
||||
};
|
||||
}
|
||||
@@ -55,7 +55,7 @@ protected:
|
||||
static constexpr char topic_name[] = "test_topic";
|
||||
rclcpp::Node::SharedPtr node;
|
||||
bool is_fastrtps;
|
||||
std::function<void(const test_msgs::msg::Empty::SharedPtr)> message_callback;
|
||||
std::function<void(test_msgs::msg::Empty::ConstSharedPtr)> message_callback;
|
||||
};
|
||||
|
||||
constexpr char TestQosEvent::topic_name[];
|
||||
|
||||
@@ -58,7 +58,7 @@ TEST(TestSerializedMessageAllocator, borrow_from_subscription) {
|
||||
std::shared_ptr<rclcpp::SubscriptionBase> sub =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"~/dummy_topic", 10,
|
||||
[](std::shared_ptr<test_msgs::msg::Empty> test_msg) {(void) test_msg;});
|
||||
[](std::shared_ptr<const test_msgs::msg::Empty> test_msg) {(void) test_msg;});
|
||||
|
||||
auto msg0 = sub->create_serialized_message();
|
||||
EXPECT_EQ(0u, msg0->capacity());
|
||||
|
||||
@@ -34,7 +34,7 @@ using namespace std::chrono_literals;
|
||||
class TestSubscription : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void OnMessage(const test_msgs::msg::Empty::SharedPtr msg)
|
||||
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
@@ -80,7 +80,7 @@ class TestSubscriptionInvalidIntraprocessQos
|
||||
class TestSubscriptionSub : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void OnMessage(const test_msgs::msg::Empty::SharedPtr msg)
|
||||
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
@@ -113,7 +113,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
void OnMessage(const test_msgs::msg::Empty::SharedPtr msg)
|
||||
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
@@ -130,7 +130,7 @@ public:
|
||||
class SubscriptionClass
|
||||
{
|
||||
public:
|
||||
void OnMessage(const test_msgs::msg::Empty::SharedPtr msg)
|
||||
void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
@@ -150,7 +150,7 @@ public:
|
||||
TEST_F(TestSubscription, construction_and_destruction) {
|
||||
initialize();
|
||||
using test_msgs::msg::Empty;
|
||||
auto callback = [](const Empty::SharedPtr msg) {
|
||||
auto callback = [](Empty::ConstSharedPtr msg) {
|
||||
(void)msg;
|
||||
};
|
||||
{
|
||||
@@ -162,7 +162,7 @@ TEST_F(TestSubscription, construction_and_destruction) {
|
||||
// get_subscription_handle()
|
||||
const rclcpp::SubscriptionBase * const_sub = sub.get();
|
||||
EXPECT_NE(nullptr, const_sub->get_subscription_handle());
|
||||
EXPECT_FALSE(sub->use_take_shared_method());
|
||||
EXPECT_TRUE(sub->use_take_shared_method());
|
||||
|
||||
EXPECT_NE(nullptr, sub->get_message_type_support_handle().typesupport_identifier);
|
||||
EXPECT_NE(nullptr, sub->get_message_type_support_handle().data);
|
||||
@@ -182,7 +182,7 @@ TEST_F(TestSubscription, construction_and_destruction) {
|
||||
*/
|
||||
TEST_F(TestSubscriptionSub, construction_and_destruction) {
|
||||
using test_msgs::msg::Empty;
|
||||
auto callback = [](const Empty::SharedPtr msg) {
|
||||
auto callback = [](Empty::ConstSharedPtr msg) {
|
||||
(void)msg;
|
||||
};
|
||||
{
|
||||
@@ -216,7 +216,7 @@ TEST_F(TestSubscriptionSub, construction_and_destruction) {
|
||||
TEST_F(TestSubscription, various_creation_signatures) {
|
||||
initialize();
|
||||
using test_msgs::msg::Empty;
|
||||
auto cb = [](test_msgs::msg::Empty::SharedPtr) {};
|
||||
auto cb = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
{
|
||||
auto sub = node->create_subscription<Empty>("topic", 1, cb);
|
||||
(void)sub;
|
||||
@@ -506,3 +506,37 @@ 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 = [](const test_msgs::msg::Empty::SharedPtr msg) {
|
||||
(void)msg;
|
||||
};
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", subscription_qos, subscription_callback);
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_subscription_get_network_flow_endpoints, RCL_RET_ERROR);
|
||||
auto mock_network_flow_endpoint_array_fini = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_network_flow_endpoint_array_fini, RCL_RET_ERROR);
|
||||
EXPECT_THROW(
|
||||
subscription->get_network_flow_endpoints(),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
{
|
||||
auto mock_network_flow_endpoint_array_fini = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_network_flow_endpoint_array_fini, RCL_RET_ERROR);
|
||||
EXPECT_THROW(
|
||||
subscription->get_network_flow_endpoints(),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_subscription_get_network_flow_endpoints, RCL_RET_OK);
|
||||
auto mock_network_flow_endpoint_array_fini = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_network_flow_endpoint_array_fini, RCL_RET_OK);
|
||||
EXPECT_NO_THROW(subscription->get_network_flow_endpoints());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -108,7 +108,7 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
static void OnMessage(const test_msgs::msg::Empty::SharedPtr msg)
|
||||
static void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
|
||||
77
rclcpp/test/rclcpp/test_typesupport_helpers.cpp
Normal file
77
rclcpp/test/rclcpp/test_typesupport_helpers.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2018, Bosch Software Innovations GmbH.
|
||||
// Copyright 2021, Apex.AI 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 <gmock/gmock.h>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rcpputils/shared_library.hpp"
|
||||
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
|
||||
using namespace ::testing; // NOLINT
|
||||
|
||||
TEST(TypesupportHelpersTest, throws_exception_if_filetype_has_no_type) {
|
||||
EXPECT_ANY_THROW(
|
||||
rclcpp::get_typesupport_library("just_a_package_name", "rosidl_typesupport_cpp"));
|
||||
}
|
||||
|
||||
TEST(TypesupportHelpersTest, throws_exception_if_filetype_has_slash_at_the_start_only) {
|
||||
EXPECT_ANY_THROW(
|
||||
rclcpp::get_typesupport_library("/name_with_slash_at_start", "rosidl_typesupport_cpp"));
|
||||
}
|
||||
|
||||
TEST(TypesupportHelpersTest, throws_exception_if_filetype_has_slash_at_the_end_only) {
|
||||
EXPECT_ANY_THROW(
|
||||
rclcpp::get_typesupport_library("name_with_slash_at_end/", "rosidl_typesupport_cpp"));
|
||||
}
|
||||
|
||||
TEST(TypesupportHelpersTest, throws_exception_if_library_cannot_be_found) {
|
||||
EXPECT_THROW(
|
||||
rclcpp::get_typesupport_library("invalid/message", "rosidl_typesupport_cpp"),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
TEST(TypesupportHelpersTest, returns_c_type_info_for_valid_legacy_library) {
|
||||
try {
|
||||
auto library = rclcpp::get_typesupport_library(
|
||||
"test_msgs/BasicTypes", "rosidl_typesupport_cpp");
|
||||
auto string_typesupport = rclcpp::get_typesupport_handle(
|
||||
"test_msgs/BasicTypes", "rosidl_typesupport_cpp", *library);
|
||||
|
||||
EXPECT_THAT(
|
||||
std::string(string_typesupport->typesupport_identifier),
|
||||
ContainsRegex("rosidl_typesupport"));
|
||||
} catch (const std::exception & e) {
|
||||
FAIL() << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TypesupportHelpersTest, returns_c_type_info_for_valid_library) {
|
||||
try {
|
||||
auto library = rclcpp::get_typesupport_library(
|
||||
"test_msgs/msg/BasicTypes", "rosidl_typesupport_cpp");
|
||||
auto string_typesupport = rclcpp::get_typesupport_handle(
|
||||
"test_msgs/msg/BasicTypes", "rosidl_typesupport_cpp", *library);
|
||||
|
||||
EXPECT_THAT(
|
||||
std::string(string_typesupport->typesupport_identifier),
|
||||
ContainsRegex("rosidl_typesupport"));
|
||||
} catch (const std::runtime_error & e) {
|
||||
FAIL() << e.what();
|
||||
}
|
||||
}
|
||||
74
rclcpp/test/rclcpp/test_wait_for_message.cpp
Normal file
74
rclcpp/test/rclcpp/test_wait_for_message.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
// Copyright 2021 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 <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/wait_for_message.hpp"
|
||||
|
||||
#include "test_msgs/msg/strings.hpp"
|
||||
#include "test_msgs/message_fixtures.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
TEST(TestUtilities, wait_for_message) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("wait_for_message_node");
|
||||
|
||||
using MsgT = test_msgs::msg::Strings;
|
||||
auto pub = node->create_publisher<MsgT>("wait_for_message_topic", 10);
|
||||
|
||||
MsgT out;
|
||||
auto received = false;
|
||||
auto wait = std::async(
|
||||
[&]() {
|
||||
auto ret = rclcpp::wait_for_message(out, node, "wait_for_message_topic", 5s);
|
||||
EXPECT_TRUE(ret);
|
||||
received = true;
|
||||
});
|
||||
|
||||
for (auto i = 0u; i < 10 && received == false; ++i) {
|
||||
pub->publish(*get_messages_strings()[0]);
|
||||
std::this_thread::sleep_for(1s);
|
||||
}
|
||||
ASSERT_TRUE(received);
|
||||
EXPECT_EQ(out, *get_messages_strings()[0]);
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestUtilities, wait_for_message_indefinitely) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("wait_for_message_node2");
|
||||
|
||||
using MsgT = test_msgs::msg::Strings;
|
||||
MsgT out;
|
||||
auto received = false;
|
||||
auto wait = std::async(
|
||||
[&]() {
|
||||
auto ret = rclcpp::wait_for_message(out, node, "wait_for_message_topic" /*, -1 */);
|
||||
EXPECT_TRUE(ret);
|
||||
received = true;
|
||||
});
|
||||
|
||||
rclcpp::shutdown();
|
||||
|
||||
ASSERT_FALSE(received);
|
||||
}
|
||||
@@ -221,7 +221,7 @@ TEST_F(TestWaitSet, add_guard_condition_to_two_different_wait_set) {
|
||||
wait_set2.add_guard_condition(guard_condition);
|
||||
}, std::runtime_error);
|
||||
|
||||
auto do_nothing = [](const std::shared_ptr<test_msgs::msg::BasicTypes>) {};
|
||||
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::BasicTypes>) {};
|
||||
auto sub = node->create_subscription<test_msgs::msg::BasicTypes>("~/test", 1, do_nothing);
|
||||
wait_set1.add_subscription(sub);
|
||||
ASSERT_THROW(
|
||||
@@ -281,7 +281,7 @@ TEST_F(TestWaitSet, add_remove_wait) {
|
||||
rclcpp::SubscriptionOptions subscription_options;
|
||||
subscription_options.event_callbacks.deadline_callback = [](auto) {};
|
||||
subscription_options.event_callbacks.liveliness_callback = [](auto) {};
|
||||
auto do_nothing = [](const std::shared_ptr<test_msgs::msg::BasicTypes>) {};
|
||||
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::BasicTypes>) {};
|
||||
auto sub =
|
||||
node->create_subscription<test_msgs::msg::BasicTypes>(
|
||||
"~/test", 1, do_nothing, subscription_options);
|
||||
|
||||
@@ -74,7 +74,7 @@ TEST_F(TestDynamicStorage, default_construct_destruct) {
|
||||
|
||||
TEST_F(TestDynamicStorage, iterables_construct_destruct) {
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
auto timer = node->create_wall_timer(std::chrono::seconds(100), []() {});
|
||||
auto guard_condition = std::make_shared<rclcpp::GuardCondition>();
|
||||
auto service =
|
||||
@@ -110,7 +110,7 @@ TEST_F(TestDynamicStorage, add_remove_dynamically) {
|
||||
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
|
||||
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {}, options);
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {}, options);
|
||||
|
||||
rclcpp::SubscriptionWaitSetMask mask{true, true, true};
|
||||
wait_set.add_subscription(subscription, mask);
|
||||
@@ -203,7 +203,7 @@ TEST_F(TestDynamicStorage, add_remove_out_of_scope) {
|
||||
|
||||
{
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
wait_set.add_subscription(subscription);
|
||||
|
||||
// This is short, so if it's not cleaned up, it will trigger wait and it won't timeout
|
||||
@@ -238,7 +238,7 @@ TEST_F(TestDynamicStorage, wait_subscription) {
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10);
|
||||
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
wait_set.add_subscription(subscription);
|
||||
|
||||
{
|
||||
|
||||
@@ -67,7 +67,7 @@ private:
|
||||
|
||||
TEST_F(TestStaticStorage, iterables_construct_destruct) {
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
// This is long, so it can stick around and be removed
|
||||
auto timer = node->create_wall_timer(std::chrono::seconds(100), []() {});
|
||||
auto guard_condition = std::make_shared<rclcpp::GuardCondition>();
|
||||
@@ -132,7 +132,7 @@ TEST_F(TestStaticStorage, fixed_storage_needs_pruning) {
|
||||
TEST_F(TestStaticStorage, wait_subscription) {
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10);
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
rclcpp::SubscriptionWaitSetMask mask{true, true, true};
|
||||
rclcpp::StaticWaitSet<1, 0, 0, 0, 0, 0> wait_set({{{subscription, mask}}});
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ TEST_F(TestStoragePolicyCommon, rcl_wait_set_resize_error) {
|
||||
rclcpp::WaitSet wait_set;
|
||||
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
rclcpp::SubscriptionWaitSetMask mask{true, true, true};
|
||||
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
@@ -103,7 +103,7 @@ TEST_F(TestStoragePolicyCommon, rcl_wait_set_clear_error) {
|
||||
TEST_F(TestStoragePolicyCommon, rcl_wait_set_add_subscription_error) {
|
||||
rclcpp::WaitSet wait_set;
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
rclcpp::SubscriptionWaitSetMask mask{true, true, true};
|
||||
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
|
||||
@@ -75,7 +75,7 @@ TEST_F(TestThreadSafeStorage, default_construct_destruct) {
|
||||
|
||||
TEST_F(TestThreadSafeStorage, iterables_construct_destruct) {
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
// This is long, so it can stick around
|
||||
auto timer = node->create_wall_timer(std::chrono::seconds(100), []() {});
|
||||
auto guard_condition = std::make_shared<rclcpp::GuardCondition>();
|
||||
@@ -113,7 +113,7 @@ TEST_F(TestThreadSafeStorage, add_remove_dynamically) {
|
||||
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
|
||||
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {}, options);
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {}, options);
|
||||
|
||||
rclcpp::SubscriptionWaitSetMask mask{true, true, true};
|
||||
wait_set.add_subscription(subscription, mask);
|
||||
@@ -207,7 +207,7 @@ TEST_F(TestThreadSafeStorage, add_remove_out_of_scope) {
|
||||
|
||||
{
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
wait_set.add_subscription(subscription);
|
||||
|
||||
// This is short, so if it's not cleaned up, it will trigger wait
|
||||
@@ -242,7 +242,7 @@ TEST_F(TestThreadSafeStorage, wait_subscription) {
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10);
|
||||
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic", 10, [](test_msgs::msg::Empty::SharedPtr) {});
|
||||
"topic", 10, [](test_msgs::msg::Empty::ConstSharedPtr) {});
|
||||
wait_set.add_subscription(subscription);
|
||||
|
||||
{
|
||||
|
||||
18
rclcpp/test/resources/test_node/load_parameters.yaml
Normal file
18
rclcpp/test/resources/test_node/load_parameters.yaml
Normal file
@@ -0,0 +1,18 @@
|
||||
/**:
|
||||
ros__parameters:
|
||||
bar: 5
|
||||
foo: 3.5
|
||||
|
||||
/*:
|
||||
load_node:
|
||||
ros__parameters:
|
||||
bar_foo: "ok"
|
||||
|
||||
namespace:
|
||||
load_node:
|
||||
ros__parameters:
|
||||
foo_bar: true
|
||||
|
||||
bar:
|
||||
ros__parameters:
|
||||
fatal: 10
|
||||
@@ -3,6 +3,34 @@ Changelog for package rclcpp_action
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
9.2.0 (2021-09-17)
|
||||
------------------
|
||||
* Fixed a bug where it would occasionally miss a goal result, which was caused by race condition (`#1677 <https://github.com/ros2/rclcpp/issues/1677>`_) (`#1683 <https://github.com/ros2/rclcpp/issues/1683>`_)
|
||||
* Co-authored-by: Kaven Yau <kavenyau@foxmail.com>
|
||||
* Contributors: Tomoya Fujita
|
||||
|
||||
9.1.0 (2021-05-20)
|
||||
------------------
|
||||
* Bump the benchmark timeout for benchmark_action_client (`#1672 <https://github.com/ros2/rclcpp/issues/1672>`_)
|
||||
* Contributors: Scott K Logan
|
||||
|
||||
9.0.3 (2021-05-10)
|
||||
------------------
|
||||
* Returns CancelResponse::REJECT while goal handle failed to transit to CANCELING state (`#1641 <https://github.com/ros2/rclcpp/issues/1641>`_) (`#1653 <https://github.com/ros2/rclcpp/issues/1653>`_)
|
||||
* Fix action server deadlock issue that caused by other mutexes locked in CancelCallback (`#1635 <https://github.com/ros2/rclcpp/issues/1635>`_) (`#1646 <https://github.com/ros2/rclcpp/issues/1646>`_)
|
||||
* Contributors: Jacob Perron, Kaven Yau, William Woodall
|
||||
|
||||
9.0.2 (2021-04-14)
|
||||
------------------
|
||||
|
||||
9.0.1 (2021-04-12)
|
||||
------------------
|
||||
|
||||
9.0.0 (2021-04-06)
|
||||
------------------
|
||||
* updating quality declaration links (re: `ros2/docs.ros2.org#52 <https://github.com/ros2/docs.ros2.org/issues/52>`_) (`#1615 <https://github.com/ros2/rclcpp/issues/1615>`_)
|
||||
* Contributors: shonigmann
|
||||
|
||||
8.2.0 (2021-03-31)
|
||||
------------------
|
||||
|
||||
|
||||
@@ -4,13 +4,13 @@ This document is a declaration of software quality for the `rclcpp_action` packa
|
||||
|
||||
The package `rclcpp_action` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware.
|
||||
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#package-quality-categories) of the ROS2 developer guide.
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://www.ros.org/reps/rep-2004.html) of the ROS2 developer guide.
|
||||
|
||||
## Version Policy [1]
|
||||
|
||||
### Version Scheme [1.i]
|
||||
|
||||
`rclcpp_action` uses `semver` according to the recommendation for ROS Core packages in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#versioning).
|
||||
`rclcpp_action` uses `semver` according to the recommendation for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#versioning).
|
||||
|
||||
### Version Stability [1.ii]
|
||||
|
||||
@@ -37,11 +37,11 @@ All installed headers are in the `include` directory of the package, headers in
|
||||
|
||||
## Change Control Process [2]
|
||||
|
||||
`rclcpp_action` follows the recommended guidelines for ROS Core packages in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#change-control-process).
|
||||
`rclcpp_action` follows the recommended guidelines for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process).
|
||||
|
||||
### Change Requests [2.i]
|
||||
|
||||
All changes will occur through a pull request, check [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#change-control-process) for additional information.
|
||||
All changes will occur through a pull request, check [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process) for additional information.
|
||||
|
||||
### Contributor Origin [2.ii]
|
||||
|
||||
@@ -49,7 +49,7 @@ This package uses DCO as its confirmation of contributor origin policy. More inf
|
||||
|
||||
### Peer Review Policy [2.iii]
|
||||
|
||||
All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#change-control-process) for additional information.
|
||||
All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#change-control-process) for additional information.
|
||||
|
||||
### Continuous Integration [2.iv]
|
||||
|
||||
@@ -109,7 +109,7 @@ The tests aim to cover both typical usage and corner cases, but are quantified b
|
||||
|
||||
### Coverage [4.iii]
|
||||
|
||||
`rclcpp_action` follows the recommendations for ROS Core packages in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#code-coverage), and opts to use line coverage instead of branch coverage.
|
||||
`rclcpp_action` follows the recommendations for ROS Core packages in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#code-coverage), and opts to use line coverage instead of branch coverage.
|
||||
|
||||
This includes:
|
||||
|
||||
@@ -119,11 +119,11 @@ This includes:
|
||||
|
||||
Changes are required to make a best effort to keep or increase coverage before being accepted, but decreases are allowed if properly justified and accepted by reviewers.
|
||||
|
||||
Current coverage statistics can be viewed [here](https://ci.ros2.org/job/nightly_linux_coverage/lastCompletedBuild/cobertura/src_ros2_rclcpp_rclcpp_action_src/). A description of how coverage statistics are calculated is summarized in this page ["ROS 2 Onboarding Guide"](https://index.ros.org/doc/ros2/Contributing/ROS-2-On-boarding-Guide/#note-on-coverage-runs).
|
||||
Current coverage statistics can be viewed [here](https://ci.ros2.org/job/nightly_linux_coverage/lastCompletedBuild/cobertura/src_ros2_rclcpp_rclcpp_action_src/). A description of how coverage statistics are calculated is summarized in this page ["ROS 2 Onboarding Guide"](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#note-on-coverage-runs).
|
||||
|
||||
### Performance [4.iv]
|
||||
|
||||
`rclcpp_action` follows the recommendations for performance testing of C/C++ code in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#performance), and opts to do performance analysis on each release rather than each change.
|
||||
`rclcpp_action` follows the recommendations for performance testing of C/C++ code in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#performance), and opts to do performance analysis on each release rather than each change.
|
||||
|
||||
The performance tests of `rclcpp_action` are located in the [test/benchmark directory](https://github.com/ros2/rclcpp/tree/master/rclcpp_action/test/benchmark).
|
||||
|
||||
@@ -135,7 +135,7 @@ Changes that introduce regressions in performance must be adequately justified i
|
||||
|
||||
### Linters and Static Analysis [4.v]
|
||||
|
||||
`rclcpp_action` uses and passes all the ROS2 standard linters and static analysis tools for a C++ package as described in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#linters-and-static-analysis). Passing implies there are no linter/static errors when testing against CI of supported platforms.
|
||||
`rclcpp_action` uses and passes all the ROS2 standard linters and static analysis tools for a C++ package as described in the [ROS 2 Developer Guide](https://docs.ros.org/en/rolling/Contributing/Developer-Guide.html#linters-and-static-analysis). Passing implies there are no linter/static errors when testing against CI of supported platforms.
|
||||
|
||||
Currently nightly test results can be seen here:
|
||||
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/testReport/rclcpp_action/)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user