Compare commits
50 Commits
8.2.0
...
fix_wait_s
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
485203168a | ||
|
|
b30ee485af | ||
|
|
e813d43a33 | ||
|
|
34119ca997 | ||
|
|
e9e398d2d4 | ||
|
|
7d8b2690ff | ||
|
|
55d2f67b90 | ||
|
|
39bfb30eb0 | ||
|
|
1c61751540 | ||
|
|
0251f70fe8 | ||
|
|
07b6ea0ff4 | ||
|
|
56f68f9c44 | ||
|
|
18fcd9138b | ||
|
|
f245b4cc81 | ||
|
|
d488535f36 | ||
|
|
72443ff295 | ||
|
|
a3383c309a | ||
|
|
5dad229662 | ||
|
|
2e4c97ac56 | ||
|
|
0659d829ce | ||
|
|
1fc2d58799 | ||
|
|
c15eb1eaac | ||
|
|
d051b8aa20 | ||
|
|
6806cdf825 | ||
|
|
79c2dd8e8b | ||
|
|
fba080cf34 | ||
|
|
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,54 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
11.0.0 (2021-05-18)
|
||||
-------------------
|
||||
* Allow declare uninitialized parameters (`#1673 <https://github.com/ros2/rclcpp/issues/1673>`_)
|
||||
* Fix syntax issue with gcc (`#1674 <https://github.com/ros2/rclcpp/issues/1674>`_)
|
||||
* [service] Don't use a weak_ptr to avoid leaking (`#1668 <https://github.com/ros2/rclcpp/issues/1668>`_)
|
||||
* Contributors: Ivan Santiago Paunovic, Jacob Perron, William Woodall
|
||||
|
||||
10.0.0 (2021-05-11)
|
||||
-------------------
|
||||
* Fix doc typo (`#1663 <https://github.com/ros2/rclcpp/issues/1663>`_)
|
||||
* [rclcpp] Type Adaptation feature (`#1557 <https://github.com/ros2/rclcpp/issues/1557>`_)
|
||||
* Do not attempt to use void allocators for memory allocation. (`#1657 <https://github.com/ros2/rclcpp/issues/1657>`_)
|
||||
* Keep custom allocator in publisher and subscription options alive. (`#1647 <https://github.com/ros2/rclcpp/issues/1647>`_)
|
||||
* Fix get_publishers_subscriptions_info_by_topic test in test_node.cpp (`#1648 <https://github.com/ros2/rclcpp/issues/1648>`_)
|
||||
* Use OnShutdown callback handle instead of OnShutdown callback (`#1639 <https://github.com/ros2/rclcpp/issues/1639>`_)
|
||||
* use dynamic_pointer_cast to detect allocator mismatch in intra process manager (`#1643 <https://github.com/ros2/rclcpp/issues/1643>`_)
|
||||
* 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: Audrow Nash, 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/)
|
||||
|
||||
161
rclcpp/doc/wait_set_architecture.md
Normal file
161
rclcpp/doc/wait_set_architecture.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# Architecture of the rclcpp::WaitSet Type
|
||||
|
||||
The `rclcpp::WaitSet` type is actually a specific configuration of a the more general `rclcpp::WaitSetTemplate` class.
|
||||
|
||||
The `rclcpp::WaitSetTemplate` class is design to have interchangeable implementations for controlling storage of items in the wait set, as well as to control access to the wait set functionality, i.e. whether or not it has thread-safety.
|
||||
|
||||
In general the idea behind a wait set in rclcpp is that you gather a set of entities that can be waited on, e.g. subscriptions, timers, guard conditions, and then wait for one or more of them to be ready.
|
||||
These things are driven by asynchronous events, e.g. subscriptions become ready when new data arrives or timers become ready when their period has elapsed.
|
||||
|
||||
At the moment, things that can be waited on are limited by what can be put into an `rcl_wait_set_t`, which includes subscriptions, service clients, service servers, timers, guard conditions, and events.
|
||||
In this context events are QoS events, as defined in the `rcl` and `rmw` interfaces.
|
||||
|
||||
## Design Goals
|
||||
|
||||
There are some design goals for the `rclcpp::WaitSet` family of classes:
|
||||
|
||||
- minimize inheritance and avoid virtual functions
|
||||
- Overhead of the executor and wait set design have been a common complaint and are often blamed for performance issues.
|
||||
- Since the wait set is at the core of the wait-do loop of the system, aggressive optimization can actually pay off.
|
||||
- avoid unnecessary changes to the underlying `rcl_wait_set_t` instances or unnecessarily recreating it
|
||||
- This can be expensive, because under the hood that requires changes to the middleware's object, e.g. a DDS wait set or similar.
|
||||
- minimize memory allocations in the wait() function, avoid them entirely if the set of things to wait on has not changed
|
||||
- prevent accidentally using an entity with more than wait set at the same time
|
||||
|
||||
Several of these surround performance concerns, but others are concerned with safety and convenience for the user.
|
||||
|
||||
## Architecture
|
||||
|
||||
The `rclcpp::WaitSetTemplate` is the core fixture in the feature, which is using a policy-based design (see https://en.wikipedia.org/wiki/Modern_C%2B%2B_Design#Policy-based_design).
|
||||
|
||||
It delegates storage and synchronization to policy template arguments to the `rclcpp::WaitSetTemplate` via the `StoragePolicy` and `SynchronizationPolicy` template arguments respectively.
|
||||
|
||||
These policies can be mixed and matched to achieve different results.
|
||||
There are a few predefined combinations, `rclcpp::WaitSet` which is a non-thread-safe wait set with dynamic storage, `rclcpp::StaticWaitSet` which is a non-thread-safe wait set with fixed storage, and `rclcpp::ThreadSafeWaitSet` which is a thread-safe wait set with dynamic storage.
|
||||
They are all just aliases to `rclcpp::WaitSetTemplate` with difference combinations of policies.
|
||||
|
||||
Users can extend the behavior by implementing their own policies if desired.
|
||||
|
||||
This kind of design allows for specialization and code sharing without using standard polymorphism via virtual functions, and the benefit of that is that calls on the wait set class are always non-virtual, avoiding dispatching via the v-table.
|
||||
It also forces us to use templates, which means a head-only implementation, which will increase compile times, but will avoid some calls to shared libraries which on some systems has a small overhead too.
|
||||
|
||||
These are aggressive optimizations, but it can actually be very beneficial in the inner-loop of applications, and the hope is that this approach will offer the best performance we can achieve.
|
||||
|
||||
### StoragePolicy
|
||||
|
||||
The storage policy is responsible for storing the entities in the wait set and providing accessors and setters (if appropriate).
|
||||
|
||||
The storage policy is also responsible for maintaining ownership of the entities it is storing at the right times.
|
||||
For example, entities are always given to the wait set as `std::shared_ptr`'s and some policies might only maintain ownership for the lifetime of the wait set or until the entity is removed.
|
||||
Other policies might only hold ownership of the entities while waiting on them, i.e. while they are actively using them, but maintain only weak ownership otherwise.
|
||||
|
||||
There are two built-in storage policies available, the `rclcpp::wait_set_policies::DynamicStorage` and the `rclcpp::wait_set_policies::StaticStorage<...>` classes.
|
||||
|
||||
The `StaticStorage` policy requires that you fully specify the number of entities of each time that can be stored as template arguments and uses the `std:array` class as backing storage.
|
||||
Therefore, it also does not allow adding or removing entities after construction.
|
||||
Because entities cannot be removed and are therefore "always" in use by the wait set, it maintains shared ownership of the entities for the lifetime of the wait set.
|
||||
|
||||
The `DynamicStorage` policy provides access to the entities and allows add and removing entities at any time after construction.
|
||||
It also only maintains shared ownership of the entities while actively waiting on them, which allows the entities to go out of scope while the wait set is idle.
|
||||
Entities that go out of scope in this manner will be removed from the wait set when it notices their weak pointers no longer lock to the entity's shared pointer.
|
||||
|
||||
Both policies allow you to initialize the wait set with entities in the constructor.
|
||||
|
||||
### SynchronizationPolicy
|
||||
|
||||
The synchronization policy is responsible for synchronizing access to wait set operations that share access to the StoragePolicy.
|
||||
|
||||
There are two built-in synchronization policies available, the `rclcpp::wait_set_policies::SequentialSynchronization` and the `rclcpp::wait_set_policies::ThreadSafeSynchronization` policies.
|
||||
|
||||
The `rclcpp::wait_set_policies::SequentialSynchronization` policy essentially does nothing but pass through requests from the user-facing `rclcpp::WaitSetTemplate` interface to the `StoragePolicy` that is being used.
|
||||
|
||||
The `rclcpp::wait_set_policies::ThreadSafeSynchronization` policy sequences access to the `StoragePolicy` being used so that it is safe to call methods like `add_subscription()` on the wait set while it is doing something else, like waiting with the `wait()` method.
|
||||
This synchronization comes at the expense of using mutexes and extra logic to organize the access, but provides some thread-safety.
|
||||
|
||||
### Entity Intake and Management
|
||||
|
||||
Entities, i.e. `rclcpp::SubscriptionBase`, `rclcpp::Waitable`, `rclcpp::ClientBase`, `rclcpp::ServiceBase`, `rclcpp::TimerBase`, `rclcpp::GuardCondition`, etc., may be passed to the wait set during construction.
|
||||
If the wait set has policies that allow for dynamic storage and supports adding and removing entities after construction, then entities can also be added and removed using method on the wait set.
|
||||
|
||||
Either way, the entities are initially given to the wait set as a `std::shared_ptr` in order to maintain shared ownership, but depending on the storage policy it may be stored as a `std::weak_ptr` while the wait set is not actively using the entities.
|
||||
|
||||
Also, when taking on new entities the wait set is responsible for ensuring that entity is not being used by another wait set (at least another instance of `rclcpp::WaitSetTemplate`).
|
||||
|
||||
One more thing that plays into intake of entities is that a few entity types have some extra requirements, specifically subscriptions and waitables.
|
||||
|
||||
Subscriptions (specifically `rclcpp::SubscriptionBase`) are complex objects that contain not only a subscription that can be waited on, but also may have `rclcpp::Waitable`'s inside in order to implement intra-process communication and for QoS events that are also implemented as `rclcpp::Waitable` instances.
|
||||
Therefore, you need a way to say which parts of the subscription should be added to the wait set, because you don't need to add all the entities that make up a subscription to a single wait set.
|
||||
The `rclcpp::SubscriptionWaitSetMask` class provides this mapping, and when adding subscriptions to a wait set you need to provide the subscription shared pointer as well as a mask instance.
|
||||
|
||||
Waitables are complicated by the fact that they often are part of a larger piece of the API, as we saw with subscriptions, and therefore the waitable may need to keep additional things in scope while being used.
|
||||
So when adding a waitable to a wait set you may also provide an "associated entity" as a shared pointer to void, which is purely there to keep something in scope along with the waitable.
|
||||
|
||||
#### EntityEntry classes
|
||||
|
||||
In order to address the need to pair extra information with some entities (e.g. the subscription mask with the subscription entity) and to provide a convenient place to check if the entity is in use by another wait set and claim if not.
|
||||
|
||||
There are a series of classes that are a variation of the `EntityEntry` concept, which wrap the incoming entities and their associated data into a single class, and also provide some safety when converting between the shared ownership and weak ownership if needed.
|
||||
|
||||
They also provide RAII-style checking about whether or not an entity is already associated with a wait set.
|
||||
This is important because when ingesting multiple entities at the same time, e.g. via the constructor, because if you claim the first few entities to be associated with the wait set, but then find one that is already associated, then you need to abort and throw an exception.
|
||||
The RAII-style of these classes addresses this by "dissociating" the first few entities from the wait set when going out of scope due to the thrown exception.
|
||||
|
||||
The process from intake to cleanup of the entity entries follows something like this, using waitables as an example:
|
||||
|
||||
|
||||
|
||||
┌───────────────┐
|
||||
┌────────────────────────┐ │ WaitSet │
|
||||
│ ├──────►│ ├───────────────┐
|
||||
│ shared_ptr<Waitable> │ │ constructor │ │
|
||||
│ │ └───────────────┘ Implicit │
|
||||
│ and optional │ │
|
||||
│ │ ┌───────────────┐ Conversion │
|
||||
│ shared_ptr<void> │ │ add_waitable()│ │
|
||||
│ ├──────►│ ├───────────────┤
|
||||
└────────────────────────┘ │ method │ │
|
||||
└───────────────┘ │
|
||||
│
|
||||
│
|
||||
│
|
||||
┌──────────────────────┐ ┌─────────────────┐ │
|
||||
│ │ │ │ │
|
||||
┌─────┤ ManagedWaitableEntry │◄──┬────┤ WaitableEntry │◄──────┘
|
||||
│ │ │ │ │ │
|
||||
│ └─┬──────────────────┬─┘ │ └─┬─────────────┬─┘
|
||||
│ │ shared_ptr+RAII │ │ │ shared_ptr │
|
||||
│ └──────────────────┘ │ └─────────────┘
|
||||
│ │
|
||||
│ ┌───────┴───────────┐
|
||||
│ │Check that entity │
|
||||
│ Conversion to │is not in use, then│
|
||||
│ weak ownership │claim it for this │
|
||||
│ if needed. │wait set. │
|
||||
│ └───────────────────┘
|
||||
│
|
||||
│ ┌───────────────────────────┐
|
||||
│ │ │
|
||||
└────►│ WeakManagedWaitableEntry │
|
||||
│ │
|
||||
└─┬───────────────────────┬─┘
|
||||
│ weak_ptr + RAII │
|
||||
└───────────────────────┘
|
||||
|
||||
Using the diagram as a reference, you can see that the input from the user is converted into the entity entry class that, for now, just stores the entity and any associated data if it exists.
|
||||
At this point the entity comes in with shared ownership and stays as shared ownership in the basic entry class.
|
||||
Then this entry class is converted into a "managed" entry class, which will check that the entity is not associated with another wait set and associate it with the current wait set during construction.
|
||||
This managed entry class will also automatically disassociate the entity with the current wait set during destruction.
|
||||
The new managed entry class continues to keep shared ownership of the entity.
|
||||
|
||||
Optionally, if the storage policy requires it, the managed entry class can be converted into a "weak" version, where in the entity is stored as a weak pointer instead of a shared pointer.
|
||||
In this case the entity will remain stored as a "weak managed" entry until it is removed or the wait set is destroyed.
|
||||
In the meantime, if the wait set needs to take shared ownership again, it can do that be getting a copy of the weak pointer(s) in the entry and locking them to get shared pointers.
|
||||
But the weak managed entry remains responsible for the RAII-style dissociation from the current wait set when destroyed.
|
||||
Unlike the managed entry that maintains shared ownership, the weak version will need to attempt to lock the weak pointer for the entity before trying to dissociate it from the wait set, because this association is maintained within the entity itself, through methods like `rclcpp::Waitable::exchange_in_use_by_wait_set_state()`.
|
||||
If the entity cannot be accessed via the weak pointer, then it is not explicitly dissociated by the managed entry, but it doesn't matter because the object has gone out of scope and has been deleted anyway.
|
||||
So the cleanup for the weak managed entry is best effort, but that does not pose an issue.
|
||||
|
||||
#### The EntityEntryTemplate classes
|
||||
|
||||
There is a template class called `rclcpp::wait_set_policies::detail::EntityEntryTemplate<EntityT>` (and associated managed and weak-managed versions) which is the foundation for most of the entity types which don't require special handling, like `rclcpp::GuardCondition` and `rclcpp::TimerBase` for example.
|
||||
Other entities like `rclcpp::SubscriptionBase` and `rclcpp::Waitable` have custom specializations of these classes to handle extra storage, custom constructors, and other extra logic to help them work.
|
||||
@@ -95,7 +95,7 @@ void set_allocator_for_deleter(AllocatorDeleter<T> * deleter, Alloc * alloc)
|
||||
template<typename Alloc, typename T>
|
||||
using Deleter = typename std::conditional<
|
||||
std::is_same<typename std::allocator_traits<Alloc>::template rebind_alloc<T>,
|
||||
typename std::allocator<void>::template rebind<T>::other>::value,
|
||||
std::allocator<T>>::value,
|
||||
std::default_delete<T>,
|
||||
AllocatorDeleter<Alloc>
|
||||
>::type;
|
||||
|
||||
@@ -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_
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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.
|
||||
@@ -80,7 +92,7 @@ public:
|
||||
*
|
||||
* Note that this function does not setup any signal handlers, so if you want
|
||||
* it to be shutdown by the signal handler, then you need to either install
|
||||
* them manually with rclcpp::install_signal_handers() or use rclcpp::init().
|
||||
* them manually with rclcpp::install_signal_handlers() or use rclcpp::init().
|
||||
* In addition to installing the signal handlers, the shutdown_on_sigint
|
||||
* of the InitOptions needs to be `true` for this context to be shutdown by
|
||||
* the signal handler, otherwise it will be passed over.
|
||||
@@ -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_
|
||||
@@ -47,11 +47,11 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT,
|
||||
typename SubscriptionT,
|
||||
typename MessageMemoryStrategyT,
|
||||
typename NodeParametersT,
|
||||
typename NodeTopicsT>
|
||||
typename NodeTopicsT,
|
||||
typename ROSMessageType = typename SubscriptionT::ROSMessageType>
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
NodeParametersT & node_parameters,
|
||||
@@ -70,7 +70,7 @@ create_subscription(
|
||||
using rclcpp::node_interfaces::get_node_topics_interface;
|
||||
auto node_topics_interface = get_node_topics_interface(node_topics);
|
||||
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>
|
||||
subscription_topic_stats = nullptr;
|
||||
|
||||
if (rclcpp::detail::resolve_enable_topic_statistics(
|
||||
@@ -92,11 +92,11 @@ create_subscription(
|
||||
qos);
|
||||
|
||||
subscription_topic_stats = std::make_shared<
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>
|
||||
>(node_topics_interface->get_node_base_interface()->get_name(), publisher);
|
||||
|
||||
std::weak_ptr<
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>
|
||||
> weak_subscription_topic_stats(subscription_topic_stats);
|
||||
auto sub_call_back = [weak_subscription_topic_stats]() {
|
||||
auto subscription_topic_stats = weak_subscription_topic_stats.lock();
|
||||
@@ -153,7 +153,6 @@ create_subscription(
|
||||
* \tparam MessageT
|
||||
* \tparam CallbackT
|
||||
* \tparam AllocatorT
|
||||
* \tparam CallbackMessageT
|
||||
* \tparam SubscriptionT
|
||||
* \tparam MessageMemoryStrategyT
|
||||
* \tparam NodeT
|
||||
@@ -171,13 +170,8 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
|
||||
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>,
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType,
|
||||
typename NodeT>
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
@@ -194,7 +188,7 @@ create_subscription(
|
||||
)
|
||||
{
|
||||
return rclcpp::detail::create_subscription<
|
||||
MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
MessageT, CallbackT, AllocatorT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
node, node, topic_name, qos, std::forward<CallbackT>(callback), options, msg_mem_strat);
|
||||
}
|
||||
|
||||
@@ -206,13 +200,8 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
|
||||
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>>
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType>
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & node_parameters,
|
||||
@@ -229,7 +218,7 @@ create_subscription(
|
||||
)
|
||||
{
|
||||
return rclcpp::detail::create_subscription<
|
||||
MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
MessageT, CallbackT, AllocatorT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
node_parameters, node_topics, topic_name, qos,
|
||||
std::forward<CallbackT>(callback), options, msg_mem_strat);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/buffers/ring_buffer_implementation.hpp"
|
||||
#include "rclcpp/intra_process_buffer_type.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -37,13 +38,13 @@ template<
|
||||
typename rclcpp::experimental::buffers::IntraProcessBuffer<MessageT, Alloc, Deleter>::UniquePtr
|
||||
create_intra_process_buffer(
|
||||
IntraProcessBufferType buffer_type,
|
||||
rmw_qos_profile_t qos,
|
||||
const rclcpp::QoS & qos,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
{
|
||||
using MessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
|
||||
|
||||
size_t buffer_size = qos.depth;
|
||||
size_t buffer_size = qos.depth();
|
||||
|
||||
using rclcpp::experimental::buffers::IntraProcessBuffer;
|
||||
typename IntraProcessBuffer<MessageT, Alloc, Deleter>::UniquePtr buffer;
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
|
||||
#include "rclcpp/logger.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
@@ -181,7 +182,7 @@ public:
|
||||
do_intra_process_publish(
|
||||
uint64_t intra_process_publisher_id,
|
||||
std::unique_ptr<MessageT, Deleter> message,
|
||||
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
|
||||
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAllocatorT = typename MessageAllocTraits::allocator_type;
|
||||
@@ -202,7 +203,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)
|
||||
{
|
||||
@@ -225,9 +227,9 @@ public:
|
||||
{
|
||||
// Construct a new shared pointer from the message
|
||||
// for the buffers that do not require ownership
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
|
||||
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);
|
||||
@@ -242,7 +244,7 @@ public:
|
||||
do_intra_process_publish_and_return_shared(
|
||||
uint64_t intra_process_publisher_id,
|
||||
std::unique_ptr<MessageT, Deleter> message,
|
||||
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
|
||||
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAllocatorT = typename MessageAllocTraits::allocator_type;
|
||||
@@ -263,17 +265,17 @@ 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;
|
||||
} else {
|
||||
// Construct a new shared pointer from the message for the buffers that
|
||||
// do not require ownership and to return.
|
||||
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
|
||||
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);
|
||||
}
|
||||
@@ -303,25 +305,6 @@ public:
|
||||
get_subscription_intra_process(uint64_t intra_process_subscription_id);
|
||||
|
||||
private:
|
||||
struct SubscriptionInfo
|
||||
{
|
||||
SubscriptionInfo() = default;
|
||||
|
||||
rclcpp::experimental::SubscriptionIntraProcessBase::WeakPtr subscription;
|
||||
rmw_qos_profile_t qos;
|
||||
const char * topic_name;
|
||||
bool use_take_shared_method;
|
||||
};
|
||||
|
||||
struct PublisherInfo
|
||||
{
|
||||
PublisherInfo() = default;
|
||||
|
||||
rclcpp::PublisherBase::WeakPtr publisher;
|
||||
rmw_qos_profile_t qos;
|
||||
const char * topic_name;
|
||||
};
|
||||
|
||||
struct SplittedSubscriptions
|
||||
{
|
||||
std::vector<uint64_t> take_shared_subscriptions;
|
||||
@@ -329,10 +312,10 @@ private:
|
||||
};
|
||||
|
||||
using SubscriptionMap =
|
||||
std::unordered_map<uint64_t, SubscriptionInfo>;
|
||||
std::unordered_map<uint64_t, rclcpp::experimental::SubscriptionIntraProcessBase::WeakPtr>;
|
||||
|
||||
using PublisherMap =
|
||||
std::unordered_map<uint64_t, PublisherInfo>;
|
||||
std::unordered_map<uint64_t, rclcpp::PublisherBase::WeakPtr>;
|
||||
|
||||
using PublisherToSubscriptionIdsMap =
|
||||
std::unordered_map<uint64_t, SplittedSubscriptions>;
|
||||
@@ -348,9 +331,14 @@ private:
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
can_communicate(PublisherInfo pub_info, SubscriptionInfo sub_info) const;
|
||||
can_communicate(
|
||||
rclcpp::PublisherBase::SharedPtr pub,
|
||||
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr sub) const;
|
||||
|
||||
template<typename MessageT>
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc,
|
||||
typename Deleter>
|
||||
void
|
||||
add_shared_msg_to_buffers(
|
||||
std::shared_ptr<const MessageT> message,
|
||||
@@ -361,11 +349,18 @@ private:
|
||||
if (subscription_it == subscriptions_.end()) {
|
||||
throw std::runtime_error("subscription has unexpectedly gone out of scope");
|
||||
}
|
||||
auto subscription_base = subscription_it->second.subscription.lock();
|
||||
auto subscription_base = subscription_it->second.lock();
|
||||
if (subscription_base) {
|
||||
auto subscription = std::static_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
|
||||
auto subscription = std::dynamic_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>
|
||||
>(subscription_base);
|
||||
if (nullptr == subscription) {
|
||||
throw std::runtime_error(
|
||||
"failed to dynamic cast SubscriptionIntraProcessBase to "
|
||||
"SubscriptionIntraProcessBuffer<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 {
|
||||
@@ -382,7 +377,7 @@ private:
|
||||
add_owned_msg_to_buffers(
|
||||
std::unique_ptr<MessageT, Deleter> message,
|
||||
std::vector<uint64_t> subscription_ids,
|
||||
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
|
||||
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
|
||||
{
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
|
||||
@@ -392,11 +387,18 @@ private:
|
||||
if (subscription_it == subscriptions_.end()) {
|
||||
throw std::runtime_error("subscription has unexpectedly gone out of scope");
|
||||
}
|
||||
auto subscription_base = subscription_it->second.subscription.lock();
|
||||
auto subscription_base = subscription_it->second.lock();
|
||||
if (subscription_base) {
|
||||
auto subscription = std::static_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
|
||||
auto subscription = std::dynamic_pointer_cast<
|
||||
rclcpp::experimental::SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>
|
||||
>(subscription_base);
|
||||
if (nullptr == subscription) {
|
||||
throw std::runtime_error(
|
||||
"failed to dynamic cast SubscriptionIntraProcessBase to "
|
||||
"SubscriptionIntraProcessBuffer<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
|
||||
@@ -405,8 +407,8 @@ private:
|
||||
// Copy the message since we have additional subscriptions to serve
|
||||
MessageUniquePtr copy_message;
|
||||
Deleter deleter = message.get_deleter();
|
||||
auto ptr = MessageAllocTraits::allocate(*allocator.get(), 1);
|
||||
MessageAllocTraits::construct(*allocator.get(), ptr, *message);
|
||||
auto ptr = MessageAllocTraits::allocate(allocator, 1);
|
||||
MessageAllocTraits::construct(allocator, ptr, *message);
|
||||
copy_message = MessageUniquePtr(ptr, deleter);
|
||||
|
||||
subscription->provide_intra_process_message(std::move(copy_message));
|
||||
|
||||
@@ -30,6 +30,8 @@
|
||||
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
#include "tracetools/tracetools.h"
|
||||
@@ -44,54 +46,43 @@ template<
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>,
|
||||
typename CallbackMessageT = MessageT>
|
||||
class SubscriptionIntraProcess : public SubscriptionIntraProcessBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
|
||||
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
|
||||
|
||||
using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
|
||||
class SubscriptionIntraProcess
|
||||
: public SubscriptionIntraProcessBuffer<
|
||||
MessageT,
|
||||
Alloc,
|
||||
Deleter
|
||||
>::UniquePtr;
|
||||
>
|
||||
{
|
||||
using SubscriptionIntraProcessBufferT = SubscriptionIntraProcessBuffer<
|
||||
MessageT,
|
||||
Alloc,
|
||||
Deleter
|
||||
>;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
|
||||
|
||||
using MessageAllocTraits = typename SubscriptionIntraProcessBufferT::MessageAllocTraits;
|
||||
using MessageAlloc = typename SubscriptionIntraProcessBufferT::MessageAlloc;
|
||||
using ConstMessageSharedPtr = typename SubscriptionIntraProcessBufferT::ConstMessageSharedPtr;
|
||||
using MessageUniquePtr = typename SubscriptionIntraProcessBufferT::MessageUniquePtr;
|
||||
using BufferUniquePtr = typename SubscriptionIntraProcessBufferT::BufferUniquePtr;
|
||||
|
||||
SubscriptionIntraProcess(
|
||||
AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
|
||||
std::shared_ptr<Alloc> allocator,
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::string & topic_name,
|
||||
rmw_qos_profile_t qos_profile,
|
||||
const rclcpp::QoS & qos_profile,
|
||||
rclcpp::IntraProcessBufferType buffer_type)
|
||||
: SubscriptionIntraProcessBase(topic_name, qos_profile),
|
||||
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(
|
||||
allocator,
|
||||
context,
|
||||
topic_name,
|
||||
qos_profile,
|
||||
buffer_type),
|
||||
any_callback_(callback)
|
||||
{
|
||||
if (!std::is_same<MessageT, CallbackMessageT>::value) {
|
||||
throw std::runtime_error("SubscriptionIntraProcess wrong callback type");
|
||||
}
|
||||
|
||||
// Create the intra-process buffer.
|
||||
buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
|
||||
buffer_type,
|
||||
qos_profile,
|
||||
allocator);
|
||||
|
||||
// Create the guard condition.
|
||||
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_, context->get_rcl_context().get(), guard_condition_options);
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw std::runtime_error("SubscriptionIntraProcess init error initializing guard condition");
|
||||
}
|
||||
|
||||
TRACEPOINT(
|
||||
rclcpp_subscription_callback_added,
|
||||
static_cast<const void *>(this),
|
||||
@@ -104,22 +95,7 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
~SubscriptionIntraProcess()
|
||||
{
|
||||
if (rcl_guard_condition_fini(&gc_) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Failed to destroy guard condition: %s",
|
||||
rcutils_get_error_string().str);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
(void) wait_set;
|
||||
return buffer_->has_data();
|
||||
}
|
||||
virtual ~SubscriptionIntraProcess() = default;
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data()
|
||||
@@ -128,9 +104,9 @@ public:
|
||||
MessageUniquePtr unique_msg;
|
||||
|
||||
if (any_callback_.use_take_shared_method()) {
|
||||
shared_msg = buffer_->consume_shared();
|
||||
shared_msg = this->buffer_->consume_shared();
|
||||
} else {
|
||||
unique_msg = buffer_->consume_unique();
|
||||
unique_msg = this->buffer_->consume_unique();
|
||||
}
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
|
||||
@@ -141,37 +117,10 @@ public:
|
||||
|
||||
void execute(std::shared_ptr<void> & data)
|
||||
{
|
||||
execute_impl<CallbackMessageT>(data);
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(ConstMessageSharedPtr message)
|
||||
{
|
||||
buffer_->add_shared(std::move(message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(MessageUniquePtr message)
|
||||
{
|
||||
buffer_->add_unique(std::move(message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
|
||||
bool
|
||||
use_take_shared_method() const
|
||||
{
|
||||
return buffer_->use_take_shared_method();
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
trigger_guard_condition()
|
||||
{
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(&gc_);
|
||||
(void)ret;
|
||||
execute_impl<MessageT>(data);
|
||||
}
|
||||
|
||||
protected:
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_same<T, rcl_serialized_message_t>::value, void>::type
|
||||
execute_impl(std::shared_ptr<void> & data)
|
||||
@@ -206,7 +155,6 @@ private:
|
||||
}
|
||||
|
||||
AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
|
||||
BufferUniquePtr buffer_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
@@ -39,7 +40,9 @@ public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(SubscriptionIntraProcessBase)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
SubscriptionIntraProcessBase(const std::string & topic_name, rmw_qos_profile_t qos_profile)
|
||||
SubscriptionIntraProcessBase(
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos_profile)
|
||||
: topic_name_(topic_name), qos_profile_(qos_profile)
|
||||
{}
|
||||
|
||||
@@ -71,7 +74,7 @@ public:
|
||||
get_topic_name() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rmw_qos_profile_t
|
||||
QoS
|
||||
get_actual_qos() const;
|
||||
|
||||
protected:
|
||||
@@ -83,7 +86,7 @@ private:
|
||||
trigger_guard_condition() = 0;
|
||||
|
||||
std::string topic_name_;
|
||||
rmw_qos_profile_t qos_profile_;
|
||||
QoS qos_profile_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
|
||||
@@ -0,0 +1,143 @@
|
||||
// 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__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
|
||||
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
#include "tracetools/tracetools.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace experimental
|
||||
{
|
||||
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>
|
||||
>
|
||||
class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
|
||||
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
|
||||
|
||||
using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
|
||||
MessageT,
|
||||
Alloc,
|
||||
Deleter
|
||||
>::UniquePtr;
|
||||
|
||||
SubscriptionIntraProcessBuffer(
|
||||
std::shared_ptr<Alloc> allocator,
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos_profile,
|
||||
rclcpp::IntraProcessBufferType buffer_type)
|
||||
: SubscriptionIntraProcessBase(topic_name, qos_profile)
|
||||
{
|
||||
// Create the intra-process buffer.
|
||||
buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
|
||||
buffer_type,
|
||||
qos_profile,
|
||||
allocator);
|
||||
|
||||
// Create the guard condition.
|
||||
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_, context->get_rcl_context().get(), guard_condition_options);
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw std::runtime_error(
|
||||
"SubscriptionIntraProcessBuffer init error initializing guard condition");
|
||||
}
|
||||
}
|
||||
|
||||
virtual ~SubscriptionIntraProcessBuffer()
|
||||
{
|
||||
if (rcl_guard_condition_fini(&gc_) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Failed to destroy guard condition: %s",
|
||||
rcutils_get_error_string().str);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
(void) wait_set;
|
||||
return buffer_->has_data();
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(ConstMessageSharedPtr message)
|
||||
{
|
||||
buffer_->add_shared(std::move(message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
|
||||
void
|
||||
provide_intra_process_message(MessageUniquePtr message)
|
||||
{
|
||||
buffer_->add_unique(std::move(message));
|
||||
trigger_guard_condition();
|
||||
}
|
||||
|
||||
bool
|
||||
use_take_shared_method() const
|
||||
{
|
||||
return buffer_->use_take_shared_method();
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
trigger_guard_condition()
|
||||
{
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(&gc_);
|
||||
(void)ret;
|
||||
}
|
||||
|
||||
BufferUniquePtr buffer_;
|
||||
};
|
||||
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
@@ -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_
|
||||
168
rclcpp/include/rclcpp/generic_subscription.hpp
Normal file
168
rclcpp/include/rclcpp/generic_subscription.hpp
Normal file
@@ -0,0 +1,168 @@
|
||||
// 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;
|
||||
|
||||
/// Handle dispatching rclcpp::SerializedMessage to user callback.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_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_
|
||||
90
rclcpp/include/rclcpp/get_message_type_support_handle.hpp
Normal file
90
rclcpp/include/rclcpp/get_message_type_support_handle.hpp
Normal file
@@ -0,0 +1,90 @@
|
||||
// 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__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_
|
||||
#define RCLCPP__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
#include "rosidl_runtime_cpp/traits.hpp"
|
||||
#include "rosidl_runtime_cpp/message_type_support_decl.hpp"
|
||||
#include "rosidl_typesupport_cpp/message_type_support.hpp"
|
||||
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
|
||||
/// Versions of rosidl_typesupport_cpp::get_message_type_support_handle that handle adapted types.
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Specialization for when MessageT is an actual ROS message type.
|
||||
template<typename MessageT>
|
||||
constexpr
|
||||
typename std::enable_if_t<
|
||||
rosidl_generator_traits::is_message<MessageT>::value,
|
||||
const rosidl_message_type_support_t &
|
||||
>
|
||||
get_message_type_support_handle()
|
||||
{
|
||||
auto handle = rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>();
|
||||
if (!handle) {
|
||||
throw std::runtime_error("Type support handle unexpectedly nullptr");
|
||||
}
|
||||
return *handle;
|
||||
}
|
||||
|
||||
/// Specialization for when MessageT is an adapted type using rclcpp::TypeAdapter.
|
||||
template<typename AdaptedType>
|
||||
constexpr
|
||||
typename std::enable_if_t<
|
||||
!rosidl_generator_traits::is_message<AdaptedType>::value &&
|
||||
rclcpp::TypeAdapter<AdaptedType>::is_specialized::value,
|
||||
const rosidl_message_type_support_t &
|
||||
>
|
||||
get_message_type_support_handle()
|
||||
{
|
||||
auto handle = rosidl_typesupport_cpp::get_message_type_support_handle<
|
||||
typename TypeAdapter<AdaptedType>::ros_message_type
|
||||
>();
|
||||
if (!handle) {
|
||||
throw std::runtime_error("Type support handle unexpectedly nullptr");
|
||||
}
|
||||
return *handle;
|
||||
}
|
||||
|
||||
/// Specialization for when MessageT is not a ROS message nor an adapted type.
|
||||
/**
|
||||
* This specialization is a pass through runtime check, which allows a better
|
||||
* static_assert to catch this issue further down the line.
|
||||
* This should never get to be called in practice, and is purely defensive.
|
||||
*/
|
||||
template<
|
||||
typename AdaptedType
|
||||
>
|
||||
constexpr
|
||||
typename std::enable_if_t<
|
||||
!rosidl_generator_traits::is_message<AdaptedType>::value &&
|
||||
!TypeAdapter<AdaptedType>::is_specialized::value,
|
||||
const rosidl_message_type_support_t &
|
||||
>
|
||||
get_message_type_support_handle()
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"this specialization of rclcpp::get_message_type_support_handle() "
|
||||
"should never be called");
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_
|
||||
35
rclcpp/include/rclcpp/is_ros_compatible_type.hpp
Normal file
35
rclcpp/include/rclcpp/is_ros_compatible_type.hpp
Normal file
@@ -0,0 +1,35 @@
|
||||
// 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__IS_ROS_COMPATIBLE_TYPE_HPP_
|
||||
#define RCLCPP__IS_ROS_COMPATIBLE_TYPE_HPP_
|
||||
|
||||
#include "rosidl_runtime_cpp/traits.hpp"
|
||||
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
template<typename T>
|
||||
struct is_ros_compatible_type
|
||||
{
|
||||
static constexpr bool value =
|
||||
rosidl_generator_traits::is_message<T>::value ||
|
||||
rclcpp::TypeAdapter<T>::is_specialized::value;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__IS_ROS_COMPATIBLE_TYPE_HPP_
|
||||
@@ -103,6 +103,9 @@ public:
|
||||
* \param[in] allocator Allocator instance in case middleware can not allocate messages
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
[[
|
||||
deprecated("used the LoanedMessage constructor that does not use a shared_ptr to the allocator")
|
||||
]]
|
||||
LoanedMessage(
|
||||
const rclcpp::PublisherBase * pub,
|
||||
std::shared_ptr<std::allocator<MessageT>> allocator)
|
||||
|
||||
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"
|
||||
@@ -203,13 +206,8 @@ public:
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
|
||||
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType
|
||||
>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
@@ -266,6 +264,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.
|
||||
|
||||
@@ -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"
|
||||
@@ -84,7 +86,6 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT,
|
||||
typename SubscriptionT,
|
||||
typename MessageMemoryStrategyT>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
@@ -152,6 +153,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(
|
||||
|
||||
@@ -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_
|
||||
|
||||
@@ -15,9 +15,6 @@
|
||||
#ifndef RCLCPP__PUBLISHER_HPP_
|
||||
#define RCLCPP__PUBLISHER_HPP_
|
||||
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
@@ -27,19 +24,26 @@
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/publisher.h"
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/detail/resolve_use_intra_process.hpp"
|
||||
#include "rclcpp/experimental/intra_process_manager.hpp"
|
||||
#include "rclcpp/get_message_type_support_handle.hpp"
|
||||
#include "rclcpp/is_ros_compatible_type.hpp"
|
||||
#include "rclcpp/loaned_message.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/publisher_base.hpp"
|
||||
#include "rclcpp/publisher_options.hpp"
|
||||
#include "rclcpp/type_adapter.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "tracetools/tracetools.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
@@ -47,15 +51,62 @@ template<typename MessageT, typename AllocatorT>
|
||||
class LoanedMessage;
|
||||
|
||||
/// A publisher publishes messages of any type to a topic.
|
||||
/**
|
||||
* MessageT must be a:
|
||||
*
|
||||
* - ROS message type with its own type support (e.g. std_msgs::msgs::String), or a
|
||||
* - rclcpp::TypeAdapter<CustomType, ROSMessageType>
|
||||
* (e.g. rclcpp::TypeAdapter<std::string, std_msgs::msg::String), or a
|
||||
* - custom type that has been setup as the implicit type for a ROS type using
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(custom_type, ros_message_type)
|
||||
*
|
||||
* In the case that MessageT is ROS message type (e.g. std_msgs::msg::String),
|
||||
* both PublishedType and ROSMessageType will be that type.
|
||||
* In the case that MessageT is a TypeAdapter<CustomType, ROSMessageType> type
|
||||
* (e.g. TypeAdapter<std::string, std_msgs::msg::String>), PublishedType will
|
||||
* be the custom type, and ROSMessageType will be the ros message type.
|
||||
*
|
||||
* This is achieved because of the "identity specialization" for TypeAdapter,
|
||||
* which returns itself if it is already a TypeAdapter, and the default
|
||||
* specialization which allows ROSMessageType to be void.
|
||||
* \sa rclcpp::TypeAdapter for more details.
|
||||
*/
|
||||
template<typename MessageT, typename AllocatorT = std::allocator<void>>
|
||||
class Publisher : public PublisherBase
|
||||
{
|
||||
public:
|
||||
using MessageAllocatorTraits = allocator::AllocRebind<MessageT, AllocatorT>;
|
||||
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAllocator, MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
|
||||
using MessageSharedPtr = std::shared_ptr<const MessageT>;
|
||||
static_assert(
|
||||
rclcpp::is_ros_compatible_type<MessageT>::value,
|
||||
"given message type is not compatible with ROS and cannot be used with a Publisher");
|
||||
|
||||
/// MessageT::custom_type if MessageT is a TypeAdapter, otherwise just MessageT.
|
||||
using PublishedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
|
||||
/// MessageT::ros_message_type if MessageT is a TypeAdapter, otherwise just MessageT.
|
||||
using ROSMessageType = typename rclcpp::TypeAdapter<MessageT>::ros_message_type;
|
||||
|
||||
using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, AllocatorT>;
|
||||
using PublishedTypeAllocator = typename PublishedTypeAllocatorTraits::allocator_type;
|
||||
using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
|
||||
|
||||
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, AllocatorT>;
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
|
||||
|
||||
using MessageAllocatorTraits
|
||||
[[deprecated("use PublishedTypeAllocatorTraits")]] =
|
||||
PublishedTypeAllocatorTraits;
|
||||
using MessageAllocator
|
||||
[[deprecated("use PublishedTypeAllocator")]] =
|
||||
PublishedTypeAllocator;
|
||||
using MessageDeleter
|
||||
[[deprecated("use PublishedTypeDeleter")]] =
|
||||
PublishedTypeDeleter;
|
||||
using MessageUniquePtr
|
||||
[[deprecated("use std::unique_ptr<PublishedType, PublishedTypeDeleter>")]] =
|
||||
std::unique_ptr<PublishedType, PublishedTypeDeleter>;
|
||||
using MessageSharedPtr
|
||||
[[deprecated("use std::shared_ptr<const PublishedType>")]] =
|
||||
std::shared_ptr<const PublishedType>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, AllocatorT>)
|
||||
|
||||
@@ -78,12 +129,14 @@ public:
|
||||
: PublisherBase(
|
||||
node_base,
|
||||
topic,
|
||||
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
|
||||
rclcpp::get_message_type_support_handle<MessageT>(),
|
||||
options.template to_rcl_publisher_options<MessageT>(qos)),
|
||||
options_(options),
|
||||
message_allocator_(new MessageAllocator(*options.get_allocator().get()))
|
||||
published_type_allocator_(*options.get_allocator()),
|
||||
ros_message_type_allocator_(*options.get_allocator())
|
||||
{
|
||||
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
|
||||
allocator::set_allocator_for_deleter(&published_type_deleter_, &published_type_allocator_);
|
||||
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
|
||||
|
||||
if (options_.event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
@@ -133,15 +186,15 @@ public:
|
||||
// Get the intra process manager instance for this context.
|
||||
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
|
||||
// Register the publisher with the intra process manager.
|
||||
if (qos.get_rmw_qos_profile().history == RMW_QOS_POLICY_HISTORY_KEEP_ALL) {
|
||||
if (qos.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with keep all history qos policy");
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos.get_rmw_qos_profile().depth == 0) {
|
||||
if (qos.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with a zero qos history depth value");
|
||||
}
|
||||
if (qos.get_rmw_qos_profile().durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
|
||||
if (qos.durability() != rclcpp::DurabilityPolicy::Volatile) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication allowed only with volatile durability");
|
||||
}
|
||||
@@ -168,21 +221,33 @@ public:
|
||||
* allocator.
|
||||
* \sa rclcpp::LoanedMessage for details of the LoanedMessage class.
|
||||
*
|
||||
* \return LoanedMessage containing memory for a ROS message of type MessageT
|
||||
* \return LoanedMessage containing memory for a ROS message of type ROSMessageType
|
||||
*/
|
||||
rclcpp::LoanedMessage<MessageT, AllocatorT>
|
||||
rclcpp::LoanedMessage<ROSMessageType, AllocatorT>
|
||||
borrow_loaned_message()
|
||||
{
|
||||
return rclcpp::LoanedMessage<MessageT, AllocatorT>(this, this->get_allocator());
|
||||
return rclcpp::LoanedMessage<ROSMessageType, AllocatorT>(
|
||||
*this,
|
||||
this->get_ros_message_type_allocator());
|
||||
}
|
||||
|
||||
/// Send a message to the topic for this publisher.
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This function is templated on the input message type, MessageT.
|
||||
* \param[in] msg A shared pointer to the message to send.
|
||||
* This signature is enabled if the element_type of the std::unique_ptr is
|
||||
* a ROS message type, as opposed to the custom_type of a TypeAdapter, and
|
||||
* that type matches the type given when creating the publisher.
|
||||
*
|
||||
* This signature allows the user to give ownership of the message to rclcpp,
|
||||
* allowing for more efficient intra-process communication optimizations.
|
||||
*
|
||||
* \param[in] msg A unique pointer to the message to send.
|
||||
*/
|
||||
virtual void
|
||||
publish(std::unique_ptr<MessageT, MessageDeleter> msg)
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rosidl_generator_traits::is_message<T>::value &&
|
||||
std::is_same<T, ROSMessageType>::value
|
||||
>
|
||||
publish(std::unique_ptr<T, ROSMessageTypeDeleter> msg)
|
||||
{
|
||||
if (!intra_process_is_enabled_) {
|
||||
this->do_inter_process_publish(*msg);
|
||||
@@ -205,8 +270,24 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void
|
||||
publish(const MessageT & msg)
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This signature is enabled if the object being published is
|
||||
* a ROS message type, as opposed to the custom_type of a TypeAdapter, and
|
||||
* that type matches the type given when creating the publisher.
|
||||
*
|
||||
* This signature allows the user to give a reference to a message, which is
|
||||
* copied onto the heap without modification so that a copy can be owned by
|
||||
* rclcpp and ownership of the copy can be moved later if needed.
|
||||
*
|
||||
* \param[in] msg A const reference to the message to send.
|
||||
*/
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rosidl_generator_traits::is_message<T>::value &&
|
||||
std::is_same<T, ROSMessageType>::value
|
||||
>
|
||||
publish(const T & msg)
|
||||
{
|
||||
// Avoid allocating when not using intra process.
|
||||
if (!intra_process_is_enabled_) {
|
||||
@@ -216,12 +297,77 @@ public:
|
||||
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
|
||||
// As the message is not const, a copy should be made.
|
||||
// A shared_ptr<const MessageT> could also be constructed here.
|
||||
auto ptr = MessageAllocatorTraits::allocate(*message_allocator_.get(), 1);
|
||||
MessageAllocatorTraits::construct(*message_allocator_.get(), ptr, msg);
|
||||
MessageUniquePtr unique_msg(ptr, message_deleter_);
|
||||
auto unique_msg = this->duplicate_ros_message_as_unique_ptr(msg);
|
||||
this->publish(std::move(unique_msg));
|
||||
}
|
||||
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This signature is enabled if this class was created with a TypeAdapter and
|
||||
* the element_type of the std::unique_ptr matches the custom_type for the
|
||||
* TypeAdapter used with this class.
|
||||
*
|
||||
* This signature allows the user to give ownership of the message to rclcpp,
|
||||
* allowing for more efficient intra-process communication optimizations.
|
||||
*
|
||||
* \param[in] msg A unique pointer to the message to send.
|
||||
*/
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rclcpp::TypeAdapter<MessageT>::is_specialized::value &&
|
||||
std::is_same<T, PublishedType>::value
|
||||
>
|
||||
publish(std::unique_ptr<T, PublishedTypeDeleter> msg)
|
||||
{
|
||||
// TODO(wjwwood): later update this to give the unique_ptr to the intra
|
||||
// process manager and let it decide if it needs to be converted or not.
|
||||
// For now, convert it unconditionally and pass it the ROSMessageType
|
||||
// publish function specialization.
|
||||
auto unique_ros_msg = this->create_ros_message_unique_ptr();
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*msg, *unique_ros_msg);
|
||||
this->publish(std::move(unique_ros_msg));
|
||||
}
|
||||
|
||||
/// Publish a message on the topic.
|
||||
/**
|
||||
* This signature is enabled if this class was created with a TypeAdapter and
|
||||
* the given type matches the custom_type of the TypeAdapter.
|
||||
*
|
||||
* This signature allows the user to give a reference to a message, which is
|
||||
* copied onto the heap without modification so that a copy can be owned by
|
||||
* rclcpp and ownership of the copy can be moved later if needed.
|
||||
*
|
||||
* \param[in] msg A const reference to the message to send.
|
||||
*/
|
||||
template<typename T>
|
||||
typename std::enable_if_t<
|
||||
rclcpp::TypeAdapter<MessageT>::is_specialized::value &&
|
||||
std::is_same<T, PublishedType>::value
|
||||
>
|
||||
publish(const T & msg)
|
||||
{
|
||||
// TODO(wjwwood): later update this to give the unique_ptr to the intra
|
||||
// process manager and let it decide if it needs to be converted or not.
|
||||
// For now, convert it unconditionally and pass it the ROSMessageType
|
||||
// publish function specialization.
|
||||
|
||||
// Avoid allocating when not using intra process.
|
||||
if (!intra_process_is_enabled_) {
|
||||
// Convert to the ROS message equivalent and publish it.
|
||||
ROSMessageType ros_msg;
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(msg, ros_msg);
|
||||
// In this case we're not using intra process.
|
||||
return this->do_inter_process_publish(ros_msg);
|
||||
}
|
||||
// Otherwise we have to allocate memory in a unique_ptr, convert it,
|
||||
// and pass it along.
|
||||
// As the message is not const, a copy should be made.
|
||||
// A shared_ptr<const MessageT> could also be constructed here.
|
||||
auto unique_ros_msg = this->create_ros_message_unique_ptr();
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(msg, *unique_ros_msg);
|
||||
this->publish(std::move(unique_ros_msg));
|
||||
}
|
||||
|
||||
void
|
||||
publish(const rcl_serialized_message_t & serialized_msg)
|
||||
{
|
||||
@@ -243,7 +389,7 @@ public:
|
||||
* \param loaned_msg The LoanedMessage instance to be published.
|
||||
*/
|
||||
void
|
||||
publish(rclcpp::LoanedMessage<MessageT, AllocatorT> && loaned_msg)
|
||||
publish(rclcpp::LoanedMessage<ROSMessageType, AllocatorT> && loaned_msg)
|
||||
{
|
||||
if (!loaned_msg.is_valid()) {
|
||||
throw std::runtime_error("loaned message is not valid");
|
||||
@@ -269,16 +415,33 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<MessageAllocator>
|
||||
[[deprecated("use get_published_type_allocator() or get_ros_message_type_allocator() instead")]]
|
||||
std::shared_ptr<PublishedTypeAllocator>
|
||||
get_allocator() const
|
||||
{
|
||||
return message_allocator_;
|
||||
return std::make_shared<PublishedTypeAllocator>(published_type_allocator_);
|
||||
}
|
||||
|
||||
PublishedTypeAllocator
|
||||
get_published_type_allocator() const
|
||||
{
|
||||
return published_type_allocator_;
|
||||
}
|
||||
|
||||
ROSMessageTypeAllocator
|
||||
get_ros_message_type_allocator() const
|
||||
{
|
||||
return ros_message_type_allocator_;
|
||||
}
|
||||
|
||||
protected:
|
||||
void
|
||||
do_inter_process_publish(const MessageT & msg)
|
||||
do_inter_process_publish(const ROSMessageType & 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) {
|
||||
@@ -310,7 +473,8 @@ protected:
|
||||
}
|
||||
|
||||
void
|
||||
do_loaned_message_publish(std::unique_ptr<MessageT, std::function<void(MessageT *)>> msg)
|
||||
do_loaned_message_publish(
|
||||
std::unique_ptr<ROSMessageType, std::function<void(ROSMessageType *)>> msg)
|
||||
{
|
||||
auto status = rcl_publish_loaned_message(publisher_handle_.get(), msg.get(), nullptr);
|
||||
|
||||
@@ -330,7 +494,7 @@ protected:
|
||||
}
|
||||
|
||||
void
|
||||
do_intra_process_publish(std::unique_ptr<MessageT, MessageDeleter> msg)
|
||||
do_intra_process_publish(std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> msg)
|
||||
{
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
@@ -341,14 +505,15 @@ protected:
|
||||
throw std::runtime_error("cannot publish msg which is a null pointer");
|
||||
}
|
||||
|
||||
ipm->template do_intra_process_publish<MessageT, AllocatorT>(
|
||||
ipm->template do_intra_process_publish<ROSMessageType, AllocatorT>(
|
||||
intra_process_publisher_id_,
|
||||
std::move(msg),
|
||||
message_allocator_);
|
||||
ros_message_type_allocator_);
|
||||
}
|
||||
|
||||
std::shared_ptr<const MessageT>
|
||||
do_intra_process_publish_and_return_shared(std::unique_ptr<MessageT, MessageDeleter> msg)
|
||||
std::shared_ptr<const ROSMessageType>
|
||||
do_intra_process_publish_and_return_shared(
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> msg)
|
||||
{
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
@@ -359,10 +524,29 @@ protected:
|
||||
throw std::runtime_error("cannot publish msg which is a null pointer");
|
||||
}
|
||||
|
||||
return ipm->template do_intra_process_publish_and_return_shared<MessageT, AllocatorT>(
|
||||
return ipm->template do_intra_process_publish_and_return_shared<ROSMessageType,
|
||||
AllocatorT>(
|
||||
intra_process_publisher_id_,
|
||||
std::move(msg),
|
||||
message_allocator_);
|
||||
ros_message_type_allocator_);
|
||||
}
|
||||
|
||||
/// Return a new unique_ptr using the ROSMessageType of the publisher.
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
|
||||
create_ros_message_unique_ptr()
|
||||
{
|
||||
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_type_allocator_, 1);
|
||||
ROSMessageTypeAllocatorTraits::construct(ros_message_type_allocator_, ptr);
|
||||
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
|
||||
}
|
||||
|
||||
/// Duplicate a given ros message as a unique_ptr.
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
|
||||
duplicate_ros_message_as_unique_ptr(const ROSMessageType & msg)
|
||||
{
|
||||
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_type_allocator_, 1);
|
||||
ROSMessageTypeAllocatorTraits::construct(ros_message_type_allocator_, ptr, msg);
|
||||
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
|
||||
}
|
||||
|
||||
/// Copy of original options passed during construction.
|
||||
@@ -372,9 +556,10 @@ protected:
|
||||
*/
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> options_;
|
||||
|
||||
std::shared_ptr<MessageAllocator> message_allocator_;
|
||||
|
||||
MessageDeleter message_deleter_;
|
||||
PublishedTypeAllocator published_type_allocator_;
|
||||
PublishedTypeDeleter published_type_deleter_;
|
||||
ROSMessageTypeAllocator ros_message_type_allocator_;
|
||||
ROSMessageTypeDeleter ros_message_type_deleter_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/publisher.h"
|
||||
@@ -45,6 +46,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;
|
||||
|
||||
@@ -59,6 +65,10 @@ struct PublisherOptionsBase
|
||||
template<typename Allocator>
|
||||
struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
{
|
||||
static_assert(
|
||||
std::is_void_v<typename std::allocator_traits<Allocator>::value_type>,
|
||||
"Publisher allocator value type must be void");
|
||||
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
|
||||
@@ -75,11 +85,10 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
to_rcl_publisher_options(const rclcpp::QoS & qos) const
|
||||
{
|
||||
rcl_publisher_options_t result = rcl_publisher_get_default_options();
|
||||
using AllocatorTraits = std::allocator_traits<Allocator>;
|
||||
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
|
||||
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
|
||||
result.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(*message_alloc);
|
||||
result.allocator = this->get_rcl_allocator();
|
||||
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()) {
|
||||
@@ -95,10 +104,35 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
|
||||
get_allocator() const
|
||||
{
|
||||
if (!this->allocator) {
|
||||
return std::make_shared<Allocator>();
|
||||
if (!allocator_storage_) {
|
||||
allocator_storage_ = std::make_shared<Allocator>();
|
||||
}
|
||||
return allocator_storage_;
|
||||
}
|
||||
return this->allocator;
|
||||
}
|
||||
|
||||
private:
|
||||
using PlainAllocator =
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<char>;
|
||||
|
||||
rcl_allocator_t
|
||||
get_rcl_allocator() const
|
||||
{
|
||||
if (!plain_allocator_storage_) {
|
||||
plain_allocator_storage_ =
|
||||
std::make_shared<PlainAllocator>(*this->get_allocator());
|
||||
}
|
||||
return rclcpp::allocator::get_rcl_allocator<char>(*plain_allocator_storage_);
|
||||
}
|
||||
|
||||
// This is a temporal workaround, to make sure that get_allocator()
|
||||
// always returns a copy of the same allocator.
|
||||
mutable std::shared_ptr<Allocator> allocator_storage_;
|
||||
|
||||
// This is a temporal workaround, to keep the plain allocator that backs
|
||||
// up the rcl allocator returned in rcl_publisher_options_t alive.
|
||||
mutable std::shared_ptr<PlainAllocator> plain_allocator_storage_;
|
||||
};
|
||||
|
||||
using PublisherOptions = PublisherOptionsWithAllocator<std::allocator<void>>;
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ public:
|
||||
* \param[in] initial_capacity The amount of memory to be allocated.
|
||||
* \param[in] allocator The allocator to be used for the initialization.
|
||||
*/
|
||||
SerializedMessage(
|
||||
explicit SerializedMessage(
|
||||
size_t initial_capacity,
|
||||
const rcl_allocator_t & allocator = rcl_get_default_allocator());
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -60,10 +60,16 @@ class NodeTopicsInterface;
|
||||
|
||||
/// Subscription implementation, templated on the type of message this subscription receives.
|
||||
template<
|
||||
typename CallbackMessageT,
|
||||
typename MessageT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
/// MessageT::custom_type if MessageT is a TypeAdapter,
|
||||
/// otherwise just MessageT.
|
||||
typename SubscribedT = typename rclcpp::TypeAdapter<MessageT>::custom_type,
|
||||
/// MessageT::ros_message_type if MessageT is a TypeAdapter,
|
||||
/// otherwise just MessageT.
|
||||
typename ROSMessageT = typename rclcpp::TypeAdapter<MessageT>::ros_message_type,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
ROSMessageT,
|
||||
AllocatorT
|
||||
>>
|
||||
class Subscription : public SubscriptionBase
|
||||
@@ -71,14 +77,36 @@ class Subscription : public SubscriptionBase
|
||||
friend class rclcpp::node_interfaces::NodeTopicsInterface;
|
||||
|
||||
public:
|
||||
using MessageAllocatorTraits = allocator::AllocRebind<CallbackMessageT, AllocatorT>;
|
||||
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAllocator, CallbackMessageT>;
|
||||
using ConstMessageSharedPtr = std::shared_ptr<const CallbackMessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<CallbackMessageT, MessageDeleter>;
|
||||
using SubscriptionTopicStatisticsSharedPtr =
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>;
|
||||
// Redeclare these here to use outside of the class.
|
||||
using SubscribedType = SubscribedT;
|
||||
using ROSMessageType = ROSMessageT;
|
||||
using MessageMemoryStrategyType = MessageMemoryStrategyT;
|
||||
|
||||
using SubscribedTypeAllocatorTraits = allocator::AllocRebind<SubscribedType, AllocatorT>;
|
||||
using SubscribedTypeAllocator = typename SubscribedTypeAllocatorTraits::allocator_type;
|
||||
using SubscribedTypeDeleter = allocator::Deleter<SubscribedTypeAllocator, SubscribedType>;
|
||||
|
||||
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, AllocatorT>;
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
|
||||
|
||||
using MessageAllocatorTraits [[deprecated("use ROSMessageTypeAllocatorTraits")]] =
|
||||
ROSMessageTypeAllocatorTraits;
|
||||
using MessageAllocator [[deprecated("use ROSMessageTypeAllocator")]] =
|
||||
ROSMessageTypeAllocator;
|
||||
using MessageDeleter [[deprecated("use ROSMessageTypeDeleter")]] =
|
||||
ROSMessageTypeDeleter;
|
||||
|
||||
using ConstMessageSharedPtr [[deprecated]] = std::shared_ptr<const ROSMessageType>;
|
||||
using MessageUniquePtr
|
||||
[[deprecated("use std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> instead")]] =
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>;
|
||||
|
||||
private:
|
||||
using SubscriptionTopicStatisticsSharedPtr =
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
|
||||
|
||||
/// Default constructor.
|
||||
@@ -104,7 +132,7 @@ public:
|
||||
const rosidl_message_type_support_t & type_support_handle,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
AnySubscriptionCallback<CallbackMessageT, AllocatorT> callback,
|
||||
AnySubscriptionCallback<MessageT, AllocatorT> callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
|
||||
typename MessageMemoryStrategyT::SharedPtr message_memory_strategy,
|
||||
SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics = nullptr)
|
||||
@@ -112,8 +140,8 @@ public:
|
||||
node_base,
|
||||
type_support_handle,
|
||||
topic_name,
|
||||
options.template to_rcl_subscription_options<CallbackMessageT>(qos),
|
||||
rclcpp::subscription_traits::is_serialized_subscription_argument<CallbackMessageT>::value),
|
||||
options.template to_rcl_subscription_options<ROSMessageType>(qos),
|
||||
callback.is_serialized_message_callback()),
|
||||
any_callback_(callback),
|
||||
options_(options),
|
||||
message_memory_strategy_(message_memory_strategy)
|
||||
@@ -155,16 +183,16 @@ public:
|
||||
using rclcpp::detail::resolve_intra_process_buffer_type;
|
||||
|
||||
// Check if the QoS is compatible with intra-process.
|
||||
rmw_qos_profile_t qos_profile = get_actual_qos().get_rmw_qos_profile();
|
||||
if (qos_profile.history == RMW_QOS_POLICY_HISTORY_KEEP_ALL) {
|
||||
auto qos_profile = get_actual_qos();
|
||||
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with keep all history qos policy");
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos_profile.depth == 0) {
|
||||
if (qos_profile.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with 0 depth qos policy");
|
||||
}
|
||||
if (qos_profile.durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
|
||||
if (qos_profile.durability() != rclcpp::DurabilityPolicy::Volatile) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication allowed only with volatile durability");
|
||||
}
|
||||
@@ -241,11 +269,34 @@ public:
|
||||
* \throws any rcl errors from rcl_take, \sa rclcpp::exceptions::throw_from_rcl_error()
|
||||
*/
|
||||
bool
|
||||
take(CallbackMessageT & message_out, rclcpp::MessageInfo & message_info_out)
|
||||
take(ROSMessageType & message_out, rclcpp::MessageInfo & message_info_out)
|
||||
{
|
||||
return this->take_type_erased(static_cast<void *>(&message_out), message_info_out);
|
||||
}
|
||||
|
||||
/// Take the next message from the inter-process subscription.
|
||||
/**
|
||||
* This verison takes a SubscribedType which is different frmo the
|
||||
* ROSMessageType when a rclcpp::TypeAdapter is in used.
|
||||
*
|
||||
* \sa take(ROSMessageType &, rclcpp::MessageInfo &)
|
||||
*/
|
||||
template<typename TakeT>
|
||||
std::enable_if_t<
|
||||
!rosidl_generator_traits::is_message<TakeT>::value &&
|
||||
std::is_same_v<TakeT, SubscribedType>,
|
||||
bool
|
||||
>
|
||||
take(TakeT & message_out, rclcpp::MessageInfo & message_info_out)
|
||||
{
|
||||
ROSMessageType local_message;
|
||||
bool taken = this->take_type_erased(static_cast<void *>(&local_message), message_info_out);
|
||||
if (taken) {
|
||||
rclcpp::TypeAdapter<MessageT>::convert_to_custom(local_message, message_out);
|
||||
}
|
||||
return taken;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
create_message() override
|
||||
{
|
||||
@@ -272,7 +323,7 @@ public:
|
||||
// we should ignore this copy of the message.
|
||||
return;
|
||||
}
|
||||
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
|
||||
auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
|
||||
|
||||
std::chrono::time_point<std::chrono::system_clock> now;
|
||||
if (subscription_topic_statistics_) {
|
||||
@@ -290,15 +341,24 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
|
||||
const rclcpp::MessageInfo & message_info) override
|
||||
{
|
||||
// TODO(wjwwood): enable topic statistics for serialized messages
|
||||
any_callback_.dispatch(serialized_message, message_info);
|
||||
}
|
||||
|
||||
void
|
||||
handle_loaned_message(
|
||||
void * loaned_message,
|
||||
const rclcpp::MessageInfo & message_info) override
|
||||
{
|
||||
auto typed_message = static_cast<CallbackMessageT *>(loaned_message);
|
||||
auto typed_message = static_cast<ROSMessageType *>(loaned_message);
|
||||
// message is loaned, so we have to make sure that the deleter does not deallocate the message
|
||||
auto sptr = std::shared_ptr<CallbackMessageT>(
|
||||
typed_message, [](CallbackMessageT * msg) {(void) msg;});
|
||||
auto sptr = std::shared_ptr<ROSMessageType>(
|
||||
typed_message, [](ROSMessageType * msg) {(void) msg;});
|
||||
any_callback_.dispatch(sptr, message_info);
|
||||
}
|
||||
|
||||
@@ -309,7 +369,7 @@ public:
|
||||
void
|
||||
return_message(std::shared_ptr<void> & message) override
|
||||
{
|
||||
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
|
||||
auto typed_message = std::static_pointer_cast<ROSMessageType>(message);
|
||||
message_memory_strategy_->return_message(typed_message);
|
||||
}
|
||||
|
||||
@@ -332,21 +392,24 @@ public:
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Subscription)
|
||||
|
||||
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_callback_;
|
||||
AnySubscriptionCallback<MessageT, AllocatorT> any_callback_;
|
||||
/// Copy of original options passed during construction.
|
||||
/**
|
||||
* It is important to save a copy of this so that the rmw payload which it
|
||||
* may contain is kept alive for the duration of the subscription.
|
||||
*/
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> options_;
|
||||
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, AllocatorT>::SharedPtr
|
||||
typename message_memory_strategy::MessageMemoryStrategy<ROSMessageType, AllocatorT>::SharedPtr
|
||||
message_memory_strategy_;
|
||||
|
||||
/// Component which computes and publishes topic statistics for this subscriber
|
||||
SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics_{nullptr};
|
||||
|
||||
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
|
||||
CallbackMessageT,
|
||||
ROSMessageType,
|
||||
AllocatorT,
|
||||
typename MessageUniquePtr::deleter_type>;
|
||||
ROSMessageTypeDeleter,
|
||||
MessageT>;
|
||||
std::shared_ptr<SubscriptionIntraProcessT> subscription_intra_process_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -179,6 +183,13 @@ public:
|
||||
void
|
||||
handle_message(std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
|
||||
const rclcpp::MessageInfo & message_info) = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
@@ -263,6 +274,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
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "rosidl_typesupport_cpp/message_type_support.hpp"
|
||||
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "rclcpp/get_message_type_support_handle.hpp"
|
||||
#include "rclcpp/intra_process_buffer_type.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
@@ -74,26 +75,23 @@ template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT =
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
|
||||
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
CallbackMessageT,
|
||||
AllocatorT
|
||||
>>
|
||||
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
|
||||
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType,
|
||||
typename ROSMessageType = typename SubscriptionT::ROSMessageType
|
||||
>
|
||||
SubscriptionFactory
|
||||
create_subscription_factory(
|
||||
CallbackT && callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
|
||||
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat,
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>
|
||||
subscription_topic_stats = nullptr
|
||||
)
|
||||
{
|
||||
auto allocator = options.get_allocator();
|
||||
|
||||
using rclcpp::AnySubscriptionCallback;
|
||||
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_subscription_callback(allocator);
|
||||
AnySubscriptionCallback<MessageT, AllocatorT> any_subscription_callback(*allocator);
|
||||
any_subscription_callback.set(std::forward<CallbackT>(callback));
|
||||
|
||||
SubscriptionFactory factory {
|
||||
@@ -107,9 +105,9 @@ create_subscription_factory(
|
||||
using rclcpp::Subscription;
|
||||
using rclcpp::SubscriptionBase;
|
||||
|
||||
auto sub = Subscription<CallbackMessageT, AllocatorT>::make_shared(
|
||||
auto sub = Subscription<MessageT, AllocatorT>::make_shared(
|
||||
node_base,
|
||||
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
|
||||
rclcpp::get_message_type_support_handle<MessageT>(),
|
||||
topic_name,
|
||||
qos,
|
||||
any_subscription_callback,
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
@@ -45,6 +46,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;
|
||||
|
||||
@@ -81,6 +87,10 @@ struct SubscriptionOptionsBase
|
||||
template<typename Allocator>
|
||||
struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
{
|
||||
static_assert(
|
||||
std::is_void_v<typename std::allocator_traits<Allocator>::value_type>,
|
||||
"Subscription allocator value type must be void");
|
||||
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
|
||||
@@ -102,12 +112,11 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
to_rcl_subscription_options(const rclcpp::QoS & qos) const
|
||||
{
|
||||
rcl_subscription_options_t result = rcl_subscription_get_default_options();
|
||||
using AllocatorTraits = std::allocator_traits<Allocator>;
|
||||
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
|
||||
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
|
||||
result.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc);
|
||||
result.allocator = this->get_rcl_allocator();
|
||||
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()) {
|
||||
@@ -117,15 +126,39 @@ struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
|
||||
return result;
|
||||
}
|
||||
|
||||
/// Get the allocator, creating one if needed.
|
||||
std::shared_ptr<Allocator>
|
||||
get_allocator() const
|
||||
{
|
||||
if (!this->allocator) {
|
||||
return std::make_shared<Allocator>();
|
||||
if (!allocator_storage_) {
|
||||
allocator_storage_ = std::make_shared<Allocator>();
|
||||
}
|
||||
return allocator_storage_;
|
||||
}
|
||||
return this->allocator;
|
||||
}
|
||||
|
||||
private:
|
||||
using PlainAllocator =
|
||||
typename std::allocator_traits<Allocator>::template rebind_alloc<char>;
|
||||
|
||||
rcl_allocator_t
|
||||
get_rcl_allocator() const
|
||||
{
|
||||
if (!plain_allocator_storage_) {
|
||||
plain_allocator_storage_ =
|
||||
std::make_shared<PlainAllocator>(*this->get_allocator());
|
||||
}
|
||||
return rclcpp::allocator::get_rcl_allocator<char>(*plain_allocator_storage_);
|
||||
}
|
||||
|
||||
// This is a temporal workaround, to make sure that get_allocator()
|
||||
// always returns a copy of the same allocator.
|
||||
mutable std::shared_ptr<Allocator> allocator_storage_;
|
||||
|
||||
// This is a temporal workaround, to keep the plain allocator that backs
|
||||
// up the rcl allocator returned in rcl_subscription_options_t alive.
|
||||
mutable std::shared_ptr<PlainAllocator> plain_allocator_storage_;
|
||||
};
|
||||
|
||||
using SubscriptionOptions = SubscriptionOptionsWithAllocator<std::allocator<void>>;
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -74,14 +74,14 @@ public:
|
||||
const rclcpp::QoS & qos = rclcpp::ClockQoS(),
|
||||
bool use_clock_thread = true);
|
||||
|
||||
/// Attack node to the time source.
|
||||
/// Attach node to the time source.
|
||||
/**
|
||||
* \param node std::shared pointer to a initialized node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void attachNode(rclcpp::Node::SharedPtr node);
|
||||
|
||||
/// Attack node to the time source.
|
||||
/// Attach node to the time source.
|
||||
/**
|
||||
* If the parameter `use_sim_time` is `true` then the source time is the simulation time,
|
||||
* otherwise the source time is defined by the system.
|
||||
@@ -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_;
|
||||
|
||||
201
rclcpp/include/rclcpp/type_adapter.hpp
Normal file
201
rclcpp/include/rclcpp/type_adapter.hpp
Normal file
@@ -0,0 +1,201 @@
|
||||
// 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__TYPE_ADAPTER_HPP_
|
||||
#define RCLCPP__TYPE_ADAPTER_HPP_
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Template structure used to adapt custom, user-defined types to ROS types.
|
||||
/**
|
||||
* Adapting a custom, user-defined type to a ROS type allows that custom type
|
||||
* to be used when publishing and subscribing in ROS.
|
||||
*
|
||||
* In order to adapt a custom type to a ROS type, the user must create a
|
||||
* template specialization of this structure for the custom type.
|
||||
* In that specialization they must:
|
||||
*
|
||||
* - change `is_specialized` to `std::true_type`,
|
||||
* - specify the custom type with `using custom_type = ...`,
|
||||
* - specify the ROS type with `using ros_message_type = ...`,
|
||||
* - provide static convert functions with the signatures:
|
||||
* - static void convert_to_ros(const custom_type &, ros_message_type &)
|
||||
* - static void convert_to_custom(const ros_message_type &, custom_type &)
|
||||
*
|
||||
* The convert functions must convert from one type to the other.
|
||||
*
|
||||
* For example, here is a theoretical example for adapting `std::string` to the
|
||||
* `std_msgs::msg::String` ROS message type:
|
||||
*
|
||||
* template<>
|
||||
* struct rclcpp::TypeAdapter<std::string, std_msgs::msg::String>
|
||||
* {
|
||||
* using is_specialized = std::true_type;
|
||||
* using custom_type = std::string;
|
||||
* using ros_message_type = std_msgs::msg::String;
|
||||
*
|
||||
* static
|
||||
* void
|
||||
* convert_to_ros_message(
|
||||
* const custom_type & source,
|
||||
* ros_message_type & destination)
|
||||
* {
|
||||
* destination.data = source;
|
||||
* }
|
||||
*
|
||||
* static
|
||||
* void
|
||||
* convert_to_custom(
|
||||
* const ros_message_type & source,
|
||||
* custom_type & destination)
|
||||
* {
|
||||
* destination = source.data;
|
||||
* }
|
||||
* };
|
||||
*
|
||||
* The adapter can then be used when creating a publisher or subscription,
|
||||
* e.g.:
|
||||
*
|
||||
* using MyAdaptedType = TypeAdapter<std::string, std_msgs::msg::String>;
|
||||
* auto pub = node->create_publisher<MyAdaptedType>("topic", 10);
|
||||
* auto sub = node->create_subscription<MyAdaptedType>(
|
||||
* "topic",
|
||||
* 10,
|
||||
* [](const std::string & msg) {...});
|
||||
*
|
||||
* You can also be more declarative by using the adapt_type::as metafunctions,
|
||||
* which are a bit less ambiguous to read:
|
||||
*
|
||||
* using AdaptedType = rclcpp::adapt_type<std::string>::as<std_msgs::msg::String>;
|
||||
* auto pub = node->create_publisher<AdaptedType>(...);
|
||||
*
|
||||
* If you wish, you may associate a custom type with a single ROS message type,
|
||||
* allowing you to be a bit more brief when creating entities, e.g.:
|
||||
*
|
||||
* // First you must declare the association, this is similar to how you
|
||||
* // would avoid using the namespace in C++ by doing `using std::vector;`.
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
|
||||
*
|
||||
* // Then you can create things with just the custom type, and the ROS
|
||||
* // message type is implied based on the previous statement.
|
||||
* auto pub = node->create_publisher<std::string>(...);
|
||||
*/
|
||||
template<typename CustomType, typename ROSMessageType = void, class Enable = void>
|
||||
struct TypeAdapter
|
||||
{
|
||||
using is_specialized = std::false_type;
|
||||
using custom_type = CustomType;
|
||||
// In this case, the CustomType is the only thing given, or there is no specialization.
|
||||
// Assign ros_message_type to CustomType for the former case.
|
||||
using ros_message_type = CustomType;
|
||||
};
|
||||
|
||||
/// Helper template to determine if a type is a TypeAdapter, false specialization.
|
||||
template<typename T>
|
||||
struct is_type_adapter : std::false_type {};
|
||||
|
||||
/// Helper template to determine if a type is a TypeAdapter, true specialization.
|
||||
template<typename ... Ts>
|
||||
struct is_type_adapter<TypeAdapter<Ts...>>: std::true_type {};
|
||||
|
||||
/// Identity specialization for TypeAdapter.
|
||||
template<typename T>
|
||||
struct TypeAdapter<T, void, std::enable_if_t<is_type_adapter<T>::value>>: T {};
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
template<typename CustomType, typename ROSMessageType>
|
||||
struct assert_type_pair_is_specialized_type_adapter
|
||||
{
|
||||
using type_adapter = TypeAdapter<CustomType, ROSMessageType>;
|
||||
static_assert(
|
||||
type_adapter::is_specialized::value,
|
||||
"No type adapter for this custom type/ros message type pair");
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// Template metafunction that can make the type being adapted explicit.
|
||||
template<typename CustomType>
|
||||
struct adapt_type
|
||||
{
|
||||
template<typename ROSMessageType>
|
||||
using as = typename ::rclcpp::detail::assert_type_pair_is_specialized_type_adapter<
|
||||
CustomType,
|
||||
ROSMessageType
|
||||
>::type_adapter;
|
||||
};
|
||||
|
||||
/// Implicit type adapter used as a short hand way to create something with just the custom type.
|
||||
/**
|
||||
* This is used when creating a publisher or subscription using just the custom
|
||||
* type in conjunction with RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE().
|
||||
* For example:
|
||||
*
|
||||
* #include "type_adapter_for_std_string_to_std_msgs_String.hpp"
|
||||
*
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
|
||||
*
|
||||
* int main(...) {
|
||||
* // ...
|
||||
* auto pub = node->create_publisher<std::string>(...);
|
||||
* }
|
||||
*
|
||||
* \sa TypeAdapter for more examples.
|
||||
*/
|
||||
template<typename CustomType>
|
||||
struct ImplicitTypeAdapter
|
||||
{
|
||||
using is_specialized = std::false_type;
|
||||
};
|
||||
|
||||
/// Specialization of TypeAdapter for ImplicitTypeAdapter.
|
||||
/**
|
||||
* This allows for things like this:
|
||||
*
|
||||
* RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(std::string, std_msgs::msg::String);
|
||||
* auto pub = node->create_publisher<std::string>("topic", 10);
|
||||
*
|
||||
*/
|
||||
template<typename T>
|
||||
struct TypeAdapter<T, void, std::enable_if_t<ImplicitTypeAdapter<T>::is_specialized::value>>
|
||||
: ImplicitTypeAdapter<T>
|
||||
{};
|
||||
|
||||
/// Assigns the custom type implicitly to the given custom type/ros message type pair.
|
||||
/**
|
||||
* Note: this macro needs to be used in the root namespace.
|
||||
* We cannot use ::rclcpp to protect against this, due to how GCC interprets the
|
||||
* syntax, see: https://stackoverflow.com/a/2781537
|
||||
*
|
||||
* \sa TypeAdapter
|
||||
* \sa ImplicitTypeAdapter
|
||||
*/
|
||||
#define RCLCPP_USING_CUSTOM_TYPE_AS_ROS_MESSAGE_TYPE(CustomType, ROSMessageType) \
|
||||
template<> \
|
||||
struct rclcpp::ImplicitTypeAdapter<CustomType> \
|
||||
: public rclcpp::TypeAdapter<CustomType, ROSMessageType> \
|
||||
{ \
|
||||
static_assert( \
|
||||
is_specialized::value, \
|
||||
"Cannot use custom type as ros type when there is no TypeAdapter for that pair"); \
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__TYPE_ADAPTER_HPP_
|
||||
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.
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
// 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_SET_POLICIES__ALREADY_ASSOCIATED_WITH_WAIT_SET_EXCEPTION_HPP_
|
||||
#define RCLCPP__WAIT_SET_POLICIES__ALREADY_ASSOCIATED_WITH_WAIT_SET_EXCEPTION_HPP_
|
||||
|
||||
#include <stdexcept>
|
||||
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace wait_set_policies
|
||||
{
|
||||
|
||||
class AlreadyAssociatedWithWaitSetException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
template<typename EntityT>
|
||||
explicit
|
||||
AlreadyAssociatedWithWaitSetException(const EntityT & entity_instance)
|
||||
: std::runtime_error(
|
||||
"cannot associate " +
|
||||
rmw::impl::cpp::demangle(entity_instance) +
|
||||
" with wait set because it is already in use by a wait set")
|
||||
{}
|
||||
};
|
||||
|
||||
} // namespace wait_set_policies
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__WAIT_SET_POLICIES__ALREADY_ASSOCIATED_WITH_WAIT_SET_EXCEPTION_HPP_
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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_SET_POLICIES__DETAIL__CLIENT_ENTRY_HPP_
|
||||
#define RCLCPP__WAIT_SET_POLICIES__DETAIL__CLIENT_ENTRY_HPP_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/entity_entry.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace wait_set_policies
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
using ClientEntry =
|
||||
// EntityEntryTemplate<rclcpp::ClientBase, std::shared_ptr<rclcpp::ClientBase>>;
|
||||
EntityEntryTemplate<rclcpp::ClientBase>;
|
||||
using WeakClientEntry =
|
||||
// EntityEntryTemplate<rclcpp::ClientBase, std::weak_ptr<rclcpp::ClientBase>>;
|
||||
EntityEntryTemplate<rclcpp::ClientBase>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace wait_set_policies
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__WAIT_SET_POLICIES__DETAIL__CLIENT_ENTRY_HPP_
|
||||
183
rclcpp/include/rclcpp/wait_set_policies/detail/entity_entry.hpp
Normal file
183
rclcpp/include/rclcpp/wait_set_policies/detail/entity_entry.hpp
Normal file
@@ -0,0 +1,183 @@
|
||||
// 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_SET_POLICIES__DETAIL__ENTITY_ENTRY_HPP_
|
||||
#define RCLCPP__WAIT_SET_POLICIES__DETAIL__ENTITY_ENTRY_HPP_
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/wait_set_policies/already_associated_with_wait_set_exception.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace wait_set_policies
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
// Forward declaration for use in friend statement.
|
||||
template<typename EntityT>
|
||||
class ManagedEntityEntryTemplate;
|
||||
|
||||
/// Encapsulating class for wait set entities, and gateway to the ManagedEntityEntryTemplate.
|
||||
/**
|
||||
* The entity is stored as a std::shared_ptr.
|
||||
*
|
||||
* This is class can be converted to a "managed" version which ensures the
|
||||
* entity is not associated with another wait set already, then associates it
|
||||
* with the current wait set, and then dissociates it on destruction.
|
||||
*/
|
||||
template<typename EntityT>
|
||||
class EntityEntryTemplate
|
||||
{
|
||||
public:
|
||||
EntityEntryTemplate(std::shared_ptr<EntityT> entity_in = nullptr)
|
||||
: entity_(entity_in)
|
||||
{}
|
||||
private:
|
||||
std::shared_ptr<EntityT> entity_;
|
||||
|
||||
friend ManagedEntityEntryTemplate<EntityT>;
|
||||
};
|
||||
|
||||
/// Managing class for wait set entities, with RAII-style (dis)association with the wait set.
|
||||
/**
|
||||
* The entity is stored as a std::shared_ptr, but ths class can be converted
|
||||
* (one way) into a weak version that stores it as a std::weak_ptr.
|
||||
*
|
||||
* This class will assert that the entity is not already associated with a
|
||||
* wait set, while atomically indicating it is associated with this wait set
|
||||
* to prevent other wait sets from using it, and then on destruction this class
|
||||
* will disassociate it.
|
||||
*
|
||||
* \throws rclcpp::wait_set_policies::AlreadyAssociatedWithWaitSetException if entity
|
||||
* is already associated with a wait set.
|
||||
*/
|
||||
template<typename EntityT>
|
||||
class ManagedEntityEntryTemplate
|
||||
{
|
||||
public:
|
||||
/// The only valid way to construct this is with an unmanaged entity entry.
|
||||
explicit ManagedEntityEntryTemplate(const EntityEntryTemplate<EntityT> & unmanaged_entity_entry)
|
||||
: entity_(unmanaged_entity_entry.entity_)
|
||||
{
|
||||
if (nullptr == entity_) {
|
||||
throw std::invalid_argument("entity cannot be nullptr for a managed entry");
|
||||
}
|
||||
bool already_in_use = entity_->exchange_in_use_by_wait_set_state(true);
|
||||
if (already_in_use) {
|
||||
throw rclcpp::wait_set_policies::AlreadyAssociatedWithWaitSetException(*entity_);
|
||||
}
|
||||
}
|
||||
|
||||
// ManagedEntityEntryTemplate(const ManagedEntityEntryTemplate<EntityT> & other)
|
||||
// {
|
||||
// if (other.should_set_in_use_by_wait_set_of_entity_to_false_on_destruction_) {
|
||||
// throw std::runtime_error("")
|
||||
// }
|
||||
// }
|
||||
|
||||
~ManagedEntityEntryTemplate()
|
||||
{
|
||||
if ((nullptr != entity_)) {
|
||||
bool was_in_use = entity_->exchange_in_use_by_wait_set_state(false);
|
||||
assert(was_in_use);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the interal entity shared pointer.
|
||||
std::shared_ptr<EntityT>
|
||||
get_entity() const
|
||||
{
|
||||
return entity_;
|
||||
}
|
||||
|
||||
/// Reset the entity.
|
||||
/**
|
||||
* Specializations of this class may reset more than one item.
|
||||
* Having this method in all instantiations of this class provides uniform access.
|
||||
*/
|
||||
// void
|
||||
// reset() noexcept
|
||||
// {
|
||||
// entity_.reset();
|
||||
// }
|
||||
|
||||
protected:
|
||||
std::shared_ptr<EntityT> entity_;
|
||||
|
||||
};
|
||||
|
||||
/// Version of ManagedEntityEntryTemplate with weak ownership and best effort disassociation.
|
||||
/**
|
||||
* The entity is stored as a std::weak_ptr, but on destruction, the entity is
|
||||
* locked, and if not nullptr, then it will be marked as not in use by a wait set.
|
||||
*/
|
||||
template<typename EntityT>
|
||||
class WeakManagedEntityEntryTemplate
|
||||
{
|
||||
public:
|
||||
/// Can only be constructed from a moved ManagedEntityEntryTemplate.
|
||||
explicit WeakManagedEntityEntryTemplate(ManagedEntityEntryTemplate<EntityT> && moved_entity_entry)
|
||||
: weak_entity_(moved_entity_entry.get_entity())
|
||||
{}
|
||||
|
||||
~WeakManagedEntityEntryTemplate()
|
||||
{
|
||||
auto entity = weak_entity_.lock();
|
||||
if (nullptr != entity) {
|
||||
bool was_in_use = entity->exchange_in_use_by_wait_set_state(false);
|
||||
assert(was_in_use);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the interal entity weak pointer.
|
||||
std::weak_ptr<EntityT>
|
||||
get_weak_entity() const
|
||||
{
|
||||
return weak_entity_;
|
||||
}
|
||||
|
||||
// /// Lock the entity.
|
||||
// /**
|
||||
// * Specializations of this class may select from more than one item to lock.
|
||||
// * Having this method in all instantiations of this class provides uniform access.
|
||||
// */
|
||||
// std::shared_ptr<EntityT>
|
||||
// lock() const
|
||||
// {
|
||||
// return weak_entity_.lock();
|
||||
// }
|
||||
|
||||
// /// Return true if the entity has expired, otherwise false.
|
||||
// /**
|
||||
// * Specializations of this class may select from more than one item to check.
|
||||
// * Having this method in all instantiations of this class provides uniform access.
|
||||
// */
|
||||
// bool
|
||||
// expired() const noexcept
|
||||
// {
|
||||
// return weak_entity_.expired();
|
||||
// }
|
||||
private:
|
||||
std::weak_ptr<EntityT> weak_entity_;
|
||||
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
} // namespace wait_set_policies
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__WAIT_SET_POLICIES__DETAIL__ENTITY_ENTRY_HPP_
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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_SET_POLICIES__DETAIL__GUARD_CONDITION_ENTRY_HPP_
|
||||
#define RCLCPP__WAIT_SET_POLICIES__DETAIL__GUARD_CONDITION_ENTRY_HPP_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/entity_entry.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace wait_set_policies
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
using GuardConditionEntry =
|
||||
// EntityEntryTemplate<rclcpp::GuardCondition, std::shared_ptr<rclcpp::GuardCondition>>;
|
||||
EntityEntryTemplate<rclcpp::GuardCondition>;
|
||||
using WeakGuardConditionEntry =
|
||||
// EntityEntryTemplate<rclcpp::GuardCondition, std::weak_ptr<rclcpp::GuardCondition>>;
|
||||
EntityEntryTemplate<rclcpp::GuardCondition>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace wait_set_policies
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__WAIT_SET_POLICIES__DETAIL__GUARD_CONDITION_ENTRY_HPP_
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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_SET_POLICIES__DETAIL__SERVICE_ENTRY_HPP_
|
||||
#define RCLCPP__WAIT_SET_POLICIES__DETAIL__SERVICE_ENTRY_HPP_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/entity_entry.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace wait_set_policies
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
using ServiceEntry =
|
||||
// EntityEntryTemplate<rclcpp::ServiceBase, std::shared_ptr<rclcpp::ServiceBase>>;
|
||||
EntityEntryTemplate<rclcpp::ServiceBase>;
|
||||
using WeakServiceEntry =
|
||||
// EntityEntryTemplate<rclcpp::ServiceBase, std::weak_ptr<rclcpp::ServiceBase>>;
|
||||
EntityEntryTemplate<rclcpp::ServiceBase>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace wait_set_policies
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__WAIT_SET_POLICIES__DETAIL__SERVICE_ENTRY_HPP_
|
||||
@@ -0,0 +1,403 @@
|
||||
// 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_SET_POLICIES__DETAIL__SUBSCRIPTION_ENTRY_HPP_
|
||||
#define RCLCPP__WAIT_SET_POLICIES__DETAIL__SUBSCRIPTION_ENTRY_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/subscription_base.hpp"
|
||||
#include "rclcpp/subscription_wait_set_mask.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/entity_entry.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/waitable_entry.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace wait_set_policies
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// See rclcpp::wait_set_policies::detail::EntityEntryTemplate.
|
||||
template<>
|
||||
class EntityEntryTemplate<rclcpp::SubscriptionBase>
|
||||
{
|
||||
using EntityT = rclcpp::SubscriptionBase;
|
||||
|
||||
public:
|
||||
EntityEntryTemplate(
|
||||
std::shared_ptr<EntityT> entity_in = nullptr,
|
||||
const rclcpp::SubscriptionWaitSetMask & mask_in = {})
|
||||
: entity_(entity_in),
|
||||
mask_(mask_in)
|
||||
{}
|
||||
|
||||
/// Create ManagedEntityEntryTemplate instances for the subscription and waitables if needed.
|
||||
/**
|
||||
* This method uses the SubscriptionWaitSetMask to determine how to decompose
|
||||
* the various entities within the subscription into managed entries.
|
||||
*
|
||||
* It optionally returns a managed entry for the subscription, if the mask
|
||||
* indicates it should, and then optionally any waitables for the
|
||||
* intra-process communication and QoS events.
|
||||
*/
|
||||
std::pair<
|
||||
std::optional<ManagedEntityEntryTemplate<rclcpp::SubscriptionBase>>,
|
||||
std::vector<ManagedEntityEntryTemplate<rclcpp::Waitable>>
|
||||
>
|
||||
manage();
|
||||
// defined below ManagedEntityEntryTemplate<rclcpp::SubscriptionBase>.
|
||||
|
||||
private:
|
||||
std::shared_ptr<EntityT> entity_;
|
||||
rclcpp::SubscriptionWaitSetMask mask_;
|
||||
};
|
||||
|
||||
/// See rclcpp::wait_set_policies::detail::ManagedEntityEntryTemplate.
|
||||
template<>
|
||||
class ManagedEntityEntryTemplate<rclcpp::SubscriptionBase>
|
||||
{
|
||||
using EntityT = rclcpp::SubscriptionBase;
|
||||
|
||||
/// Only constructed by EntityEntryTemplate<rclcpp::SubscriptionBase>::manage().
|
||||
explicit ManagedEntityEntryTemplate(std::shared_ptr<EntityT> subscription)
|
||||
: entity_(subscription)
|
||||
{
|
||||
if (nullptr == entity_) {
|
||||
throw std::invalid_argument("entity cannot be nullptr for a managed entry");
|
||||
}
|
||||
bool already_in_use = entity_->exchange_in_use_by_wait_set_state(entity_.get(), true);
|
||||
if (already_in_use) {
|
||||
throw rclcpp::wait_set_policies::AlreadyAssociatedWithWaitSetException(*entity_);
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
// ManagedEntityEntryTemplate(const ManagedEntityEntryTemplate<EntityT> & other)
|
||||
// {
|
||||
// if (other.should_set_in_use_by_wait_set_of_entity_to_false_on_destruction_) {
|
||||
// throw std::runtime_error("")
|
||||
// }
|
||||
// }
|
||||
|
||||
~ManagedEntityEntryTemplate()
|
||||
{
|
||||
if ((nullptr != entity_)) {
|
||||
bool was_in_use = entity_->exchange_in_use_by_wait_set_state(entity_.get(), false);
|
||||
assert(was_in_use);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the interal entity shared pointer.
|
||||
std::shared_ptr<EntityT>
|
||||
get_entity() const
|
||||
{
|
||||
return entity_;
|
||||
}
|
||||
|
||||
/// Reset the entity.
|
||||
/**
|
||||
* Specializations of this class may reset more than one item.
|
||||
* Having this method in all instantiations of this class provides uniform access.
|
||||
*/
|
||||
// void
|
||||
// reset() noexcept
|
||||
// {
|
||||
// entity_.reset();
|
||||
// }
|
||||
|
||||
private:
|
||||
std::shared_ptr<EntityT> entity_;
|
||||
|
||||
friend EntityEntryTemplate<EntityT>;
|
||||
|
||||
};
|
||||
|
||||
std::pair<
|
||||
std::optional<ManagedEntityEntryTemplate<rclcpp::SubscriptionBase>>,
|
||||
std::vector<ManagedEntityEntryTemplate<rclcpp::Waitable>>
|
||||
>
|
||||
EntityEntryTemplate<rclcpp::SubscriptionBase>::manage()
|
||||
{
|
||||
std::optional<ManagedEntityEntryTemplate<rclcpp::SubscriptionBase>> managed_subscription_entry;
|
||||
std::vector<ManagedEntityEntryTemplate<rclcpp::Waitable>> waitables;
|
||||
|
||||
if (mask_.include_subscription) {
|
||||
managed_subscription_entry = ManagedEntityEntryTemplate<rclcpp::SubscriptionBase>(entity_);
|
||||
}
|
||||
if (mask_.include_events) {
|
||||
for (const auto & event_waitable : entity_->get_event_handlers()) {
|
||||
waitables.emplace_back(EntityEntryTemplate<rclcpp::Waitable>(event_waitable, entity_));
|
||||
}
|
||||
}
|
||||
if (mask_.include_intra_process_waitable) {
|
||||
waitables.emplace_back(
|
||||
EntityEntryTemplate<rclcpp::Waitable>(entity_->get_intra_process_waitable(), entity_)
|
||||
);
|
||||
}
|
||||
|
||||
return {managed_subscription_entry, waitables};
|
||||
}
|
||||
|
||||
/// See rclcpp::wait_set_policies::detail::WeakManagedEntityEntryTemplate.
|
||||
template<>
|
||||
class WeakManagedEntityEntryTemplate<rclcpp::SubscriptionBase>
|
||||
{
|
||||
using EntityT = rclcpp::SubscriptionBase;
|
||||
|
||||
public:
|
||||
/// Can only be constructed from a moved ManagedEntityEntryTemplate.
|
||||
explicit WeakManagedEntityEntryTemplate(ManagedEntityEntryTemplate<EntityT> && moved_entity_entry)
|
||||
: weak_entity_(moved_entity_entry.get_entity())
|
||||
{}
|
||||
|
||||
~WeakManagedEntityEntryTemplate()
|
||||
{
|
||||
auto entity = weak_entity_.lock();
|
||||
if (nullptr != entity) {
|
||||
bool was_in_use = entity->exchange_in_use_by_wait_set_state(entity.get(), false);
|
||||
assert(was_in_use);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the interal entity weak pointer.
|
||||
std::weak_ptr<EntityT>
|
||||
get_weak_entity() const
|
||||
{
|
||||
return weak_entity_;
|
||||
}
|
||||
|
||||
// /// Lock the entity.
|
||||
// /**
|
||||
// * Specializations of this class may select from more than one item to lock.
|
||||
// * Having this method in all instantiations of this class provides uniform access.
|
||||
// */
|
||||
// std::shared_ptr<EntityT>
|
||||
// lock() const
|
||||
// {
|
||||
// return weak_entity_.lock();
|
||||
// }
|
||||
|
||||
// /// Return true if the entity has expired, otherwise false.
|
||||
// /**
|
||||
// * Specializations of this class may select from more than one item to check.
|
||||
// * Having this method in all instantiations of this class provides uniform access.
|
||||
// */
|
||||
// bool
|
||||
// expired() const noexcept
|
||||
// {
|
||||
// return weak_entity_.expired();
|
||||
// }
|
||||
private:
|
||||
std::weak_ptr<EntityT> weak_entity_;
|
||||
std::weak_ptr<void> weak_associated_entity_;
|
||||
|
||||
};
|
||||
|
||||
using SubscriptionEntry = EntityEntryTemplate<rclcpp::SubscriptionBase>;
|
||||
using ManagedSubscriptionEntry = ManagedEntityEntryTemplate<rclcpp::SubscriptionBase>;
|
||||
using WeakManagedSubscriptionEntry = WeakManagedEntityEntryTemplate<rclcpp::SubscriptionBase>;
|
||||
|
||||
// /// Specialization for Subscriptions.
|
||||
// template<>
|
||||
// class EntityEntryTemplate<rclcpp::SubscriptionBase>
|
||||
// {
|
||||
// using EntityT = rclcpp::SubscriptionBase;
|
||||
// std::shared_ptr<EntityT> entity_;
|
||||
// rclcpp::SubscriptionWaitSetMask mask_;
|
||||
|
||||
// public:
|
||||
// EntityEntryTemplate(
|
||||
// std::shared_ptr<EntityT> entity_in = nullptr,
|
||||
// const rclcpp::SubscriptionWaitSetMask & mask_in = {})
|
||||
// : entity_(entity_in),
|
||||
// mask_(mask_in)
|
||||
// {}
|
||||
|
||||
// ~EntityEntryTemplate()
|
||||
// {
|
||||
// if (should_set_in_use_by_wait_set_of_entity_to_false_on_destruction_) {
|
||||
// EntityEntryTemplate<rclcpp::SubscriptionBase>::cleanup(entity_, mask_);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// See EntityEntryTemplate::get_entity().
|
||||
// std::shared_ptr<EntityT>
|
||||
// get_entity() const
|
||||
// {
|
||||
// return entity_;
|
||||
// }
|
||||
|
||||
// /// Return a const reference to the subscrption mask.
|
||||
// const rclcpp::SubscriptionWaitSetMask &
|
||||
// get_mask() const
|
||||
// {
|
||||
// return mask_;
|
||||
// }
|
||||
|
||||
// /// See EntityEntryTemplate::manage().
|
||||
// std::vector<WaitableEntry>
|
||||
// manage()
|
||||
// {
|
||||
// if (nullptr == entity_) {
|
||||
// throw std::runtime_error("manage() called on EntityEntry with null entity");
|
||||
// }
|
||||
// auto associate = [this](void * entity_part) {
|
||||
// bool already_in_use = entity_->exchange_in_use_by_wait_set_state(entity_part, true);
|
||||
// if (already_in_use) {
|
||||
// throw rclcpp::wait_set_policies::AlreadyAssociatedWithWaitSetException(*entity_);
|
||||
// }
|
||||
// };
|
||||
|
||||
// if (mask_.include_subscription) {
|
||||
// associate(entity_.get());
|
||||
// }
|
||||
|
||||
// std::vector<WaitableEntry> decomposed_waitables;
|
||||
|
||||
// if (mask_.include_events) {
|
||||
// for (auto event : entity_->get_event_handlers()) {
|
||||
// // TODO(wjwwood): do we need to use the unmanage_function argument
|
||||
// // to utilize the exchange_in_use_by_wait_set_state of SubscriptionBase?
|
||||
// decomposed_waitables.emplace_back(event, entity_);
|
||||
// decomposed_waitables.back().manage();
|
||||
// }
|
||||
// // mask_.include_events = false;
|
||||
// }
|
||||
// if (mask_.include_intra_process_waitable) {
|
||||
// auto waitable = entity_->get_intra_process_waitable();
|
||||
// if (nullptr != waitable) {
|
||||
// // TODO(wjwwood): do we need to use the unmanage_function argument
|
||||
// // to utilize the exchange_in_use_by_wait_set_state of SubscriptionBase?
|
||||
// decomposed_waitables.emplace_back(waitable, entity_);
|
||||
// decomposed_waitables.back().manage();
|
||||
// // mask_.include_intra_process_waitable = false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// should_set_in_use_by_wait_set_of_entity_to_false_on_destruction_ = true;
|
||||
|
||||
// return decomposed_waitables;
|
||||
// }
|
||||
|
||||
// /// See EntityEntryTemplate::reset().
|
||||
// void
|
||||
// reset() noexcept
|
||||
// {
|
||||
// entity_.reset();
|
||||
// }
|
||||
|
||||
// protected:
|
||||
// bool should_set_in_use_by_wait_set_of_entity_to_false_on_destruction_{false};
|
||||
|
||||
// static
|
||||
// void
|
||||
// cleanup(std::shared_ptr<EntityT> subscription, rclcpp::SubscriptionWaitSetMask mask)
|
||||
// {
|
||||
// if (subscription != nullptr) {
|
||||
// auto dissociate = [&subscription](void * entity_part) {
|
||||
// bool was_in_use = subscription->exchange_in_use_by_wait_set_state(entity_part, false);
|
||||
// assert(was_in_use);
|
||||
// };
|
||||
// if (mask.include_subscription) {
|
||||
// dissociate(subscription.get());
|
||||
// }
|
||||
// if (mask.include_events) {
|
||||
// for (auto event : subscription->get_event_handlers()) {
|
||||
// dissociate(event.get());
|
||||
// }
|
||||
// }
|
||||
// if (mask.include_intra_process_waitable) {
|
||||
// auto waitable = subscription->get_intra_process_waitable();
|
||||
// if (nullptr != waitable) {
|
||||
// dissociate(waitable.get());
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// friend WeakEntityEntryTemplate<EntityT>;
|
||||
|
||||
// };
|
||||
|
||||
// /// Specialization for Subscriptions.
|
||||
// template<>
|
||||
// class WeakEntityEntryTemplate<rclcpp::SubscriptionBase>
|
||||
// {
|
||||
// using EntityT = rclcpp::SubscriptionBase;
|
||||
// std::weak_ptr<EntityT> weak_entity_;
|
||||
// rclcpp::SubscriptionWaitSetMask mask_;
|
||||
|
||||
// public:
|
||||
// explicit WeakEntityEntryTemplate(EntityEntryTemplate<EntityT> && moved_entity_entry)
|
||||
// : weak_entity_(moved_entity_entry.get_entity()),
|
||||
// mask_(moved_entity_entry.mask_),
|
||||
// should_set_in_use_by_wait_set_of_entity_to_false_on_destruction_(
|
||||
// moved_entity_entry.should_set_in_use_by_wait_set_of_entity_to_false_on_destruction_
|
||||
// )
|
||||
// {
|
||||
// moved_entity_entry.should_set_in_use_by_wait_set_of_entity_to_false_on_destruction_ = false;
|
||||
// }
|
||||
|
||||
// ~WeakEntityEntryTemplate()
|
||||
// {
|
||||
// auto entity = weak_entity_.lock();
|
||||
// if (should_set_in_use_by_wait_set_of_entity_to_false_on_destruction_) {
|
||||
// EntityEntryTemplate<rclcpp::SubscriptionBase>::cleanup(entity, mask_);
|
||||
// }
|
||||
// }
|
||||
|
||||
// /// See WeakEntityEntryTemplate::get_weak_entity().
|
||||
// std::weak_ptr<EntityT>
|
||||
// get_weak_entity() const
|
||||
// {
|
||||
// return weak_entity_;
|
||||
// }
|
||||
|
||||
// /// Return a const reference to the subscrption mask.
|
||||
// const rclcpp::SubscriptionWaitSetMask &
|
||||
// get_mask() const
|
||||
// {
|
||||
// return mask_;
|
||||
// }
|
||||
|
||||
// /// See WeakEntityEntryTemplate::lock().
|
||||
// std::shared_ptr<EntityT>
|
||||
// lock() const
|
||||
// {
|
||||
// return weak_entity_.lock();
|
||||
// }
|
||||
|
||||
// /// See WeakEntityEntryTemplate::expired().
|
||||
// bool
|
||||
// expired() const noexcept
|
||||
// {
|
||||
// return weak_entity_.expired();
|
||||
// }
|
||||
|
||||
// protected:
|
||||
// bool should_set_in_use_by_wait_set_of_entity_to_false_on_destruction_{false};
|
||||
|
||||
// };
|
||||
|
||||
// using SubscriptionEntry = EntityEntryTemplate<rclcpp::SubscriptionBase>;
|
||||
// using WeakSubscriptionEntry = WeakEntityEntryTemplate<rclcpp::SubscriptionBase>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace wait_set_policies
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__WAIT_SET_POLICIES__DETAIL__SUBSCRIPTION_ENTRY_HPP_
|
||||
@@ -0,0 +1,41 @@
|
||||
// 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_SET_POLICIES__DETAIL__TIMER_ENTRY_HPP_
|
||||
#define RCLCPP__WAIT_SET_POLICIES__DETAIL__TIMER_ENTRY_HPP_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/timer.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/entity_entry.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace wait_set_policies
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
using TimerEntry =
|
||||
// EntityEntryTemplate<rclcpp::TimerBase, std::shared_ptr<rclcpp::TimerBase>>;
|
||||
EntityEntryTemplate<rclcpp::TimerBase>;
|
||||
using WeakTimerEntry =
|
||||
// EntityEntryTemplate<rclcpp::TimerBase, std::weak_ptr<rclcpp::TimerBase>>;
|
||||
EntityEntryTemplate<rclcpp::TimerBase>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace wait_set_policies
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__WAIT_SET_POLICIES__DETAIL__TIMER_ENTRY_HPP_
|
||||
@@ -0,0 +1,183 @@
|
||||
// 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_SET_POLICIES__DETAIL__WAITABLE_ENTRY_HPP_
|
||||
#define RCLCPP__WAIT_SET_POLICIES__DETAIL__WAITABLE_ENTRY_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
#include "rclcpp/waitable.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/entity_entry.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace wait_set_policies
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// See rclcpp::wait_set_policies::detail::EntityEntryTemplate.
|
||||
template<>
|
||||
class EntityEntryTemplate<rclcpp::Waitable>
|
||||
{
|
||||
using EntityT = rclcpp::Waitable;
|
||||
|
||||
public:
|
||||
EntityEntryTemplate(
|
||||
std::shared_ptr<EntityT> entity_in = nullptr,
|
||||
std::shared_ptr<void> associated_entity_in = nullptr)
|
||||
: entity_(entity_in),
|
||||
associated_entity_(associated_entity_in)
|
||||
{}
|
||||
private:
|
||||
std::shared_ptr<EntityT> entity_;
|
||||
std::shared_ptr<void> associated_entity_;
|
||||
|
||||
friend ManagedEntityEntryTemplate<EntityT>;
|
||||
};
|
||||
|
||||
/// See rclcpp::wait_set_policies::detail::ManagedEntityEntryTemplate.
|
||||
template<>
|
||||
class ManagedEntityEntryTemplate<rclcpp::Waitable>
|
||||
{
|
||||
using EntityT = rclcpp::Waitable;
|
||||
|
||||
public:
|
||||
/// The only valid way to construct this is with an unmanaged entity entry.
|
||||
explicit ManagedEntityEntryTemplate(const EntityEntryTemplate<EntityT> & unmanaged_entity_entry)
|
||||
: entity_(unmanaged_entity_entry.entity_),
|
||||
associated_entity_(unmanaged_entity_entry.associated_entity_)
|
||||
{
|
||||
if (nullptr == entity_) {
|
||||
throw std::invalid_argument("entity cannot be nullptr for a managed entry");
|
||||
}
|
||||
bool already_in_use = entity_->exchange_in_use_by_wait_set_state(true);
|
||||
if (already_in_use) {
|
||||
throw rclcpp::wait_set_policies::AlreadyAssociatedWithWaitSetException(*entity_);
|
||||
}
|
||||
}
|
||||
|
||||
// ManagedEntityEntryTemplate(const ManagedEntityEntryTemplate<EntityT> & other)
|
||||
// {
|
||||
// if (other.should_set_in_use_by_wait_set_of_entity_to_false_on_destruction_) {
|
||||
// throw std::runtime_error("")
|
||||
// }
|
||||
// }
|
||||
|
||||
~ManagedEntityEntryTemplate()
|
||||
{
|
||||
if ((nullptr != entity_)) {
|
||||
bool was_in_use = entity_->exchange_in_use_by_wait_set_state(false);
|
||||
assert(was_in_use);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the interal entity shared pointer.
|
||||
std::shared_ptr<EntityT>
|
||||
get_entity() const
|
||||
{
|
||||
return entity_;
|
||||
}
|
||||
|
||||
/// Return the interal associated entity shared pointer.
|
||||
std::shared_ptr<void>
|
||||
get_associated_entity() const
|
||||
{
|
||||
return associated_entity_;
|
||||
}
|
||||
|
||||
/// Reset the entity.
|
||||
/**
|
||||
* Specializations of this class may reset more than one item.
|
||||
* Having this method in all instantiations of this class provides uniform access.
|
||||
*/
|
||||
// void
|
||||
// reset() noexcept
|
||||
// {
|
||||
// entity_.reset();
|
||||
// }
|
||||
|
||||
protected:
|
||||
std::shared_ptr<EntityT> entity_;
|
||||
std::shared_ptr<void> associated_entity_;
|
||||
|
||||
};
|
||||
|
||||
/// See rclcpp::wait_set_policies::detail::WeakManagedEntityEntryTemplate.
|
||||
template<>
|
||||
class WeakManagedEntityEntryTemplate<rclcpp::Waitable>
|
||||
{
|
||||
using EntityT = rclcpp::Waitable;
|
||||
|
||||
public:
|
||||
/// Can only be constructed from a moved ManagedEntityEntryTemplate.
|
||||
explicit WeakManagedEntityEntryTemplate(ManagedEntityEntryTemplate<EntityT> && moved_entity_entry)
|
||||
: weak_entity_(moved_entity_entry.get_entity()),
|
||||
weak_associated_entity_(moved_entity_entry.get_associated_entity())
|
||||
{}
|
||||
|
||||
~WeakManagedEntityEntryTemplate()
|
||||
{
|
||||
auto entity = weak_entity_.lock();
|
||||
if (nullptr != entity) {
|
||||
bool was_in_use = entity->exchange_in_use_by_wait_set_state(false);
|
||||
assert(was_in_use);
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the interal entity weak pointer.
|
||||
std::weak_ptr<EntityT>
|
||||
get_weak_entity() const
|
||||
{
|
||||
return weak_entity_;
|
||||
}
|
||||
|
||||
// /// Lock the entity.
|
||||
// /**
|
||||
// * Specializations of this class may select from more than one item to lock.
|
||||
// * Having this method in all instantiations of this class provides uniform access.
|
||||
// */
|
||||
// std::shared_ptr<EntityT>
|
||||
// lock() const
|
||||
// {
|
||||
// return weak_entity_.lock();
|
||||
// }
|
||||
|
||||
// /// Return true if the entity has expired, otherwise false.
|
||||
// /**
|
||||
// * Specializations of this class may select from more than one item to check.
|
||||
// * Having this method in all instantiations of this class provides uniform access.
|
||||
// */
|
||||
// bool
|
||||
// expired() const noexcept
|
||||
// {
|
||||
// return weak_entity_.expired();
|
||||
// }
|
||||
private:
|
||||
std::weak_ptr<EntityT> weak_entity_;
|
||||
std::weak_ptr<void> weak_associated_entity_;
|
||||
|
||||
};
|
||||
|
||||
using WaitableEntry = EntityEntryTemplate<rclcpp::Waitable>;
|
||||
using ManagedWaitableEntry = ManagedEntityEntryTemplate<rclcpp::Waitable>;
|
||||
using WeakManagedWaitableEntry = WeakManagedEntityEntryTemplate<rclcpp::Waitable>;
|
||||
|
||||
} // namespace detail
|
||||
} // namespace wait_set_policies
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__WAIT_SET_POLICIES__DETAIL__WAITABLE_ENTRY_HPP_
|
||||
@@ -28,7 +28,13 @@
|
||||
#include "rclcpp/subscription_wait_set_mask.hpp"
|
||||
#include "rclcpp/timer.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/client_entry.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/guard_condition_entry.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/service_entry.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/storage_policy_common.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/subscription_entry.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/timer_entry.hpp"
|
||||
#include "rclcpp/wait_set_policies/detail/waitable_entry.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -42,124 +48,27 @@ class DynamicStorage : public rclcpp::wait_set_policies::detail::StoragePolicyCo
|
||||
protected:
|
||||
using is_mutable = std::true_type;
|
||||
|
||||
class SubscriptionEntry
|
||||
{
|
||||
// (wjwwood): indent of 'public:' is weird, I know. uncrustify is dumb.
|
||||
using WeakManagedSubscriptionEntry = detail::WeakManagedSubscriptionEntry;
|
||||
using SubscriptionEntry = detail::SubscriptionEntry;
|
||||
|
||||
public:
|
||||
std::shared_ptr<rclcpp::SubscriptionBase> subscription;
|
||||
rclcpp::SubscriptionWaitSetMask mask;
|
||||
|
||||
/// Conversion constructor, which is intentionally not marked explicit.
|
||||
SubscriptionEntry(
|
||||
std::shared_ptr<rclcpp::SubscriptionBase> subscription_in = nullptr,
|
||||
const rclcpp::SubscriptionWaitSetMask & mask_in = {})
|
||||
: subscription(std::move(subscription_in)),
|
||||
mask(mask_in)
|
||||
{}
|
||||
|
||||
void
|
||||
reset() noexcept
|
||||
{
|
||||
subscription.reset();
|
||||
}
|
||||
};
|
||||
class WeakSubscriptionEntry
|
||||
{
|
||||
public:
|
||||
std::weak_ptr<rclcpp::SubscriptionBase> subscription;
|
||||
rclcpp::SubscriptionWaitSetMask mask;
|
||||
|
||||
explicit WeakSubscriptionEntry(
|
||||
const std::shared_ptr<rclcpp::SubscriptionBase> & subscription_in,
|
||||
const rclcpp::SubscriptionWaitSetMask & mask_in) noexcept
|
||||
: subscription(subscription_in),
|
||||
mask(mask_in)
|
||||
{}
|
||||
|
||||
explicit WeakSubscriptionEntry(const SubscriptionEntry & other)
|
||||
: subscription(other.subscription),
|
||||
mask(other.mask)
|
||||
{}
|
||||
|
||||
std::shared_ptr<rclcpp::SubscriptionBase>
|
||||
lock() const
|
||||
{
|
||||
return subscription.lock();
|
||||
}
|
||||
|
||||
bool
|
||||
expired() const noexcept
|
||||
{
|
||||
return subscription.expired();
|
||||
}
|
||||
};
|
||||
using SequenceOfWeakSubscriptions = std::vector<WeakSubscriptionEntry>;
|
||||
using SequenceOfWeakSubscriptions = std::vector<WeakManagedSubscriptionEntry>;
|
||||
using SubscriptionsIterable = std::vector<SubscriptionEntry>;
|
||||
|
||||
using SequenceOfWeakGuardConditions = std::vector<std::weak_ptr<rclcpp::GuardCondition>>;
|
||||
using GuardConditionsIterable = std::vector<std::shared_ptr<rclcpp::GuardCondition>>;
|
||||
using SequenceOfWeakGuardConditions = std::vector<detail::WeakGuardConditionEntry>;
|
||||
using GuardConditionsIterable = std::vector<detail::GuardConditionEntry>;
|
||||
|
||||
using SequenceOfWeakTimers = std::vector<std::weak_ptr<rclcpp::TimerBase>>;
|
||||
using TimersIterable = std::vector<std::shared_ptr<rclcpp::TimerBase>>;
|
||||
using SequenceOfWeakTimers = std::vector<detail::WeakTimerEntry>;
|
||||
using TimersIterable = std::vector<detail::TimerEntry>;
|
||||
|
||||
using SequenceOfWeakClients = std::vector<std::weak_ptr<rclcpp::ClientBase>>;
|
||||
using ClientsIterable = std::vector<std::shared_ptr<rclcpp::ClientBase>>;
|
||||
using SequenceOfWeakClients = std::vector<detail::WeakClientEntry>;
|
||||
using ClientsIterable = std::vector<detail::ClientEntry>;
|
||||
|
||||
using SequenceOfWeakServices = std::vector<std::weak_ptr<rclcpp::ServiceBase>>;
|
||||
using ServicesIterable = std::vector<std::shared_ptr<rclcpp::ServiceBase>>;
|
||||
using SequenceOfWeakServices = std::vector<detail::WeakServiceEntry>;
|
||||
using ServicesIterable = std::vector<detail::ServiceEntry>;
|
||||
|
||||
class WaitableEntry
|
||||
{
|
||||
public:
|
||||
std::shared_ptr<rclcpp::Waitable> waitable;
|
||||
std::shared_ptr<void> associated_entity;
|
||||
using WeakWaitableEntry = detail::WeakWaitableEntry;
|
||||
using WaitableEntry = detail::WaitableEntry;
|
||||
|
||||
/// Conversion constructor, which is intentionally not marked explicit.
|
||||
WaitableEntry(
|
||||
std::shared_ptr<rclcpp::Waitable> waitable_in = nullptr,
|
||||
std::shared_ptr<void> associated_entity_in = nullptr) noexcept
|
||||
: waitable(std::move(waitable_in)),
|
||||
associated_entity(std::move(associated_entity_in))
|
||||
{}
|
||||
|
||||
void
|
||||
reset() noexcept
|
||||
{
|
||||
waitable.reset();
|
||||
associated_entity.reset();
|
||||
}
|
||||
};
|
||||
class WeakWaitableEntry
|
||||
{
|
||||
public:
|
||||
std::weak_ptr<rclcpp::Waitable> waitable;
|
||||
std::weak_ptr<void> associated_entity;
|
||||
|
||||
explicit WeakWaitableEntry(
|
||||
const std::shared_ptr<rclcpp::Waitable> & waitable_in,
|
||||
const std::shared_ptr<void> & associated_entity_in) noexcept
|
||||
: waitable(waitable_in),
|
||||
associated_entity(associated_entity_in)
|
||||
{}
|
||||
|
||||
explicit WeakWaitableEntry(const WaitableEntry & other)
|
||||
: waitable(other.waitable),
|
||||
associated_entity(other.associated_entity)
|
||||
{}
|
||||
|
||||
std::shared_ptr<rclcpp::Waitable>
|
||||
lock() const
|
||||
{
|
||||
return waitable.lock();
|
||||
}
|
||||
|
||||
bool
|
||||
expired() const noexcept
|
||||
{
|
||||
return waitable.expired();
|
||||
}
|
||||
};
|
||||
using SequenceOfWeakWaitables = std::vector<WeakWaitableEntry>;
|
||||
using WaitablesIterable = std::vector<WaitableEntry>;
|
||||
|
||||
@@ -184,8 +93,9 @@ public:
|
||||
services,
|
||||
waitables,
|
||||
context),
|
||||
subscriptions_(subscriptions.cbegin(), subscriptions.cend()),
|
||||
shared_subscriptions_(subscriptions_.size()),
|
||||
// subscriptions_ is populated in the constructor dynamically, in order
|
||||
// to respect the mask.
|
||||
// shared_subscriptions_ is resized based on the result of subscriptions_.
|
||||
guard_conditions_(guard_conditions.cbegin(), guard_conditions.cend()),
|
||||
shared_guard_conditions_(guard_conditions_.size()),
|
||||
timers_(timers.cbegin(), timers.cend()),
|
||||
@@ -194,9 +104,39 @@ public:
|
||||
shared_clients_(clients_.size()),
|
||||
services_(services.cbegin(), services.cend()),
|
||||
shared_services_(services_.size()),
|
||||
waitables_(waitables.cbegin(), waitables.cend()),
|
||||
shared_waitables_(waitables_.size())
|
||||
{}
|
||||
waitables_(waitables.cbegin(), waitables.cend())
|
||||
// shared_waitables_ is resized based on the result of waitables_ after
|
||||
// waitables from subscriptions are added, if any.
|
||||
{
|
||||
// Ensure subscriptions are not being used by other wait sets and extract
|
||||
// their waitables, if they have them.
|
||||
std::vector<WaitableEntry> waitables_from_subscriptions;
|
||||
for (auto & subscription_entry : subscriptions) {
|
||||
std::vector<WaitableEntry> local_waitables_from_subscriptions = subscription_entry.manage();
|
||||
if (subscription_entry.get_mask().include_subscription) {
|
||||
subscriptions_.push_back(subscription_entry);
|
||||
}
|
||||
}
|
||||
// Add subscription entries from subscriptions, if the subscription mask indicates.
|
||||
std::copy_if(
|
||||
subscriptions.cbegin(),
|
||||
subscriptions.cend(),
|
||||
std::back_inserter(subscriptions_),
|
||||
[](const auto & subscription_entry) {
|
||||
return subscription_entry.mask.include_subscription;
|
||||
}
|
||||
);
|
||||
shared_subscriptions_.resize(subscriptions_.size());
|
||||
// Add waitables from subscriptions, if the subscription mask indicates.
|
||||
for (const auto & subscription_entry : subscriptions) {
|
||||
if (subscription_entry.mask.include_events) {
|
||||
for (const auto & event : subscription_entry.subscription->get_event_handlers()) {
|
||||
waitables_.push_back({event, subscription_entry.subscription});
|
||||
}
|
||||
}
|
||||
}
|
||||
shared_waitables_.resize(waitables_.size());
|
||||
}
|
||||
|
||||
~DynamicStorage() = default;
|
||||
|
||||
@@ -243,7 +183,7 @@ public:
|
||||
if (this->storage_has_entity(*subscription, subscriptions_)) {
|
||||
throw std::runtime_error("subscription already in wait set");
|
||||
}
|
||||
WeakSubscriptionEntry weak_entry{std::move(subscription), {}};
|
||||
WeakManagedSubscriptionEntry weak_entry{std::move(subscription), {}};
|
||||
subscriptions_.push_back(std::move(weak_entry));
|
||||
this->storage_flag_for_resize();
|
||||
}
|
||||
|
||||
@@ -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>11.0.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>
|
||||
|
||||
@@ -31,8 +31,6 @@
|
||||
#include "rcutils/error_handling.h"
|
||||
#include "rcutils/macros.h"
|
||||
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
|
||||
#include "./logging_mutex.hpp"
|
||||
|
||||
using rclcpp::Context;
|
||||
@@ -315,9 +313,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 +346,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
|
||||
|
||||
@@ -56,7 +56,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 +138,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>
|
||||
@@ -260,7 +268,9 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
|
||||
// If the node already has an executor
|
||||
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
||||
if (has_executor.exchange(true)) {
|
||||
throw std::runtime_error("Node has already been added to an executor.");
|
||||
throw std::runtime_error(
|
||||
std::string("Node '") + node_ptr->get_fully_qualified_name() +
|
||||
"' has already been added to an executor.");
|
||||
}
|
||||
std::lock_guard<std::mutex> guard{mutex_};
|
||||
for (auto & weak_group : node_ptr->get_callback_groups()) {
|
||||
@@ -581,8 +591,7 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
|
||||
[&]() {return subscription->take_serialized(*serialized_msg.get(), message_info);},
|
||||
[&]()
|
||||
{
|
||||
auto void_serialized_msg = std::static_pointer_cast<void>(serialized_msg);
|
||||
subscription->handle_message(void_serialized_msg, message_info);
|
||||
subscription->handle_serialized_message(serialized_msg, message_info);
|
||||
});
|
||||
subscription->return_serialized_message(serialized_msg);
|
||||
} else if (subscription->can_loan_messages()) {
|
||||
@@ -610,16 +619,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.
|
||||
|
||||
@@ -76,9 +76,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
|
||||
|
||||
@@ -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
|
||||
75
rclcpp/src/rclcpp/generic_subscription.cpp
Normal file
75
rclcpp/src/rclcpp/generic_subscription.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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> &,
|
||||
const rclcpp::MessageInfo &)
|
||||
{
|
||||
throw rclcpp::exceptions::UnimplementedError(
|
||||
"handle_message is not implemented for GenericSubscription");
|
||||
}
|
||||
|
||||
void
|
||||
GenericSubscription::handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> & message,
|
||||
const rclcpp::MessageInfo &)
|
||||
{
|
||||
callback_(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
|
||||
@@ -36,23 +36,26 @@ IntraProcessManager::add_publisher(rclcpp::PublisherBase::SharedPtr publisher)
|
||||
{
|
||||
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
|
||||
|
||||
auto id = IntraProcessManager::get_next_unique_id();
|
||||
uint64_t pub_id = IntraProcessManager::get_next_unique_id();
|
||||
|
||||
publishers_[id].publisher = publisher;
|
||||
publishers_[id].topic_name = publisher->get_topic_name();
|
||||
publishers_[id].qos = publisher->get_actual_qos().get_rmw_qos_profile();
|
||||
publishers_[pub_id] = publisher;
|
||||
|
||||
// Initialize the subscriptions storage for this publisher.
|
||||
pub_to_subs_[id] = SplittedSubscriptions();
|
||||
pub_to_subs_[pub_id] = SplittedSubscriptions();
|
||||
|
||||
// create an entry for the publisher id and populate with already existing subscriptions
|
||||
for (auto & pair : subscriptions_) {
|
||||
if (can_communicate(publishers_[id], pair.second)) {
|
||||
insert_sub_id_for_pub(pair.first, id, pair.second.use_take_shared_method);
|
||||
auto subscription = pair.second.lock();
|
||||
if (!subscription) {
|
||||
continue;
|
||||
}
|
||||
if (can_communicate(publisher, subscription)) {
|
||||
uint64_t sub_id = pair.first;
|
||||
insert_sub_id_for_pub(sub_id, pub_id, subscription->use_take_shared_method());
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
return pub_id;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
@@ -60,21 +63,23 @@ IntraProcessManager::add_subscription(SubscriptionIntraProcessBase::SharedPtr su
|
||||
{
|
||||
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
|
||||
|
||||
auto id = IntraProcessManager::get_next_unique_id();
|
||||
uint64_t sub_id = IntraProcessManager::get_next_unique_id();
|
||||
|
||||
subscriptions_[id].subscription = subscription;
|
||||
subscriptions_[id].topic_name = subscription->get_topic_name();
|
||||
subscriptions_[id].qos = subscription->get_actual_qos();
|
||||
subscriptions_[id].use_take_shared_method = subscription->use_take_shared_method();
|
||||
subscriptions_[sub_id] = subscription;
|
||||
|
||||
// adds the subscription id to all the matchable publishers
|
||||
for (auto & pair : publishers_) {
|
||||
if (can_communicate(pair.second, subscriptions_[id])) {
|
||||
insert_sub_id_for_pub(id, pair.first, subscriptions_[id].use_take_shared_method);
|
||||
auto publisher = pair.second.lock();
|
||||
if (!publisher) {
|
||||
continue;
|
||||
}
|
||||
if (can_communicate(publisher, subscription)) {
|
||||
uint64_t pub_id = pair.first;
|
||||
insert_sub_id_for_pub(sub_id, pub_id, subscription->use_take_shared_method());
|
||||
}
|
||||
}
|
||||
|
||||
return id;
|
||||
return sub_id;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -116,7 +121,7 @@ IntraProcessManager::matches_any_publishers(const rmw_gid_t * id) const
|
||||
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
|
||||
|
||||
for (auto & publisher_pair : publishers_) {
|
||||
auto publisher = publisher_pair.second.publisher.lock();
|
||||
auto publisher = publisher_pair.second.lock();
|
||||
if (!publisher) {
|
||||
continue;
|
||||
}
|
||||
@@ -157,7 +162,7 @@ IntraProcessManager::get_subscription_intra_process(uint64_t intra_process_subsc
|
||||
if (subscription_it == subscriptions_.end()) {
|
||||
return nullptr;
|
||||
} else {
|
||||
auto subscription = subscription_it->second.subscription.lock();
|
||||
auto subscription = subscription_it->second.lock();
|
||||
if (subscription) {
|
||||
return subscription;
|
||||
} else {
|
||||
@@ -204,25 +209,16 @@ IntraProcessManager::insert_sub_id_for_pub(
|
||||
|
||||
bool
|
||||
IntraProcessManager::can_communicate(
|
||||
PublisherInfo pub_info,
|
||||
SubscriptionInfo sub_info) const
|
||||
rclcpp::PublisherBase::SharedPtr pub,
|
||||
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr sub) const
|
||||
{
|
||||
// publisher and subscription must be on the same topic
|
||||
if (strcmp(pub_info.topic_name, sub_info.topic_name) != 0) {
|
||||
if (strcmp(pub->get_topic_name(), sub->get_topic_name()) != 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(alsora): the following checks for qos compatibility should be provided by the RMW
|
||||
// a reliable subscription can't be connected with a best effort publisher
|
||||
if (
|
||||
sub_info.qos.reliability == RMW_QOS_POLICY_RELIABILITY_RELIABLE &&
|
||||
pub_info.qos.reliability == RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// a publisher and a subscription with different durability can't communicate
|
||||
if (sub_info.qos.durability != pub_info.qos.durability) {
|
||||
auto check_result = rclcpp::qos_check_compatible(pub->get_actual_qos(), sub->get_actual_qos());
|
||||
if (check_result.compatibility == rclcpp::QoSCompatibility::Error) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,6 @@
|
||||
#include <vector>
|
||||
|
||||
#include "rcutils/logging_macros.h"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
@@ -34,6 +33,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 +268,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;
|
||||
}
|
||||
|
||||
@@ -31,7 +31,7 @@ SubscriptionIntraProcessBase::get_topic_name() const
|
||||
return topic_name_.c_str();
|
||||
}
|
||||
|
||||
rmw_qos_profile_t
|
||||
rclcpp::QoS
|
||||
SubscriptionIntraProcessBase::get_actual_qos() const
|
||||
{
|
||||
return qos_profile_;
|
||||
|
||||
@@ -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)));
|
||||
|
||||
1
rclcpp/test/msg/String.msg
Normal file
1
rclcpp/test/msg/String.msg
Normal file
@@ -0,0 +1 @@
|
||||
string data
|
||||
@@ -7,6 +7,7 @@ add_definitions(-DTEST_RESOURCES_DIRECTORY="${TEST_RESOURCES_DIRECTORY}")
|
||||
rosidl_generate_interfaces(${PROJECT_NAME}_test_msgs
|
||||
../msg/Header.msg
|
||||
../msg/MessageWithHeader.msg
|
||||
../msg/String.msg
|
||||
DEPENDENCIES builtin_interfaces
|
||||
LIBRARY_NAME ${PROJECT_NAME}
|
||||
SKIP_INSTALL
|
||||
@@ -143,6 +144,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
|
||||
@@ -348,6 +356,44 @@ if(TARGET test_publisher)
|
||||
)
|
||||
target_link_libraries(test_publisher ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
if(WIN32)
|
||||
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_publisher_with_type_adapter test_publisher_with_type_adapter.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
)
|
||||
if(TARGET test_publisher_with_type_adapter)
|
||||
ament_target_dependencies(test_publisher_with_type_adapter
|
||||
"rcutils"
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
"test_msgs"
|
||||
)
|
||||
rosidl_target_interfaces(test_publisher_with_type_adapter ${PROJECT_NAME}_test_msgs "rosidl_typesupport_cpp")
|
||||
target_link_libraries(test_publisher_with_type_adapter ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_subscription_with_type_adapter test_subscription_with_type_adapter.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
)
|
||||
if(TARGET test_subscription_with_type_adapter)
|
||||
ament_target_dependencies(test_subscription_with_type_adapter
|
||||
"rcutils"
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_runtime_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
"test_msgs"
|
||||
)
|
||||
rosidl_target_interfaces(test_subscription_with_type_adapter ${PROJECT_NAME}_test_msgs "rosidl_typesupport_cpp")
|
||||
target_link_libraries(test_subscription_with_type_adapter ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_publisher_subscription_count_api test_publisher_subscription_count_api.cpp)
|
||||
if(TARGET test_publisher_subscription_count_api)
|
||||
ament_target_dependencies(test_publisher_subscription_count_api
|
||||
@@ -368,6 +414,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 +534,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
|
||||
@@ -481,11 +546,6 @@ if(TARGET test_find_weak_nodes)
|
||||
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
if(WIN32)
|
||||
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_externally_defined_services test_externally_defined_services.cpp)
|
||||
ament_target_dependencies(test_externally_defined_services
|
||||
"rcl"
|
||||
@@ -539,14 +599,6 @@ 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})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_interface_traits test_interface_traits.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_interface_traits)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -71,6 +71,8 @@ public:
|
||||
|
||||
void handle_message(std::shared_ptr<void> &, const rclcpp::MessageInfo &) override {}
|
||||
void handle_loaned_message(void *, const rclcpp::MessageInfo &) override {}
|
||||
void handle_serialized_message(
|
||||
const std::shared_ptr<rclcpp::SerializedMessage> &, const rclcpp::MessageInfo &) override {}
|
||||
void return_message(std::shared_ptr<void> &) override {}
|
||||
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> &) override {}
|
||||
};
|
||||
|
||||
@@ -125,7 +125,7 @@ protected:
|
||||
|
||||
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);
|
||||
|
||||
@@ -511,8 +511,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;
|
||||
}
|
||||
@@ -778,7 +780,7 @@ 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<
|
||||
|
||||
@@ -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);
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
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,539 @@ 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);
|
||||
// Type adapter to be used in tests.
|
||||
struct MyEmpty {};
|
||||
|
||||
// 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);
|
||||
template<>
|
||||
struct rclcpp::TypeAdapter<MyEmpty, test_msgs::msg::Empty>
|
||||
{
|
||||
using is_specialized = std::true_type;
|
||||
using custom_type = MyEmpty;
|
||||
using ros_message_type = test_msgs::msg::Empty;
|
||||
|
||||
// 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);
|
||||
static
|
||||
void
|
||||
convert_to_ros_message(const custom_type &, ros_message_type &)
|
||||
{}
|
||||
|
||||
static
|
||||
void
|
||||
convert_to_custom(const ros_message_type &, custom_type &)
|
||||
{}
|
||||
};
|
||||
|
||||
using MyTA = rclcpp::TypeAdapter<MyEmpty, test_msgs::msg::Empty>;
|
||||
|
||||
template<typename MessageT>
|
||||
class InstanceContextImpl
|
||||
{
|
||||
public:
|
||||
InstanceContextImpl() = default;
|
||||
virtual ~InstanceContextImpl() = default;
|
||||
|
||||
explicit InstanceContextImpl(rclcpp::AnySubscriptionCallback<MessageT> asc)
|
||||
: any_subscription_callback_(asc)
|
||||
{}
|
||||
|
||||
virtual
|
||||
rclcpp::AnySubscriptionCallback<MessageT>
|
||||
get_any_subscription_callback_to_test() const
|
||||
{
|
||||
return any_subscription_callback_;
|
||||
}
|
||||
|
||||
protected:
|
||||
rclcpp::AnySubscriptionCallback<MessageT> any_subscription_callback_;
|
||||
};
|
||||
|
||||
template<typename MessageT>
|
||||
class InstanceContext
|
||||
{
|
||||
public:
|
||||
InstanceContext(const std::string & name, std::shared_ptr<InstanceContextImpl<MessageT>> impl)
|
||||
: name(name), impl_(impl)
|
||||
{}
|
||||
|
||||
InstanceContext(
|
||||
const std::string & name,
|
||||
rclcpp::AnySubscriptionCallback<MessageT> asc)
|
||||
: name(name), impl_(std::make_shared<InstanceContextImpl<MessageT>>(asc))
|
||||
{}
|
||||
|
||||
InstanceContext(const InstanceContext & other)
|
||||
: InstanceContext(other.name, other.impl_) {}
|
||||
|
||||
rclcpp::AnySubscriptionCallback<MessageT>
|
||||
get_any_subscription_callback_to_test() const
|
||||
{
|
||||
return impl_->get_any_subscription_callback_to_test();
|
||||
}
|
||||
|
||||
std::string name;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<InstanceContextImpl<MessageT>> impl_;
|
||||
};
|
||||
|
||||
class DispatchTests
|
||||
: public TestAnySubscriptionCallback,
|
||||
public ::testing::WithParamInterface<InstanceContext<test_msgs::msg::Empty>>
|
||||
{};
|
||||
|
||||
class DispatchTestsWithTA
|
||||
: public TestAnySubscriptionCallback,
|
||||
public ::testing::WithParamInterface<InstanceContext<MyTA>>
|
||||
{};
|
||||
|
||||
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);
|
||||
auto
|
||||
format_parameter_with_ta(const ::testing::TestParamInfo<DispatchTestsWithTA::ParamType> & info)
|
||||
{
|
||||
return info.param.name;
|
||||
}
|
||||
|
||||
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++;
|
||||
};
|
||||
#define PARAMETERIZED_TESTS(DispatchTests_name) \
|
||||
/* Testing dispatch with shared_ptr<MessageT> as input */ \
|
||||
TEST_P(DispatchTests_name, 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_); \
|
||||
} \
|
||||
\
|
||||
/* Testing dispatch with shared_ptr<const MessageT> as input */ \
|
||||
TEST_P(DispatchTests_name, 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_); \
|
||||
} \
|
||||
\
|
||||
/* Testing dispatch with unique_ptr<MessageT> as input */ \
|
||||
TEST_P(DispatchTests_name, 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_); \
|
||||
}
|
||||
|
||||
any_subscription_callback_.set(const_shared_ptr_callback);
|
||||
PARAMETERIZED_TESTS(DispatchTests)
|
||||
PARAMETERIZED_TESTS(DispatchTestsWithTA)
|
||||
|
||||
// Ok to promote shared_ptr to ConstSharedPtr
|
||||
EXPECT_NO_THROW(any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
// Generic classes for testing callbacks using std::bind to class methods.
|
||||
template<typename MessageT, typename ... CallbackArgs>
|
||||
class BindContextImpl : public InstanceContextImpl<MessageT>
|
||||
{
|
||||
static constexpr size_t number_of_callback_args{sizeof...(CallbackArgs)};
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(msg_const_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 2);
|
||||
public:
|
||||
using InstanceContextImpl<MessageT>::InstanceContextImpl;
|
||||
virtual ~BindContextImpl() = default;
|
||||
|
||||
// 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);
|
||||
}
|
||||
void on_message(CallbackArgs ...) const {}
|
||||
|
||||
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++;
|
||||
};
|
||||
rclcpp::AnySubscriptionCallback<MessageT>
|
||||
get_any_subscription_callback_to_test() const override
|
||||
{
|
||||
if constexpr (number_of_callback_args == 1) {
|
||||
return rclcpp::AnySubscriptionCallback<MessageT>().set(
|
||||
std::bind(&BindContextImpl::on_message, this, std::placeholders::_1)
|
||||
);
|
||||
} else {
|
||||
return rclcpp::AnySubscriptionCallback<MessageT>().set(
|
||||
std::bind(&BindContextImpl::on_message, this, std::placeholders::_1, std::placeholders::_2)
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
any_subscription_callback_.set(
|
||||
std::move(const_shared_ptr_callback));
|
||||
template<typename MessageT, typename ... CallbackArgs>
|
||||
class BindContext : public InstanceContext<MessageT>
|
||||
{
|
||||
public:
|
||||
explicit BindContext(const std::string & name)
|
||||
: InstanceContext<MessageT>(name, std::make_shared<BindContextImpl<MessageT, CallbackArgs ...>>())
|
||||
{}
|
||||
};
|
||||
|
||||
// Ok to promote shared_ptr to ConstSharedPtr
|
||||
EXPECT_NO_THROW(any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
//
|
||||
// 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 &) {}
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(msg_const_shared_ptr_, message_info_));
|
||||
EXPECT_EQ(callback_count, 2);
|
||||
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<test_msgs::msg::Empty, const test_msgs::msg::Empty &>("bind_method"),
|
||||
BindContext<test_msgs::msg::Empty, const test_msgs::msg::Empty &, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
// 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);
|
||||
}
|
||||
void const_ref_ta_free_func(const MyEmpty &) {}
|
||||
void const_ref_ta_w_info_free_func(const MyEmpty &, const rclcpp::MessageInfo &) {}
|
||||
|
||||
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++;
|
||||
};
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ConstRefTACallbackTests,
|
||||
DispatchTestsWithTA,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const MyEmpty &) {})},
|
||||
InstanceContext<MyTA>{"lambda_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const MyEmpty &, const rclcpp::MessageInfo &) {})},
|
||||
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const test_msgs::msg::Empty &) {})},
|
||||
InstanceContext<MyTA>{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const test_msgs::msg::Empty &, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_ta_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_ta_w_info_free_func)},
|
||||
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<MyTA, const MyEmpty &>("bind_method_ta"),
|
||||
BindContext<MyTA, const MyEmpty &, const rclcpp::MessageInfo &>(
|
||||
"bind_method_ta_with_info"),
|
||||
BindContext<MyTA, const test_msgs::msg::Empty &>("bind_method"),
|
||||
BindContext<MyTA, const test_msgs::msg::Empty &, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter_with_ta
|
||||
);
|
||||
|
||||
any_subscription_callback_.set(unique_ptr_callback);
|
||||
//
|
||||
// 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 &)
|
||||
{}
|
||||
|
||||
// Message is copied into unique_ptr
|
||||
EXPECT_NO_THROW(any_subscription_callback_.dispatch(msg_shared_ptr_, message_info_));
|
||||
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<test_msgs::msg::Empty, std::unique_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<
|
||||
test_msgs::msg::Empty,
|
||||
std::unique_ptr<test_msgs::msg::Empty>,
|
||||
const rclcpp::MessageInfo &
|
||||
>("bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
EXPECT_THROW(
|
||||
any_subscription_callback_.dispatch_intra_process(msg_const_shared_ptr_, message_info_),
|
||||
std::runtime_error);
|
||||
EXPECT_EQ(callback_count, 1);
|
||||
void unique_ptr_ta_free_func(std::unique_ptr<MyEmpty>) {}
|
||||
void unique_ptr_ta_w_info_free_func(std::unique_ptr<MyEmpty>, const rclcpp::MessageInfo &) {}
|
||||
|
||||
// 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);
|
||||
}
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
UniquePtrCallbackTests,
|
||||
DispatchTestsWithTA,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::unique_ptr<MyEmpty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::unique_ptr<MyEmpty>, const rclcpp::MessageInfo &) {})},
|
||||
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::unique_ptr<test_msgs::msg::Empty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
unique_ptr_ta_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
unique_ptr_ta_w_info_free_func)},
|
||||
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
unique_ptr_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
unique_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>>("bind_method_ta"),
|
||||
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_ta_with_info"),
|
||||
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<MyTA, std::unique_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter_with_ta
|
||||
);
|
||||
|
||||
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 `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 &)
|
||||
{}
|
||||
|
||||
any_subscription_callback_.set(unique_ptr_callback);
|
||||
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<test_msgs::msg::Empty, std::shared_ptr<const test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<test_msgs::msg::Empty, std::shared_ptr<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);
|
||||
void shared_const_ptr_ta_free_func(std::shared_ptr<const MyEmpty>) {}
|
||||
void shared_const_ptr_ta_w_info_free_func(
|
||||
std::shared_ptr<const MyEmpty>, 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(
|
||||
SharedConstPtrCallbackTests,
|
||||
DispatchTestsWithTA,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<const MyEmpty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<const MyEmpty>, const rclcpp::MessageInfo &) {})},
|
||||
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<const test_msgs::msg::Empty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_const_ptr_ta_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_const_ptr_ta_w_info_free_func)},
|
||||
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_const_ptr_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_const_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<MyTA, std::shared_ptr<const MyEmpty>>("bind_method_ta"),
|
||||
BindContext<MyTA, std::shared_ptr<const MyEmpty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_ta_with_info"),
|
||||
BindContext<MyTA, std::shared_ptr<const test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<MyTA, std::shared_ptr<const test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter_with_ta
|
||||
);
|
||||
|
||||
// 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 `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<test_msgs::msg::Empty,
|
||||
const std::shared_ptr<const test_msgs::msg::Empty> &>("bind_method"),
|
||||
BindContext<test_msgs::msg::Empty, const std::shared_ptr<const test_msgs::msg::Empty> &,
|
||||
const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
void const_ref_shared_const_ptr_ta_free_func(const std::shared_ptr<const MyEmpty> &) {}
|
||||
void const_ref_shared_const_ptr_ta_w_info_free_func(
|
||||
const std::shared_ptr<const MyEmpty> &, const rclcpp::MessageInfo &)
|
||||
{}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ConstRefSharedConstPtrCallbackTests,
|
||||
DispatchTestsWithTA,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const std::shared_ptr<const MyEmpty> &) {})},
|
||||
InstanceContext<MyTA>{"lambda_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const std::shared_ptr<const MyEmpty> &, const rclcpp::MessageInfo &) {})},
|
||||
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const std::shared_ptr<const test_msgs::msg::Empty> &) {})},
|
||||
InstanceContext<MyTA>{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](const std::shared_ptr<const test_msgs::msg::Empty> &, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_shared_const_ptr_ta_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_shared_const_ptr_ta_w_info_free_func)},
|
||||
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_shared_const_ptr_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
const_ref_shared_const_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<MyTA, const std::shared_ptr<const MyEmpty> &>("bind_method_ta"),
|
||||
BindContext<MyTA, const std::shared_ptr<const MyEmpty> &, const rclcpp::MessageInfo &>(
|
||||
"bind_method_ta_with_info"),
|
||||
BindContext<MyTA, const std::shared_ptr<const test_msgs::msg::Empty> &>("bind_method"),
|
||||
BindContext<MyTA, const std::shared_ptr<const test_msgs::msg::Empty> &,
|
||||
const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter_with_ta
|
||||
);
|
||||
|
||||
//
|
||||
// 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<test_msgs::msg::Empty, std::shared_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<test_msgs::msg::Empty, std::shared_ptr<test_msgs::msg::Empty>,
|
||||
const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter
|
||||
);
|
||||
|
||||
void shared_ptr_ta_free_func(std::shared_ptr<MyEmpty>) {}
|
||||
void shared_ptr_ta_w_info_free_func(
|
||||
std::shared_ptr<MyEmpty>, const rclcpp::MessageInfo &)
|
||||
{}
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
SharedPtrCallbackTests,
|
||||
DispatchTestsWithTA,
|
||||
::testing::Values(
|
||||
// lambda
|
||||
InstanceContext<MyTA>{"lambda_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<MyEmpty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<MyEmpty>, const rclcpp::MessageInfo &) {})},
|
||||
InstanceContext<MyTA>{"lambda", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<test_msgs::msg::Empty>) {})},
|
||||
InstanceContext<MyTA>{"lambda_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
[](std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &) {})},
|
||||
// free function
|
||||
InstanceContext<MyTA>{"free_function_ta", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_ptr_ta_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_ta_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_ptr_ta_w_info_free_func)},
|
||||
InstanceContext<MyTA>{"free_function", rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_ptr_free_func)},
|
||||
InstanceContext<MyTA>{"free_function_with_info",
|
||||
rclcpp::AnySubscriptionCallback<MyTA>().set(
|
||||
shared_ptr_w_info_free_func)},
|
||||
// bind function
|
||||
BindContext<MyTA, std::shared_ptr<MyEmpty>>("bind_method_ta"),
|
||||
BindContext<MyTA, std::shared_ptr<MyEmpty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_ta_with_info"),
|
||||
BindContext<MyTA, std::shared_ptr<test_msgs::msg::Empty>>("bind_method"),
|
||||
BindContext<MyTA, std::shared_ptr<test_msgs::msg::Empty>, const rclcpp::MessageInfo &>(
|
||||
"bind_method_with_info")
|
||||
),
|
||||
format_parameter_with_ta
|
||||
);
|
||||
|
||||
@@ -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
|
||||
@@ -52,8 +52,8 @@ class PublisherBase
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
|
||||
|
||||
PublisherBase()
|
||||
: qos(rclcpp::QoS(10)),
|
||||
explicit PublisherBase(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: qos_profile(qos),
|
||||
topic_name("topic")
|
||||
{}
|
||||
|
||||
@@ -76,7 +76,7 @@ public:
|
||||
rclcpp::QoS
|
||||
get_actual_qos() const
|
||||
{
|
||||
return qos;
|
||||
return qos_profile;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -93,7 +93,7 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
rclcpp::QoS qos;
|
||||
rclcpp::QoS qos_profile;
|
||||
std::string topic_name;
|
||||
uint64_t intra_process_publisher_id_;
|
||||
IntraProcessManagerWeakPtr weak_ipm_;
|
||||
@@ -111,9 +111,9 @@ public:
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>)
|
||||
|
||||
Publisher()
|
||||
explicit Publisher(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: PublisherBase(qos)
|
||||
{
|
||||
qos = rclcpp::QoS(10);
|
||||
auto allocator = std::make_shared<Alloc>();
|
||||
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
|
||||
}
|
||||
@@ -136,7 +136,10 @@ namespace buffers
|
||||
{
|
||||
namespace mock
|
||||
{
|
||||
template<typename MessageT>
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename MessageDeleter = std::default_delete<MessageT>>
|
||||
class IntraProcessBuffer
|
||||
{
|
||||
public:
|
||||
@@ -191,8 +194,8 @@ class SubscriptionIntraProcessBase
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(SubscriptionIntraProcessBase)
|
||||
|
||||
SubscriptionIntraProcessBase()
|
||||
: qos_profile(rmw_qos_profile_default), topic_name("topic")
|
||||
explicit SubscriptionIntraProcessBase(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: qos_profile(qos), topic_name("topic")
|
||||
{}
|
||||
|
||||
virtual ~SubscriptionIntraProcessBase() {}
|
||||
@@ -200,7 +203,7 @@ public:
|
||||
virtual bool
|
||||
use_take_shared_method() const = 0;
|
||||
|
||||
rmw_qos_profile_t
|
||||
QoS
|
||||
get_actual_qos()
|
||||
{
|
||||
return qos_profile;
|
||||
@@ -212,18 +215,21 @@ public:
|
||||
return topic_name;
|
||||
}
|
||||
|
||||
rmw_qos_profile_t qos_profile;
|
||||
rclcpp::QoS qos_profile;
|
||||
const char * topic_name;
|
||||
};
|
||||
|
||||
template<typename MessageT>
|
||||
class SubscriptionIntraProcess : public SubscriptionIntraProcessBase
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>>
|
||||
class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
|
||||
|
||||
SubscriptionIntraProcess()
|
||||
: take_shared_method(false)
|
||||
explicit SubscriptionIntraProcessBuffer(rclcpp::QoS qos)
|
||||
: SubscriptionIntraProcessBase(qos), take_shared_method(false)
|
||||
{
|
||||
buffer = std::make_unique<rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>>();
|
||||
}
|
||||
@@ -259,6 +265,25 @@ public:
|
||||
typename rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>::UniquePtr buffer;
|
||||
};
|
||||
|
||||
template<
|
||||
typename MessageT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename Deleter = std::default_delete<MessageT>>
|
||||
class SubscriptionIntraProcess : public SubscriptionIntraProcessBuffer<
|
||||
MessageT,
|
||||
Alloc,
|
||||
Deleter
|
||||
>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
|
||||
|
||||
explicit SubscriptionIntraProcess(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(qos)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace mock
|
||||
} // namespace experimental
|
||||
} // namespace rclcpp
|
||||
@@ -267,12 +292,14 @@ public:
|
||||
#define RCLCPP__PUBLISHER_HPP_
|
||||
#define RCLCPP__PUBLISHER_BASE_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
|
||||
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BASE_HPP_
|
||||
// Force ipm to use our mock publisher class.
|
||||
#define Publisher mock::Publisher
|
||||
#define PublisherBase mock::PublisherBase
|
||||
#define IntraProcessBuffer mock::IntraProcessBuffer
|
||||
#define SubscriptionIntraProcessBase mock::SubscriptionIntraProcessBase
|
||||
#define SubscriptionIntraProcessBuffer mock::SubscriptionIntraProcessBuffer
|
||||
#define SubscriptionIntraProcess mock::SubscriptionIntraProcess
|
||||
#include "../src/rclcpp/intra_process_manager.cpp"
|
||||
#undef Publisher
|
||||
@@ -304,7 +331,7 @@ void Publisher<T, Alloc>::publish(MessageUniquePtr msg)
|
||||
ipm->template do_intra_process_publish<T, Alloc>(
|
||||
intra_process_publisher_id_,
|
||||
std::move(msg),
|
||||
message_allocator_);
|
||||
*message_allocator_);
|
||||
}
|
||||
|
||||
} // namespace mock
|
||||
@@ -332,15 +359,12 @@ TEST(TestIntraProcessManager, add_pub_sub) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>();
|
||||
p1->qos.get_rmw_qos_profile().reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
|
||||
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
|
||||
|
||||
auto p2 = std::make_shared<PublisherT>();
|
||||
p2->qos.get_rmw_qos_profile().reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
|
||||
auto p2 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
|
||||
p2->topic_name = "different_topic_name";
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
s1->qos_profile.reliability = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT;
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).best_effort());
|
||||
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
auto p2_id = ipm->add_publisher(p2);
|
||||
@@ -356,11 +380,9 @@ TEST(TestIntraProcessManager, add_pub_sub) {
|
||||
ASSERT_EQ(0u, p2_subs);
|
||||
ASSERT_EQ(0u, non_existing_pub_subs);
|
||||
|
||||
auto p3 = std::make_shared<PublisherT>();
|
||||
p3->qos.get_rmw_qos_profile().reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE;
|
||||
auto p3 = std::make_shared<PublisherT>(rclcpp::QoS(10).reliable());
|
||||
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
s2->qos_profile.reliability = RMW_QOS_POLICY_RELIABILITY_RELIABLE;
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).reliable());
|
||||
|
||||
auto s2_id = ipm->add_subscription(s2);
|
||||
auto p3_id = ipm->add_publisher(p3);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user