Compare commits
40 Commits
brawner/rc
...
6.1.0
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9c62c1c946 | ||
|
|
5b6e0af339 | ||
|
|
c64ba72bbd | ||
|
|
eddb938aec | ||
|
|
0dc782aca8 | ||
|
|
ea0ee50318 | ||
|
|
35c73aa61e | ||
|
|
08963df926 | ||
|
|
f5e35bda86 | ||
|
|
a7db1dcca2 | ||
|
|
ba1056fe63 | ||
|
|
048d9b57c0 | ||
|
|
de353f9e45 | ||
|
|
cba6f20988 | ||
|
|
71a58d40f1 | ||
|
|
add6d61231 | ||
|
|
8c8c268aad | ||
|
|
27d1b11647 | ||
|
|
4c5986aa2d | ||
|
|
dd0f97f179 | ||
|
|
7d257177e0 | ||
|
|
a95efa452e | ||
|
|
06465ba827 | ||
|
|
5fe6840ad1 | ||
|
|
361be5e4c0 | ||
|
|
58bd8d6c21 | ||
|
|
3b04b056e3 | ||
|
|
0aa416e17f | ||
|
|
79403119e4 | ||
|
|
3ae5170b52 | ||
|
|
2309811814 | ||
|
|
aa159a5e8f | ||
|
|
8a8e46d7e9 | ||
|
|
1ddc8c815c | ||
|
|
579e9d01d6 | ||
|
|
371074523a | ||
|
|
eb7c46ea43 | ||
|
|
0810140e18 | ||
|
|
5d9db5de74 | ||
|
|
8e5ddb1f81 |
@@ -2,6 +2,94 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
6.1.0 (2020-12-10)
|
||||
------------------
|
||||
* Add getters to rclcpp::qos and rclcpp::Policy enum classes (`#1467 <https://github.com/ros2/rclcpp/issues/1467>`_)
|
||||
* Change nullptr checks to use ASSERT_TRUE. (`#1486 <https://github.com/ros2/rclcpp/issues/1486>`_)
|
||||
* Adjust logic around finding and erasing guard_condition (`#1474 <https://github.com/ros2/rclcpp/issues/1474>`_)
|
||||
* Update QDs to QL 1 (`#1477 <https://github.com/ros2/rclcpp/issues/1477>`_)
|
||||
* Add performance tests for parameter transport (`#1463 <https://github.com/ros2/rclcpp/issues/1463>`_)
|
||||
* Contributors: Chris Lalancette, Ivan Santiago Paunovic, Scott K Logan, Stephen Brawner
|
||||
|
||||
6.0.0 (2020-11-18)
|
||||
------------------
|
||||
* Move ownership of shutdown_guard_condition to executors/graph_listener (`#1404 <https://github.com/ros2/rclcpp/issues/1404>`_)
|
||||
* Add options to automatically declare qos parameters when creating a publisher/subscription (`#1465 <https://github.com/ros2/rclcpp/issues/1465>`_)
|
||||
* Add `take_data` to `Waitable` and `data` to `AnyExecutable` (`#1241 <https://github.com/ros2/rclcpp/issues/1241>`_)
|
||||
* Add benchmarks for node parameters interface (`#1444 <https://github.com/ros2/rclcpp/issues/1444>`_)
|
||||
* Remove allocation from executor::remove_node() (`#1448 <https://github.com/ros2/rclcpp/issues/1448>`_)
|
||||
* Fix test crashes on CentOS 7 (`#1449 <https://github.com/ros2/rclcpp/issues/1449>`_)
|
||||
* Bump rclcpp packages to Quality Level 2 (`#1445 <https://github.com/ros2/rclcpp/issues/1445>`_)
|
||||
* Added executor benchmark tests (`#1413 <https://github.com/ros2/rclcpp/issues/1413>`_)
|
||||
* Add fully-qualified namespace to WeakCallbackGroupsToNodesMap (`#1435 <https://github.com/ros2/rclcpp/issues/1435>`_)
|
||||
* Contributors: Alejandro Hernández Cordero, Audrow Nash, Chris Lalancette, Ivan Santiago Paunovic, Louise Poubel, Scott K Logan, brawner
|
||||
|
||||
5.1.0 (2020-11-02)
|
||||
------------------
|
||||
* Deprecate Duration(rcl_duration_value_t) in favor of static Duration::from_nanoseconds(rcl_duration_value_t) (`#1432 <https://github.com/ros2/rclcpp/issues/1432>`_)
|
||||
* Avoid parsing arguments twice in `rclcpp::init_and_remove_ros_arguments` (`#1415 <https://github.com/ros2/rclcpp/issues/1415>`_)
|
||||
* Add service and client benchmarks (`#1425 <https://github.com/ros2/rclcpp/issues/1425>`_)
|
||||
* Set CMakeLists to only use default rmw for benchmarks (`#1427 <https://github.com/ros2/rclcpp/issues/1427>`_)
|
||||
* Update tracetools' QL in rclcpp's QD (`#1428 <https://github.com/ros2/rclcpp/issues/1428>`_)
|
||||
* Add missing locking to the rclcpp_action::ServerBase. (`#1421 <https://github.com/ros2/rclcpp/issues/1421>`_)
|
||||
* Initial benchmark tests for rclcpp::init/shutdown create/destroy node (`#1411 <https://github.com/ros2/rclcpp/issues/1411>`_)
|
||||
* Refactor test CMakeLists in prep for benchmarks (`#1422 <https://github.com/ros2/rclcpp/issues/1422>`_)
|
||||
* Add methods in topic and service interface to resolve a name (`#1410 <https://github.com/ros2/rclcpp/issues/1410>`_)
|
||||
* Update deprecated gtest macros (`#1370 <https://github.com/ros2/rclcpp/issues/1370>`_)
|
||||
* Clear members for StaticExecutorEntitiesCollector to avoid shared_ptr dependency (`#1303 <https://github.com/ros2/rclcpp/issues/1303>`_)
|
||||
* Increase test timeouts of slow running tests with rmw_connext_cpp (`#1400 <https://github.com/ros2/rclcpp/issues/1400>`_)
|
||||
* Avoid self dependency that not destoryed (`#1301 <https://github.com/ros2/rclcpp/issues/1301>`_)
|
||||
* Update maintainers (`#1384 <https://github.com/ros2/rclcpp/issues/1384>`_)
|
||||
* Add clock qos to node options (`#1375 <https://github.com/ros2/rclcpp/issues/1375>`_)
|
||||
* Fix NodeOptions copy constructor (`#1376 <https://github.com/ros2/rclcpp/issues/1376>`_)
|
||||
* Make sure to clean the external client/service handle. (`#1296 <https://github.com/ros2/rclcpp/issues/1296>`_)
|
||||
* Increase coverage of WaitSetTemplate (`#1368 <https://github.com/ros2/rclcpp/issues/1368>`_)
|
||||
* Increase coverage of guard_condition.cpp to 100% (`#1369 <https://github.com/ros2/rclcpp/issues/1369>`_)
|
||||
* Add coverage statement (`#1367 <https://github.com/ros2/rclcpp/issues/1367>`_)
|
||||
* Tests for LoanedMessage with mocked loaned message publisher (`#1366 <https://github.com/ros2/rclcpp/issues/1366>`_)
|
||||
* Add unit tests for qos and qos_event files (`#1352 <https://github.com/ros2/rclcpp/issues/1352>`_)
|
||||
* Finish coverage of publisher API (`#1365 <https://github.com/ros2/rclcpp/issues/1365>`_)
|
||||
* Finish API coverage on executors. (`#1364 <https://github.com/ros2/rclcpp/issues/1364>`_)
|
||||
* Add test for ParameterService (`#1355 <https://github.com/ros2/rclcpp/issues/1355>`_)
|
||||
* Add time API coverage tests (`#1347 <https://github.com/ros2/rclcpp/issues/1347>`_)
|
||||
* Add timer coverage tests (`#1363 <https://github.com/ros2/rclcpp/issues/1363>`_)
|
||||
* Add in additional tests for parameter_client.cpp coverage.
|
||||
* Minor fixes to the parameter_service.cpp file.
|
||||
* reset rcl_context shared_ptr after calling rcl_init sucessfully (`#1357 <https://github.com/ros2/rclcpp/issues/1357>`_)
|
||||
* Improved test publisher - zero qos history depth value exception (`#1360 <https://github.com/ros2/rclcpp/issues/1360>`_)
|
||||
* Covered resolve_use_intra_process (`#1359 <https://github.com/ros2/rclcpp/issues/1359>`_)
|
||||
* Improve test_subscription_options (`#1358 <https://github.com/ros2/rclcpp/issues/1358>`_)
|
||||
* Add in more tests for init_options coverage. (`#1353 <https://github.com/ros2/rclcpp/issues/1353>`_)
|
||||
* Test the remaining node public API (`#1342 <https://github.com/ros2/rclcpp/issues/1342>`_)
|
||||
* Complete coverage of Parameter and ParameterValue API (`#1344 <https://github.com/ros2/rclcpp/issues/1344>`_)
|
||||
* Add in more tests for the utilities. (`#1349 <https://github.com/ros2/rclcpp/issues/1349>`_)
|
||||
* Add in two more tests for expand_topic_or_service_name. (`#1350 <https://github.com/ros2/rclcpp/issues/1350>`_)
|
||||
* Add tests for node_options API (`#1343 <https://github.com/ros2/rclcpp/issues/1343>`_)
|
||||
* Add in more coverage for expand_topic_or_service_name. (`#1346 <https://github.com/ros2/rclcpp/issues/1346>`_)
|
||||
* Test exception in spin_until_future_complete. (`#1345 <https://github.com/ros2/rclcpp/issues/1345>`_)
|
||||
* Add coverage tests graph_listener (`#1330 <https://github.com/ros2/rclcpp/issues/1330>`_)
|
||||
* Add in unit tests for the Executor class.
|
||||
* Allow mimick patching of methods with up to 9 arguments.
|
||||
* Improve the error messages in the Executor class.
|
||||
* Add coverage for client API (`#1329 <https://github.com/ros2/rclcpp/issues/1329>`_)
|
||||
* Increase service coverage (`#1332 <https://github.com/ros2/rclcpp/issues/1332>`_)
|
||||
* Make more of the static entity collector API private.
|
||||
* Const-ify more of the static executor.
|
||||
* Add more tests for the static single threaded executor.
|
||||
* Many more tests for the static_executor_entities_collector.
|
||||
* Get one more line of code coverage in memory_strategy.cpp
|
||||
* Bugfix when adding callback group.
|
||||
* Fix typos in comments.
|
||||
* Remove deprecated executor::FutureReturnCode APIs. (`#1327 <https://github.com/ros2/rclcpp/issues/1327>`_)
|
||||
* Increase coverage of publisher/subscription API (`#1325 <https://github.com/ros2/rclcpp/issues/1325>`_)
|
||||
* Not finalize guard condition while destructing SubscriptionIntraProcess (`#1307 <https://github.com/ros2/rclcpp/issues/1307>`_)
|
||||
* Expose qos setting for /rosout (`#1247 <https://github.com/ros2/rclcpp/issues/1247>`_)
|
||||
* Add coverage for missing API (except executors) (`#1326 <https://github.com/ros2/rclcpp/issues/1326>`_)
|
||||
* Include topic name in QoS mismatch warning messages (`#1286 <https://github.com/ros2/rclcpp/issues/1286>`_)
|
||||
* Add coverage tests context functions (`#1321 <https://github.com/ros2/rclcpp/issues/1321>`_)
|
||||
* Increase coverage of node_interfaces, including with mocking rcl errors (`#1322 <https://github.com/ros2/rclcpp/issues/1322>`_)
|
||||
* Contributors: Ada-King, Alejandro Hernández Cordero, Audrow Nash, Barry Xu, Chen Lihui, Chris Lalancette, Christophe Bedard, Ivan Santiago Paunovic, Jorge Perez, Morgan Quigley, brawner
|
||||
|
||||
5.0.0 (2020-09-18)
|
||||
------------------
|
||||
* Make node_graph::count_graph_users() const (`#1320 <https://github.com/ros2/rclcpp/issues/1320>`_)
|
||||
|
||||
@@ -85,6 +85,7 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/publisher_base.cpp
|
||||
src/rclcpp/qos.cpp
|
||||
src/rclcpp/qos_event.cpp
|
||||
src/rclcpp/qos_overriding_options.cpp
|
||||
src/rclcpp/serialization.cpp
|
||||
src/rclcpp/serialized_message.cpp
|
||||
src/rclcpp/service.cpp
|
||||
@@ -222,12 +223,6 @@ if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
|
||||
# For benchmark tests. Currently there is a bug where these targets can't be
|
||||
# found if these calls are added to the CMakeLists.txt in test/rclcpp/benchmark
|
||||
find_package(benchmark REQUIRED)
|
||||
find_package(osrf_testing_tools_cpp REQUIRED)
|
||||
find_package(performance_test_fixture REQUIRED)
|
||||
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ This document is a declaration of software quality for the `rclcpp` package, bas
|
||||
|
||||
# rclcpp Quality Declaration
|
||||
|
||||
The package `rclcpp` claims to be in the **Quality Level 3** category.
|
||||
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.
|
||||
|
||||
@@ -82,13 +82,13 @@ The API is publicly available in its [ROS 2 API documentation](http://docs.ros2.
|
||||
|
||||
The license for `rclcpp` is Apache 2.0, and a summary is in each source file, the type is declared in the [`package.xml`](./package.xml) manifest file, and a full copy of the license is in the [`LICENSE`](../LICENSE) file.
|
||||
|
||||
There is an automated test which runs a linter that ensures each file has a license statement. [Here](http://build.ros2.org/view/Fpr/job/Fpr__rclcpp__ubuntu_focal_amd64/lastSuccessfulBuild/testReport/rclcpp/) can be found a list with the latest results of the various linters being run on the package.
|
||||
There is an automated test which runs a linter that ensures each file has a license statement. [Here](http://build.ros2.org/view/Rpr/job/Rpr__rclcpp__ubuntu_focal_amd64/lastCompletedBuild/testReport/rclcpp/) can be found a list with the latest results of the various linters being run on the package.
|
||||
|
||||
### Copyright Statements [3.iv]
|
||||
|
||||
The copyright holders each provide a statement of copyright in each source code file in `rclcpp`.
|
||||
|
||||
There is an automated test which runs a linter that ensures each file has at least one copyright statement. Latest linter result report can be seen [here](http://build.ros2.org/view/Fpr/job/Fpr__rclcpp__ubuntu_focal_amd64/lastSuccessfulBuild/testReport/rclcpp/copyright/).
|
||||
There is an automated test which runs a linter that ensures each file has at least one copyright statement. Latest linter result report can be seen [here](http://build.ros2.org/view/Rpr/job/Rpr__rclcpp__ubuntu_focal_amd64/lastCompletedBuild/testReport/rclcpp/copyright/).
|
||||
|
||||
## Testing [4]
|
||||
|
||||
@@ -121,13 +121,21 @@ 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/lastSuccessfulBuild/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://index.ros.org/doc/ros2/Contributing/ROS-2-On-boarding-Guide/#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]
|
||||
|
||||
It is not yet defined if this package requires performance testing and how addresses this topic.
|
||||
`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.
|
||||
|
||||
The performance tests of `rclcpp` are located in the [test/benchmark directory](https://github.com/ros2/rclcpp/tree/master/rclcpp/test/benchmark).
|
||||
|
||||
System level performance benchmarks that cover features of `rclcpp` can be found at:
|
||||
* [Benchmarks](http://build.ros2.org/view/Rci/job/Rci__benchmark_ubuntu_focal_amd64/BenchmarkTable/)
|
||||
* [Performance](http://build.ros2.org/view/Rci/job/Rci__nightly-performance_ubuntu_focal_amd64/lastCompletedBuild/)
|
||||
|
||||
Changes that introduce regressions in performance must be adequately justified in order to be accepted and merged.
|
||||
|
||||
### Linters and Static Analysis [4.v]
|
||||
|
||||
@@ -155,49 +163,49 @@ It also has several test dependencies, which do not affect the resulting quality
|
||||
|
||||
The `libstatistics_collector` package provides lightweight aggregation utilities to collect statistics and measure message metrics.
|
||||
|
||||
It is **Quality Level 3**, see its [Quality Declaration document](https://github.com/ros-tooling/libstatistics_collector/tree/master/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros-tooling/libstatistics_collector/tree/master/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rcl`
|
||||
|
||||
`rcl` a library to support implementation of language specific ROS 2 Client Libraries.
|
||||
|
||||
It is **Quality Level 3**, see its [Quality Declaration document](https://github.com/ros2/rcl/blob/master/rcl/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcl/blob/master/rcl/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rcl_yaml_param_parser`
|
||||
|
||||
The `rcl_yaml_param_parser` package provides an API that is used to parse YAML configuration files which may be used to configure ROS and specific nodes.
|
||||
|
||||
It is **Quality Level 3**, see its [Quality Declaration document](https://github.com/ros2/rcl/tree/master/rcl_yaml_param_parser/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcl/tree/master/rcl_yaml_param_parser/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rcpputils`
|
||||
|
||||
The `rcpputils` package provides an API which contains common utilities and data structures useful when programming in C++.
|
||||
|
||||
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros2/rcpputils/blob/master/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcpputils/blob/master/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rcutils`
|
||||
|
||||
The `rcutils` package provides an API which contains common utilities and data structures useful when programming in C.
|
||||
|
||||
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros2/rcutils/blob/master/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcutils/blob/master/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rmw`
|
||||
|
||||
`rmw` is the ROS 2 middleware library.
|
||||
|
||||
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros2/rmw/blob/master/rmw/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rmw/blob/master/rmw/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `statistics_msgs`
|
||||
|
||||
The `statistics_msgs` package contains ROS 2 message definitions for reporting statistics for topics and system resources.
|
||||
|
||||
It is **Quality Level 3**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/statistics_msgs/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/statistics_msgs/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `tracetools`
|
||||
|
||||
The `tracetools` package provides utilities for instrumenting the code in `rclcpp` so that it may be traced for debugging and performance analysis.
|
||||
|
||||
It is **Quality Level 2**, see its [Quality Declaration document](https://gitlab.com/micro-ROS/ros_tracing/ros2_tracing/-/blob/master/tracetools/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://gitlab.com/ros-tracing/ros2_tracing/-/blob/master/tracetools/QUALITY_DECLARATION.md).
|
||||
|
||||
### Direct Runtime non-ROS Dependency [5.iii]
|
||||
|
||||
|
||||
@@ -6,4 +6,4 @@ Visit the [rclcpp API documentation](http://docs.ros2.org/latest/api/rclcpp/) fo
|
||||
|
||||
## Quality Declaration
|
||||
|
||||
This package claims to be in the **Quality Level 3** category, see the [Quality Declaration](QUALITY_DECLARATION.md) for more details.
|
||||
This package claims to be in the **Quality Level 1** category, see the [Quality Declaration](QUALITY_DECLARATION.md) for more details.
|
||||
|
||||
@@ -47,6 +47,7 @@ struct AnyExecutable
|
||||
// These are used to keep the scope on the containing items
|
||||
rclcpp::CallbackGroup::SharedPtr callback_group;
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base;
|
||||
std::shared_ptr<void> data;
|
||||
};
|
||||
|
||||
namespace executor
|
||||
|
||||
@@ -248,67 +248,6 @@ public:
|
||||
void
|
||||
interrupt_all_sleep_for();
|
||||
|
||||
/// Get a handle to the guard condition which is triggered when interrupted.
|
||||
/**
|
||||
* This guard condition is triggered any time interrupt_all_wait_sets() is
|
||||
* called, which may be called by the user, or shutdown().
|
||||
* And in turn, shutdown() may be called by the user, the destructor of this
|
||||
* context, or the signal handler if installed and shutdown_on_sigint is true
|
||||
* for this context.
|
||||
*
|
||||
* The first time that this function is called for a given wait set a new guard
|
||||
* condition will be created and returned; thereafter the same guard condition
|
||||
* will be returned for the same wait set.
|
||||
* This mechanism is designed to ensure that the same guard condition is not
|
||||
* reused across wait sets (e.g., when using multiple executors in the same
|
||||
* process).
|
||||
* This method will throw an exception if initialization of the guard
|
||||
* condition fails.
|
||||
*
|
||||
* The returned guard condition needs to be released with the
|
||||
* release_interrupt_guard_condition() method in order to reclaim resources.
|
||||
*
|
||||
* \param[in] wait_set Pointer to the rcl_wait_set_t that will be using the
|
||||
* resulting guard condition.
|
||||
* \return Pointer to the guard condition.
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rcl_guard_condition_t *
|
||||
get_interrupt_guard_condition(rcl_wait_set_t * wait_set);
|
||||
|
||||
/// Release the previously allocated guard condition which is triggered when interrupted.
|
||||
/**
|
||||
* If you previously called get_interrupt_guard_condition() for a given wait
|
||||
* set to get a interrupt guard condition, then you should call
|
||||
* release_interrupt_guard_condition() when you're done, to free that
|
||||
* condition.
|
||||
* Will throw an exception if get_interrupt_guard_condition() wasn't
|
||||
* previously called for the given wait set.
|
||||
*
|
||||
* After calling this, the pointer returned by get_interrupt_guard_condition()
|
||||
* for the given wait_set is invalid.
|
||||
*
|
||||
* \param[in] wait_set Pointer to the rcl_wait_set_t that was using the
|
||||
* resulting guard condition.
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
* \throws std::runtime_error if a nonexistent wait set is trying to release sigint guard condition.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
release_interrupt_guard_condition(rcl_wait_set_t * wait_set);
|
||||
|
||||
/// Nothrow version of release_interrupt_guard_condition(), logs to RCLCPP_ERROR instead.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
release_interrupt_guard_condition(rcl_wait_set_t * wait_set, const std::nothrow_t &) noexcept;
|
||||
|
||||
/// Interrupt any blocking executors, or wait sets associated with this context.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
interrupt_all_wait_sets();
|
||||
|
||||
/// Return a singleton instance for the SubContext type, constructing one if necessary.
|
||||
template<typename SubContext, typename ... Args>
|
||||
std::shared_ptr<SubContext>
|
||||
@@ -368,11 +307,6 @@ private:
|
||||
/// Mutex for protecting the global condition variable.
|
||||
std::mutex interrupt_mutex_;
|
||||
|
||||
/// Mutex to protect sigint_guard_cond_handles_.
|
||||
std::mutex interrupt_guard_cond_handles_mutex_;
|
||||
/// Guard conditions for interrupting of associated wait sets on interrupt_all_wait_sets().
|
||||
std::unordered_map<rcl_wait_set_t *, rcl_guard_condition_t> interrupt_guard_cond_handles_;
|
||||
|
||||
/// Keep shared ownership of global vector of weak contexts
|
||||
std::shared_ptr<WeakContextsWrapper> weak_contexts_;
|
||||
};
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
@@ -24,11 +25,57 @@
|
||||
#include "rclcpp/publisher_factory.hpp"
|
||||
#include "rclcpp/publisher_options.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
#include "rclcpp/detail/qos_parameters.hpp"
|
||||
|
||||
#include "rmw/qos_profiles.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
/// Create and return a publisher of the given MessageT type.
|
||||
template<
|
||||
typename MessageT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename PublisherT = rclcpp::Publisher<MessageT, AllocatorT>,
|
||||
typename NodeParametersT,
|
||||
typename NodeTopicsT>
|
||||
std::shared_ptr<PublisherT>
|
||||
create_publisher(
|
||||
NodeParametersT & node_parameters,
|
||||
NodeTopicsT & node_topics,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
)
|
||||
{
|
||||
auto node_topics_interface = rclcpp::node_interfaces::get_node_topics_interface(node_topics);
|
||||
const rclcpp::QoS & actual_qos = options.qos_overriding_options.get_policy_kinds().size() ?
|
||||
rclcpp::detail::declare_qos_parameters(
|
||||
options.qos_overriding_options, node_parameters,
|
||||
node_topics_interface->resolve_topic_name(topic_name),
|
||||
qos, rclcpp::detail::PublisherQosParametersTraits{}) :
|
||||
qos;
|
||||
|
||||
// Create the publisher.
|
||||
auto pub = node_topics_interface->create_publisher(
|
||||
topic_name,
|
||||
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(options),
|
||||
actual_qos
|
||||
);
|
||||
|
||||
// Add the publisher to the node topics interface.
|
||||
node_topics_interface->add_publisher(pub, options.callback_group);
|
||||
|
||||
return std::dynamic_pointer_cast<PublisherT>(pub);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
|
||||
/// Create and return a publisher of the given MessageT type.
|
||||
/**
|
||||
* The NodeT type only needs to have a method called get_node_topics_interface()
|
||||
@@ -49,21 +96,28 @@ create_publisher(
|
||||
)
|
||||
)
|
||||
{
|
||||
// Extract the NodeTopicsInterface from the NodeT.
|
||||
using rclcpp::node_interfaces::get_node_topics_interface;
|
||||
auto node_topics = get_node_topics_interface(node);
|
||||
return detail::create_publisher<MessageT, AllocatorT, PublisherT>(
|
||||
node, node, topic_name, qos, options);
|
||||
}
|
||||
|
||||
// Create the publisher.
|
||||
auto pub = node_topics->create_publisher(
|
||||
topic_name,
|
||||
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(options),
|
||||
qos
|
||||
);
|
||||
|
||||
// Add the publisher to the node topics interface.
|
||||
node_topics->add_publisher(pub, options.callback_group);
|
||||
|
||||
return std::dynamic_pointer_cast<PublisherT>(pub);
|
||||
/// Create and return a publisher of the given MessageT type.
|
||||
template<
|
||||
typename MessageT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename PublisherT = rclcpp::Publisher<MessageT, AllocatorT>>
|
||||
std::shared_ptr<PublisherT>
|
||||
create_publisher(
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & node_parameters,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr & node_topics,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
|
||||
)
|
||||
)
|
||||
{
|
||||
return detail::create_publisher<MessageT, AllocatorT, PublisherT>(
|
||||
node_parameters, node_topics, topic_name, qos, options);
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -41,6 +41,105 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename CallbackMessageT,
|
||||
typename SubscriptionT,
|
||||
typename MessageMemoryStrategyT,
|
||||
typename NodeParametersT,
|
||||
typename NodeTopicsT>
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
NodeParametersT & node_parameters,
|
||||
NodeTopicsT & node_topics,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
CallbackT && callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
|
||||
),
|
||||
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat = (
|
||||
MessageMemoryStrategyT::create_default()
|
||||
)
|
||||
)
|
||||
{
|
||||
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>>
|
||||
subscription_topic_stats = nullptr;
|
||||
|
||||
if (rclcpp::detail::resolve_enable_topic_statistics(
|
||||
options,
|
||||
*node_topics_interface->get_node_base_interface()))
|
||||
{
|
||||
if (options.topic_stats_options.publish_period <= std::chrono::milliseconds(0)) {
|
||||
throw std::invalid_argument(
|
||||
"topic_stats_options.publish_period must be greater than 0, specified value of " +
|
||||
std::to_string(options.topic_stats_options.publish_period.count()) +
|
||||
" ms");
|
||||
}
|
||||
|
||||
std::shared_ptr<Publisher<statistics_msgs::msg::MetricsMessage>>
|
||||
publisher = rclcpp::detail::create_publisher<statistics_msgs::msg::MetricsMessage>(
|
||||
node_parameters,
|
||||
node_topics_interface,
|
||||
options.topic_stats_options.publish_topic,
|
||||
qos);
|
||||
|
||||
subscription_topic_stats = std::make_shared<
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
|
||||
>(node_topics_interface->get_node_base_interface()->get_name(), publisher);
|
||||
|
||||
std::weak_ptr<
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
|
||||
> weak_subscription_topic_stats(subscription_topic_stats);
|
||||
auto sub_call_back = [weak_subscription_topic_stats]() {
|
||||
auto subscription_topic_stats = weak_subscription_topic_stats.lock();
|
||||
if (subscription_topic_stats) {
|
||||
subscription_topic_stats->publish_message();
|
||||
}
|
||||
};
|
||||
|
||||
auto node_timer_interface = node_topics_interface->get_node_timers_interface();
|
||||
|
||||
auto timer = create_wall_timer(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
options.topic_stats_options.publish_period),
|
||||
sub_call_back,
|
||||
options.callback_group,
|
||||
node_topics_interface->get_node_base_interface(),
|
||||
node_timer_interface
|
||||
);
|
||||
|
||||
subscription_topic_stats->set_publisher_timer(timer);
|
||||
}
|
||||
|
||||
auto factory = rclcpp::create_subscription_factory<MessageT>(
|
||||
std::forward<CallbackT>(callback),
|
||||
options,
|
||||
msg_mem_strat,
|
||||
subscription_topic_stats
|
||||
);
|
||||
|
||||
const rclcpp::QoS & actual_qos = options.qos_overriding_options.get_policy_kinds().size() ?
|
||||
rclcpp::detail::declare_qos_parameters(
|
||||
options.qos_overriding_options, node_parameters,
|
||||
node_topics_interface->resolve_topic_name(topic_name),
|
||||
qos, rclcpp::detail::SubscriptionQosParametersTraits{}) :
|
||||
qos;
|
||||
|
||||
auto sub = node_topics_interface->create_subscription(topic_name, factory, actual_qos);
|
||||
node_topics_interface->add_subscription(sub, options.callback_group);
|
||||
|
||||
return std::dynamic_pointer_cast<SubscriptionT>(sub);
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
/// Create and return a subscription of the given MessageT type.
|
||||
/**
|
||||
* The NodeT type only needs to have a method called get_node_topics_interface()
|
||||
@@ -78,7 +177,7 @@ template<
|
||||
typename NodeT>
|
||||
typename std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
NodeT && node,
|
||||
NodeT & node,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
CallbackT && callback,
|
||||
@@ -90,68 +189,45 @@ create_subscription(
|
||||
)
|
||||
)
|
||||
{
|
||||
using rclcpp::node_interfaces::get_node_topics_interface;
|
||||
auto node_topics = get_node_topics_interface(std::forward<NodeT>(node));
|
||||
return rclcpp::detail::create_subscription<
|
||||
MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
node, node, topic_name, qos, std::forward<CallbackT>(callback), options, msg_mem_strat);
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>>
|
||||
subscription_topic_stats = nullptr;
|
||||
|
||||
if (rclcpp::detail::resolve_enable_topic_statistics(
|
||||
options,
|
||||
*node_topics->get_node_base_interface()))
|
||||
{
|
||||
if (options.topic_stats_options.publish_period <= std::chrono::milliseconds(0)) {
|
||||
throw std::invalid_argument(
|
||||
"topic_stats_options.publish_period must be greater than 0, specified value of " +
|
||||
std::to_string(options.topic_stats_options.publish_period.count()) +
|
||||
" ms");
|
||||
}
|
||||
|
||||
std::shared_ptr<Publisher<statistics_msgs::msg::MetricsMessage>> publisher =
|
||||
create_publisher<statistics_msgs::msg::MetricsMessage>(
|
||||
node,
|
||||
options.topic_stats_options.publish_topic,
|
||||
qos);
|
||||
|
||||
subscription_topic_stats = std::make_shared<
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
|
||||
>(node_topics->get_node_base_interface()->get_name(), publisher);
|
||||
|
||||
std::weak_ptr<
|
||||
rclcpp::topic_statistics::SubscriptionTopicStatistics<CallbackMessageT>
|
||||
> weak_subscription_topic_stats(subscription_topic_stats);
|
||||
auto sub_call_back = [weak_subscription_topic_stats]() {
|
||||
auto subscription_topic_stats = weak_subscription_topic_stats.lock();
|
||||
if (subscription_topic_stats) {
|
||||
subscription_topic_stats->publish_message();
|
||||
}
|
||||
};
|
||||
|
||||
auto node_timer_interface = node_topics->get_node_timers_interface();
|
||||
|
||||
auto timer = create_wall_timer(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
options.topic_stats_options.publish_period),
|
||||
sub_call_back,
|
||||
options.callback_group,
|
||||
node_topics->get_node_base_interface(),
|
||||
node_timer_interface
|
||||
);
|
||||
|
||||
subscription_topic_stats->set_publisher_timer(timer);
|
||||
}
|
||||
|
||||
auto factory = rclcpp::create_subscription_factory<MessageT>(
|
||||
std::forward<CallbackT>(callback),
|
||||
options,
|
||||
msg_mem_strat,
|
||||
subscription_topic_stats
|
||||
);
|
||||
|
||||
auto sub = node_topics->create_subscription(topic_name, factory, qos);
|
||||
node_topics->add_subscription(sub, options.callback_group);
|
||||
|
||||
return std::dynamic_pointer_cast<SubscriptionT>(sub);
|
||||
/// Create and return a subscription of the given MessageT type.
|
||||
/**
|
||||
* See \ref create_subscription().
|
||||
*/
|
||||
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 std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr & node_parameters,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr & node_topics,
|
||||
const std::string & topic_name,
|
||||
const rclcpp::QoS & qos,
|
||||
CallbackT && callback,
|
||||
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
|
||||
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
|
||||
),
|
||||
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat = (
|
||||
MessageMemoryStrategyT::create_default()
|
||||
)
|
||||
)
|
||||
{
|
||||
return rclcpp::detail::create_subscription<
|
||||
MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT, MessageMemoryStrategyT>(
|
||||
node_parameters, node_topics, topic_name, qos,
|
||||
std::forward<CallbackT>(callback), options, msg_mem_strat);
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
326
rclcpp/include/rclcpp/detail/qos_parameters.hpp
Normal file
326
rclcpp/include/rclcpp/detail/qos_parameters.hpp
Normal file
@@ -0,0 +1,326 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__DETAIL__QOS_PARAMETERS_HPP_
|
||||
#define RCLCPP__DETAIL__QOS_PARAMETERS_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
|
||||
#include "rcpputils/pointer_traits.hpp"
|
||||
#include "rmw/qos_string_conversions.h"
|
||||
|
||||
#include "rclcpp/duration.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_parameters_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
|
||||
/// \internal Trait used to specialize `declare_qos_parameters()` for publishers.
|
||||
struct PublisherQosParametersTraits
|
||||
{
|
||||
static constexpr const char * entity_type() {return "publisher";}
|
||||
static constexpr auto allowed_policies()
|
||||
{
|
||||
return std::array<::rclcpp::QosPolicyKind, 9> {
|
||||
QosPolicyKind::AvoidRosNamespaceConventions,
|
||||
QosPolicyKind::Deadline,
|
||||
QosPolicyKind::Durability,
|
||||
QosPolicyKind::History,
|
||||
QosPolicyKind::Depth,
|
||||
QosPolicyKind::Lifespan,
|
||||
QosPolicyKind::Liveliness,
|
||||
QosPolicyKind::LivelinessLeaseDuration,
|
||||
QosPolicyKind::Reliability,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// \internal Trait used to specialize `declare_qos_parameters()` for subscriptions.
|
||||
struct SubscriptionQosParametersTraits
|
||||
{
|
||||
static constexpr const char * entity_type() {return "subscription";}
|
||||
static constexpr auto allowed_policies()
|
||||
{
|
||||
return std::array<::rclcpp::QosPolicyKind, 8> {
|
||||
QosPolicyKind::AvoidRosNamespaceConventions,
|
||||
QosPolicyKind::Deadline,
|
||||
QosPolicyKind::Durability,
|
||||
QosPolicyKind::History,
|
||||
QosPolicyKind::Depth,
|
||||
QosPolicyKind::Liveliness,
|
||||
QosPolicyKind::LivelinessLeaseDuration,
|
||||
QosPolicyKind::Reliability,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/// \internal Returns the given `policy` of the profile `qos` converted to a parameter value.
|
||||
inline
|
||||
::rclcpp::ParameterValue
|
||||
get_default_qos_param_value(rclcpp::QosPolicyKind policy, const rclcpp::QoS & qos);
|
||||
|
||||
/// \internal Modify the given `policy` in `qos` to be `value`.
|
||||
inline
|
||||
void
|
||||
apply_qos_override(
|
||||
rclcpp::QosPolicyKind policy, rclcpp::ParameterValue value, rclcpp::QoS & qos);
|
||||
|
||||
inline
|
||||
rclcpp::ParameterValue
|
||||
declare_parameter_or_get(
|
||||
rclcpp::node_interfaces::NodeParametersInterface & parameters_interface,
|
||||
const std::string & param_name,
|
||||
rclcpp::ParameterValue param_value,
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor)
|
||||
{
|
||||
try {
|
||||
return parameters_interface.declare_parameter(
|
||||
param_name, param_value, descriptor);
|
||||
} catch (const rclcpp::exceptions::ParameterAlreadyDeclaredException &) {
|
||||
return parameters_interface.get_parameter(param_name).get_parameter_value();
|
||||
}
|
||||
}
|
||||
|
||||
/// \internal Declare QoS parameters for the given entity.
|
||||
/**
|
||||
* \tparam NodeT Node pointer or reference type.
|
||||
* \tparam EntityQosParametersTraits A class with two static methods: `entity_type()` and
|
||||
* `allowed_policies()`. See `PublisherQosParametersTraits` and `SubscriptionQosParametersTraits`.
|
||||
* \param options User provided options that indicate if QoS parameter overrides should be
|
||||
* declared or not, which policy can have overrides, and optionally a callback to validate the profile.
|
||||
* \param node Parameters will be declared using this node.
|
||||
* \param topic_name Name of the topic of the entity.
|
||||
* \param default_qos User provided qos. It will be used as a default for the parameters declared.
|
||||
* \return qos profile based on the user provided parameter overrides.
|
||||
*/
|
||||
template<typename NodeT, typename EntityQosParametersTraits>
|
||||
std::enable_if_t<
|
||||
rclcpp::node_interfaces::has_node_parameters_interface<
|
||||
decltype(std::declval<typename rcpputils::remove_pointer<NodeT>::type>())>::value ||
|
||||
std::is_same<typename std::decay_t<NodeT>,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr>::value,
|
||||
rclcpp::QoS>
|
||||
declare_qos_parameters(
|
||||
const ::rclcpp::QosOverridingOptions & options,
|
||||
NodeT & node,
|
||||
const std::string & topic_name,
|
||||
const ::rclcpp::QoS & default_qos,
|
||||
EntityQosParametersTraits)
|
||||
{
|
||||
auto & parameters_interface = *rclcpp::node_interfaces::get_node_parameters_interface(node);
|
||||
std::string param_prefix;
|
||||
const auto & id = options.get_id();
|
||||
{
|
||||
std::ostringstream oss{"qos_overrides.", std::ios::ate};
|
||||
oss << topic_name << "." << EntityQosParametersTraits::entity_type();
|
||||
if (!id.empty()) {
|
||||
oss << "_" << id;
|
||||
}
|
||||
oss << ".";
|
||||
param_prefix = oss.str();
|
||||
}
|
||||
std::string param_description_suffix;
|
||||
{
|
||||
std::ostringstream oss{"} for ", std::ios::ate};
|
||||
oss << EntityQosParametersTraits::entity_type() << " {" << topic_name << "}";
|
||||
if (!id.empty()) {
|
||||
oss << " with id {" << id << "}";
|
||||
}
|
||||
param_description_suffix = oss.str();
|
||||
}
|
||||
rclcpp::QoS qos = default_qos;
|
||||
for (auto policy : EntityQosParametersTraits::allowed_policies()) {
|
||||
if (
|
||||
std::count(options.get_policy_kinds().begin(), options.get_policy_kinds().end(), policy))
|
||||
{
|
||||
std::ostringstream param_name{param_prefix, std::ios::ate};
|
||||
param_name << qos_policy_kind_to_cstr(policy);
|
||||
std::ostringstream param_desciption{"qos policy {", std::ios::ate};
|
||||
param_desciption << qos_policy_kind_to_cstr(policy) << param_description_suffix;
|
||||
rcl_interfaces::msg::ParameterDescriptor descriptor{};
|
||||
descriptor.description = param_desciption.str();
|
||||
descriptor.read_only = true;
|
||||
auto value = declare_parameter_or_get(
|
||||
parameters_interface, param_name.str(),
|
||||
get_default_qos_param_value(policy, qos), descriptor);
|
||||
::rclcpp::detail::apply_qos_override(policy, value, qos);
|
||||
}
|
||||
}
|
||||
const auto & validation_callback = options.get_validation_callback();
|
||||
if (validation_callback) {
|
||||
auto result = validation_callback(qos);
|
||||
if (!result.successful) {
|
||||
throw rclcpp::exceptions::InvalidQosOverridesException{
|
||||
"validation callback failed: " + result.reason};
|
||||
}
|
||||
}
|
||||
return qos;
|
||||
}
|
||||
|
||||
// TODO(ivanpauno): This overload cannot declare the QoS parameters, as a node parameters interface
|
||||
// was not provided.
|
||||
template<typename NodeT, typename EntityQosParametersTraits>
|
||||
std::enable_if_t<
|
||||
!(rclcpp::node_interfaces::has_node_parameters_interface<
|
||||
decltype(std::declval<typename rcpputils::remove_pointer<NodeT>::type>())>::value ||
|
||||
std::is_same<typename std::decay_t<NodeT>,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr>::value),
|
||||
rclcpp::QoS>
|
||||
declare_qos_parameters(
|
||||
const ::rclcpp::QosOverridingOptions & options,
|
||||
NodeT &,
|
||||
const std::string &,
|
||||
const ::rclcpp::QoS & default_qos,
|
||||
EntityQosParametersTraits)
|
||||
{
|
||||
if (options.get_policy_kinds().size()) {
|
||||
std::runtime_error exc{
|
||||
"passed non-default qos overriding options without providing a parameters interface"};
|
||||
throw exc;
|
||||
}
|
||||
return default_qos;
|
||||
}
|
||||
|
||||
/// \internal Helper function to get a rmw qos policy value from a string.
|
||||
#define RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING( \
|
||||
kind_lower, kind_upper, parameter_value, rclcpp_qos) \
|
||||
do { \
|
||||
auto policy_string = (parameter_value).get<std::string>(); \
|
||||
auto policy_value = rmw_qos_ ## kind_lower ## _policy_from_str(policy_string.c_str()); \
|
||||
if (RMW_QOS_POLICY_ ## kind_upper ## _UNKNOWN == policy_value) { \
|
||||
throw std::invalid_argument{"unknown QoS policy " #kind_lower " value: " + policy_string}; \
|
||||
} \
|
||||
((rclcpp_qos).kind_lower)(policy_value); \
|
||||
} while (0)
|
||||
|
||||
inline
|
||||
void
|
||||
apply_qos_override(
|
||||
rclcpp::QosPolicyKind policy, rclcpp::ParameterValue value, rclcpp::QoS & qos)
|
||||
{
|
||||
switch (policy) {
|
||||
case QosPolicyKind::AvoidRosNamespaceConventions:
|
||||
qos.avoid_ros_namespace_conventions(value.get<bool>());
|
||||
break;
|
||||
case QosPolicyKind::Deadline:
|
||||
qos.deadline(::rclcpp::Duration::from_nanoseconds(value.get<int64_t>()));
|
||||
break;
|
||||
case QosPolicyKind::Durability:
|
||||
RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING(
|
||||
durability, DURABILITY, value, qos);
|
||||
break;
|
||||
case QosPolicyKind::History:
|
||||
RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING(
|
||||
history, HISTORY, value, qos);
|
||||
break;
|
||||
case QosPolicyKind::Depth:
|
||||
qos.get_rmw_qos_profile().depth = static_cast<size_t>(value.get<int64_t>());
|
||||
break;
|
||||
case QosPolicyKind::Lifespan:
|
||||
qos.lifespan(::rclcpp::Duration::from_nanoseconds(value.get<int64_t>()));
|
||||
break;
|
||||
case QosPolicyKind::Liveliness:
|
||||
RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING(
|
||||
liveliness, LIVELINESS, value, qos);
|
||||
break;
|
||||
case QosPolicyKind::LivelinessLeaseDuration:
|
||||
qos.liveliness_lease_duration(::rclcpp::Duration::from_nanoseconds(value.get<int64_t>()));
|
||||
break;
|
||||
case QosPolicyKind::Reliability:
|
||||
RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING(
|
||||
reliability, RELIABILITY, value, qos);
|
||||
break;
|
||||
default:
|
||||
throw std::invalid_argument{"unknown QosPolicyKind"};
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert `rmw_time_t` to `int64_t` that can be used as a parameter value.
|
||||
inline
|
||||
int64_t
|
||||
rmw_duration_to_int64_t(rmw_time_t rmw_duration)
|
||||
{
|
||||
return ::rclcpp::Duration(
|
||||
static_cast<int32_t>(rmw_duration.sec),
|
||||
static_cast<uint32_t>(rmw_duration.nsec)
|
||||
).nanoseconds();
|
||||
}
|
||||
|
||||
/// \internal Throw an exception if `policy_value_stringified` is NULL.
|
||||
inline
|
||||
const char *
|
||||
check_if_stringified_policy_is_null(const char * policy_value_stringified, QosPolicyKind kind)
|
||||
{
|
||||
if (!policy_value_stringified) {
|
||||
std::ostringstream oss{"unknown value for policy kind {", std::ios::ate};
|
||||
oss << kind << "}";
|
||||
throw std::invalid_argument{oss.str()};
|
||||
}
|
||||
return policy_value_stringified;
|
||||
}
|
||||
|
||||
inline
|
||||
::rclcpp::ParameterValue
|
||||
get_default_qos_param_value(rclcpp::QosPolicyKind kind, const rclcpp::QoS & qos)
|
||||
{
|
||||
using ParameterValue = ::rclcpp::ParameterValue;
|
||||
const auto & rmw_qos = qos.get_rmw_qos_profile();
|
||||
switch (kind) {
|
||||
case QosPolicyKind::AvoidRosNamespaceConventions:
|
||||
return ParameterValue(rmw_qos.avoid_ros_namespace_conventions);
|
||||
case QosPolicyKind::Deadline:
|
||||
return ParameterValue(rmw_duration_to_int64_t(rmw_qos.deadline));
|
||||
case QosPolicyKind::Durability:
|
||||
return ParameterValue(
|
||||
check_if_stringified_policy_is_null(
|
||||
rmw_qos_durability_policy_to_str(rmw_qos.durability), kind));
|
||||
case QosPolicyKind::History:
|
||||
return ParameterValue(
|
||||
check_if_stringified_policy_is_null(
|
||||
rmw_qos_history_policy_to_str(rmw_qos.history), kind));
|
||||
case QosPolicyKind::Depth:
|
||||
return ParameterValue(static_cast<int64_t>(rmw_qos.depth));
|
||||
case QosPolicyKind::Lifespan:
|
||||
return ParameterValue(rmw_duration_to_int64_t(rmw_qos.lifespan));
|
||||
case QosPolicyKind::Liveliness:
|
||||
return ParameterValue(
|
||||
check_if_stringified_policy_is_null(
|
||||
rmw_qos_liveliness_policy_to_str(rmw_qos.liveliness), kind));
|
||||
case QosPolicyKind::LivelinessLeaseDuration:
|
||||
return ParameterValue(rmw_duration_to_int64_t(rmw_qos.liveliness_lease_duration));
|
||||
case QosPolicyKind::Reliability:
|
||||
return ParameterValue(
|
||||
check_if_stringified_policy_is_null(
|
||||
rmw_qos_reliability_policy_to_str(rmw_qos.reliability), kind));
|
||||
default:
|
||||
throw std::invalid_argument{"unknown QoS policy kind"};
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__DETAIL__QOS_PARAMETERS_HPP_
|
||||
@@ -38,10 +38,14 @@ public:
|
||||
*/
|
||||
Duration(int32_t seconds, uint32_t nanoseconds);
|
||||
|
||||
// This constructor matches any numeric value - ints or floats.
|
||||
/// Construct duration from the specified nanoseconds.
|
||||
[[deprecated(
|
||||
"Use Duration::from_nanoseconds instead or std::chrono_literals. For example:"
|
||||
"rclcpp::Duration::from_nanoseconds(int64_variable);"
|
||||
"rclcpp::Duration(0ns);")]]
|
||||
explicit Duration(rcl_duration_value_t nanoseconds);
|
||||
|
||||
// This constructor matches std::chrono::nanoseconds.
|
||||
/// Construct duration from the specified std::chrono::nanoseconds.
|
||||
explicit Duration(std::chrono::nanoseconds nanoseconds);
|
||||
|
||||
// This constructor matches any std::chrono value other than nanoseconds
|
||||
@@ -129,6 +133,13 @@ public:
|
||||
static Duration
|
||||
from_seconds(double seconds);
|
||||
|
||||
/// Create a duration object from an integer number representing nanoseconds
|
||||
static Duration
|
||||
from_nanoseconds(rcl_duration_value_t nanoseconds);
|
||||
|
||||
static Duration
|
||||
from_rmw_time(rmw_time_t duration);
|
||||
|
||||
/// Convert Duration into a std::chrono::Duration.
|
||||
template<class DurationT>
|
||||
DurationT
|
||||
@@ -143,6 +154,8 @@ public:
|
||||
|
||||
private:
|
||||
rcl_duration_t rcl_duration_;
|
||||
|
||||
Duration() = default;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -282,6 +282,13 @@ class ParameterModifiedInCallbackException : public std::runtime_error
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
/// Thrown if the QoS overrides provided aren't valid.
|
||||
class InvalidQosOverridesException : public std::runtime_error
|
||||
{
|
||||
// Inherit constructors from runtime_error.
|
||||
using std::runtime_error::runtime_error;
|
||||
};
|
||||
|
||||
} // namespace exceptions
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "rcl/wait.h"
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/executor_options.hpp"
|
||||
#include "rclcpp/future_return_code.hpp"
|
||||
#include "rclcpp/memory_strategies.hpp"
|
||||
@@ -525,6 +526,8 @@ protected:
|
||||
/// Guard condition for signaling the rmw layer to wake up for special events.
|
||||
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
|
||||
|
||||
std::shared_ptr<rclcpp::GuardCondition> shutdown_guard_condition_;
|
||||
|
||||
/// Wait set for managing entities that the rmw layer waits on.
|
||||
rcl_wait_set_t wait_set_ = rcl_get_zero_initialized_wait_set();
|
||||
|
||||
|
||||
@@ -72,9 +72,19 @@ public:
|
||||
void
|
||||
fini();
|
||||
|
||||
/// Execute the waitable.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
execute() override;
|
||||
execute(std::shared_ptr<void> & data) override;
|
||||
|
||||
/// Take the data so that it can be consumed with `execute`.
|
||||
/**
|
||||
* For `StaticExecutorEntitiesCollector`, this always return `nullptr`.
|
||||
* \sa rclcpp::Waitable::take_data()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void>
|
||||
take_data() override;
|
||||
|
||||
/// Function to add_handles_to_wait_set and wait for work and
|
||||
/**
|
||||
|
||||
@@ -207,7 +207,7 @@ public:
|
||||
sub_ids.take_shared_subscriptions.size() <= 1)
|
||||
{
|
||||
// There is at maximum 1 buffer that does not require ownership.
|
||||
// So we this case is equivalent to all the buffers requiring ownership
|
||||
// So this case is equivalent to all the buffers requiring ownership
|
||||
|
||||
// Merge the two vector of ids into a unique one
|
||||
std::vector<uint64_t> concatenated_vector(sub_ids.take_shared_subscriptions);
|
||||
|
||||
@@ -18,7 +18,9 @@
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <functional>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -115,13 +117,31 @@ public:
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
(void)wait_set;
|
||||
(void) wait_set;
|
||||
return buffer_->has_data();
|
||||
}
|
||||
|
||||
void execute()
|
||||
std::shared_ptr<void>
|
||||
take_data()
|
||||
{
|
||||
execute_impl<CallbackMessageT>();
|
||||
ConstMessageSharedPtr shared_msg;
|
||||
MessageUniquePtr unique_msg;
|
||||
|
||||
if (any_callback_.use_take_shared_method()) {
|
||||
shared_msg = buffer_->consume_shared();
|
||||
} else {
|
||||
unique_msg = buffer_->consume_unique();
|
||||
}
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
|
||||
std::pair<ConstMessageSharedPtr, MessageUniquePtr>(
|
||||
shared_msg, std::move(unique_msg)))
|
||||
);
|
||||
}
|
||||
|
||||
void execute(std::shared_ptr<void> & data)
|
||||
{
|
||||
execute_impl<CallbackMessageT>(data);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -154,26 +174,35 @@ private:
|
||||
|
||||
template<typename T>
|
||||
typename std::enable_if<std::is_same<T, rcl_serialized_message_t>::value, void>::type
|
||||
execute_impl()
|
||||
execute_impl(std::shared_ptr<void> & data)
|
||||
{
|
||||
(void)data;
|
||||
throw std::runtime_error("Subscription intra-process can't handle serialized messages");
|
||||
}
|
||||
|
||||
template<class T>
|
||||
typename std::enable_if<!std::is_same<T, rcl_serialized_message_t>::value, void>::type
|
||||
execute_impl()
|
||||
execute_impl(std::shared_ptr<void> & data)
|
||||
{
|
||||
if (!data) {
|
||||
throw std::runtime_error("'data' is empty");
|
||||
}
|
||||
|
||||
rmw_message_info_t msg_info;
|
||||
msg_info.publisher_gid = {0, {0}};
|
||||
msg_info.from_intra_process = true;
|
||||
|
||||
auto shared_ptr = std::static_pointer_cast<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
|
||||
data);
|
||||
|
||||
if (any_callback_.use_take_shared_method()) {
|
||||
ConstMessageSharedPtr msg = buffer_->consume_shared();
|
||||
any_callback_.dispatch_intra_process(msg, msg_info);
|
||||
ConstMessageSharedPtr shared_msg = shared_ptr->first;
|
||||
any_callback_.dispatch_intra_process(shared_msg, msg_info);
|
||||
} else {
|
||||
MessageUniquePtr msg = buffer_->consume_unique();
|
||||
any_callback_.dispatch_intra_process(std::move(msg), msg_info);
|
||||
MessageUniquePtr unique_msg = std::move(shared_ptr->second);
|
||||
any_callback_.dispatch_intra_process(std::move(unique_msg), msg_info);
|
||||
}
|
||||
shared_ptr.reset();
|
||||
}
|
||||
|
||||
AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
|
||||
|
||||
@@ -56,8 +56,12 @@ public:
|
||||
virtual bool
|
||||
is_ready(rcl_wait_set_t * wait_set) = 0;
|
||||
|
||||
virtual
|
||||
std::shared_ptr<void>
|
||||
take_data() = 0;
|
||||
|
||||
virtual void
|
||||
execute() = 0;
|
||||
execute(std::shared_ptr<void> & data) = 0;
|
||||
|
||||
virtual bool
|
||||
use_take_shared_method() const = 0;
|
||||
|
||||
@@ -63,7 +63,7 @@ class GraphListener : public std::enable_shared_from_this<GraphListener>
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
explicit GraphListener(std::shared_ptr<rclcpp::Context> parent_context);
|
||||
explicit GraphListener(const rclcpp::Context::SharedPtr & parent_context);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~GraphListener();
|
||||
@@ -160,14 +160,23 @@ protected:
|
||||
void
|
||||
run_loop();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init_wait_set();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
cleanup_wait_set();
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(GraphListener)
|
||||
|
||||
/** \internal */
|
||||
void
|
||||
__shutdown(bool);
|
||||
__shutdown();
|
||||
|
||||
rclcpp::Context::WeakPtr parent_context_;
|
||||
std::weak_ptr<rclcpp::Context> weak_parent_context_;
|
||||
std::shared_ptr<rcl_context_t> rcl_parent_context_;
|
||||
|
||||
std::thread listener_thread_;
|
||||
bool is_started_;
|
||||
@@ -179,7 +188,6 @@ private:
|
||||
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> node_graph_interfaces_;
|
||||
|
||||
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
|
||||
rcl_guard_condition_t * shutdown_guard_condition_;
|
||||
rcl_wait_set_t wait_set_ = rcl_get_zero_initialized_wait_set();
|
||||
};
|
||||
|
||||
|
||||
@@ -118,6 +118,10 @@ public:
|
||||
bool
|
||||
get_enable_topic_statistics_default() const override;
|
||||
|
||||
std::string
|
||||
resolve_topic_or_service_name(
|
||||
const std::string & name, bool is_service, bool only_expand = false) const override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeBase)
|
||||
|
||||
|
||||
@@ -163,6 +163,13 @@ public:
|
||||
virtual
|
||||
bool
|
||||
get_enable_topic_statistics_default() const = 0;
|
||||
|
||||
/// Expand and remap a given topic or service name.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::string
|
||||
resolve_topic_or_service_name(
|
||||
const std::string & name, bool is_service, bool only_expand = false) const = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#ifndef RCLCPP__NODE_INTERFACES__NODE_SERVICES_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_SERVICES_HPP_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
@@ -53,6 +55,10 @@ public:
|
||||
rclcpp::ServiceBase::SharedPtr service_base_ptr,
|
||||
rclcpp::CallbackGroup::SharedPtr group) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
resolve_service_name(const std::string & name, bool only_expand = false) const override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeServices)
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
#ifndef RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
@@ -49,6 +51,12 @@ public:
|
||||
add_service(
|
||||
rclcpp::ServiceBase::SharedPtr service_base_ptr,
|
||||
rclcpp::CallbackGroup::SharedPtr group) = 0;
|
||||
|
||||
/// Get the remapped and expanded service name given a input name.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::string
|
||||
resolve_service_name(const std::string & name, bool only_expand = false) const = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -81,6 +81,10 @@ public:
|
||||
rclcpp::node_interfaces::NodeTimersInterface *
|
||||
get_node_timers_interface() const override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
resolve_topic_name(const std::string & name, bool only_expand = false) const override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeTopics)
|
||||
|
||||
|
||||
@@ -86,6 +86,12 @@ public:
|
||||
virtual
|
||||
rclcpp::node_interfaces::NodeTimersInterface *
|
||||
get_node_timers_interface() const = 0;
|
||||
|
||||
/// Get a remapped and expanded topic name given an input name.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::string
|
||||
resolve_topic_name(const std::string & name, bool only_expand = false) const = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "rclcpp/intra_process_setting.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -50,6 +51,8 @@ struct PublisherOptionsBase
|
||||
/// Optional RMW implementation specific payload to be used during creation of the publisher.
|
||||
std::shared_ptr<rclcpp::detail::RMWImplementationSpecificPublisherPayload>
|
||||
rmw_implementation_payload = nullptr;
|
||||
|
||||
QosOverridingOptions qos_overriding_options;
|
||||
};
|
||||
|
||||
/// Structure containing optional configuration for Publishers.
|
||||
|
||||
@@ -30,6 +30,38 @@ namespace rclcpp
|
||||
RCLCPP_PUBLIC
|
||||
std::string qos_policy_name_from_kind(rmw_qos_policy_kind_t policy_kind);
|
||||
|
||||
enum class HistoryPolicy
|
||||
{
|
||||
KeepLast = RMW_QOS_POLICY_HISTORY_KEEP_LAST,
|
||||
KeepAll = RMW_QOS_POLICY_HISTORY_KEEP_ALL,
|
||||
SystemDefault = RMW_QOS_POLICY_HISTORY_SYSTEM_DEFAULT,
|
||||
Unknown = RMW_QOS_POLICY_HISTORY_UNKNOWN,
|
||||
};
|
||||
|
||||
enum class ReliabilityPolicy
|
||||
{
|
||||
BestEffort = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
|
||||
Reliable = RMW_QOS_POLICY_RELIABILITY_RELIABLE,
|
||||
SystemDefault = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT,
|
||||
Unknown = RMW_QOS_POLICY_RELIABILITY_UNKNOWN,
|
||||
};
|
||||
|
||||
enum class DurabilityPolicy
|
||||
{
|
||||
Volatile = RMW_QOS_POLICY_DURABILITY_VOLATILE,
|
||||
TransientLocal = RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
|
||||
SystemDefault = RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT,
|
||||
Unknown = RMW_QOS_POLICY_DURABILITY_UNKNOWN,
|
||||
};
|
||||
|
||||
enum class LivelinessPolicy
|
||||
{
|
||||
Automatic = RMW_QOS_POLICY_LIVELINESS_AUTOMATIC,
|
||||
ManualByTopic = RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC,
|
||||
SystemDefault = RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT,
|
||||
Unknown = RMW_QOS_POLICY_LIVELINESS_UNKNOWN,
|
||||
};
|
||||
|
||||
/// QoS initialization values, cannot be created directly, use KeepAll or KeepLast instead.
|
||||
struct RCLCPP_PUBLIC QoSInitialization
|
||||
{
|
||||
@@ -82,6 +114,10 @@ public:
|
||||
const rmw_qos_profile_t &
|
||||
get_rmw_qos_profile() const;
|
||||
|
||||
/// Set the history policy.
|
||||
QoS &
|
||||
history(HistoryPolicy history);
|
||||
|
||||
/// Set the history policy.
|
||||
QoS &
|
||||
history(rmw_qos_history_policy_t history);
|
||||
@@ -98,6 +134,10 @@ public:
|
||||
QoS &
|
||||
reliability(rmw_qos_reliability_policy_t reliability);
|
||||
|
||||
/// Set the reliability setting.
|
||||
QoS &
|
||||
reliability(ReliabilityPolicy reliability);
|
||||
|
||||
/// Set the reliability setting to reliable.
|
||||
QoS &
|
||||
reliable();
|
||||
@@ -110,6 +150,10 @@ public:
|
||||
QoS &
|
||||
durability(rmw_qos_durability_policy_t durability);
|
||||
|
||||
/// Set the durability setting.
|
||||
QoS &
|
||||
durability(DurabilityPolicy durability);
|
||||
|
||||
/// Set the durability setting to volatile.
|
||||
/**
|
||||
* Note that this cannot be named `volatile` because it is a C++ keyword.
|
||||
@@ -141,6 +185,10 @@ public:
|
||||
QoS &
|
||||
liveliness(rmw_qos_liveliness_policy_t liveliness);
|
||||
|
||||
/// Set the liveliness setting.
|
||||
QoS &
|
||||
liveliness(LivelinessPolicy liveliness);
|
||||
|
||||
/// Set the liveliness_lease_duration setting.
|
||||
QoS &
|
||||
liveliness_lease_duration(rmw_time_t liveliness_lease_duration);
|
||||
@@ -153,6 +201,42 @@ public:
|
||||
QoS &
|
||||
avoid_ros_namespace_conventions(bool avoid_ros_namespace_conventions);
|
||||
|
||||
/// Get the history qos policy.
|
||||
HistoryPolicy
|
||||
history() const;
|
||||
|
||||
/// Get the history depth.
|
||||
size_t
|
||||
depth() const;
|
||||
|
||||
/// Get the reliability policy.
|
||||
ReliabilityPolicy
|
||||
reliability() const;
|
||||
|
||||
/// Get the durability policy.
|
||||
DurabilityPolicy
|
||||
durability() const;
|
||||
|
||||
/// Get the deadline duration setting.
|
||||
rclcpp::Duration
|
||||
deadline() const;
|
||||
|
||||
/// Get the lifespan duration setting.
|
||||
rclcpp::Duration
|
||||
lifespan() const;
|
||||
|
||||
/// Get the liveliness policy.
|
||||
LivelinessPolicy
|
||||
liveliness() const;
|
||||
|
||||
/// Get the liveliness lease duration setting.
|
||||
rclcpp::Duration
|
||||
liveliness_lease_duration() const;
|
||||
|
||||
/// Get the `avoid ros namespace convention` setting.
|
||||
bool
|
||||
avoid_ros_namespace_conventions() const;
|
||||
|
||||
private:
|
||||
rmw_qos_profile_t rmw_qos_profile_;
|
||||
};
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#define RCLCPP__QOS_EVENT_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
@@ -131,21 +133,31 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Execute any entities of the Waitable that are ready.
|
||||
void
|
||||
execute() override
|
||||
/// Take data so that the callback cannot be scheduled again
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
EventCallbackInfoT callback_info;
|
||||
|
||||
rcl_ret_t ret = rcl_take_event(&event_handle_, &callback_info);
|
||||
if (ret != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't take event info: %s", rcl_get_error_string().str);
|
||||
return;
|
||||
return nullptr;
|
||||
}
|
||||
return std::static_pointer_cast<void>(std::make_shared<EventCallbackInfoT>(callback_info));
|
||||
}
|
||||
|
||||
event_callback_(callback_info);
|
||||
/// Execute any entities of the Waitable that are ready.
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override
|
||||
{
|
||||
if (!data) {
|
||||
throw std::runtime_error("'data' is empty");
|
||||
}
|
||||
auto callback_ptr = std::static_pointer_cast<EventCallbackInfoT>(data);
|
||||
event_callback_(*callback_ptr);
|
||||
callback_ptr.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
154
rclcpp/include/rclcpp/qos_overriding_options.hpp
Normal file
154
rclcpp/include/rclcpp/qos_overriding_options.hpp
Normal file
@@ -0,0 +1,154 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__QOS_OVERRIDING_OPTIONS_HPP_
|
||||
#define RCLCPP__QOS_OVERRIDING_OPTIONS_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rcl_interfaces/msg/set_parameters_result.hpp"
|
||||
#include "rmw/qos_policy_kind.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
enum class RCLCPP_PUBLIC_TYPE QosPolicyKind
|
||||
{
|
||||
AvoidRosNamespaceConventions = RMW_QOS_POLICY_AVOID_ROS_NAMESPACE_CONVENTIONS,
|
||||
Deadline = RMW_QOS_POLICY_DEADLINE,
|
||||
Depth = RMW_QOS_POLICY_DEPTH,
|
||||
Durability = RMW_QOS_POLICY_DURABILITY,
|
||||
History = RMW_QOS_POLICY_HISTORY,
|
||||
Lifespan = RMW_QOS_POLICY_LIFESPAN,
|
||||
Liveliness = RMW_QOS_POLICY_LIVELINESS,
|
||||
LivelinessLeaseDuration = RMW_QOS_POLICY_LIVELINESS_LEASE_DURATION,
|
||||
Reliability = RMW_QOS_POLICY_RELIABILITY,
|
||||
Invalid = RMW_QOS_POLICY_INVALID,
|
||||
};
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
qos_policy_kind_to_cstr(const QosPolicyKind & qpk);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::ostream &
|
||||
operator<<(std::ostream & os, const QosPolicyKind & qpk);
|
||||
|
||||
using QosCallbackResult = rcl_interfaces::msg::SetParametersResult;
|
||||
using QosCallback = std::function<QosCallbackResult(const rclcpp::QoS &)>;
|
||||
|
||||
namespace detail
|
||||
{
|
||||
// forward declare
|
||||
template<typename T>
|
||||
class QosParameters;
|
||||
}
|
||||
|
||||
/// Options that are passed in subscription/publisher constructor to specify QoSConfigurability.
|
||||
/**
|
||||
* This options struct allows configuring:
|
||||
* - Which policy kinds will have declared parameters.
|
||||
* - An optional callback, that will be called to validate the final qos profile.
|
||||
* - An optional id. In the case that different qos are desired for two publishers/subscriptions in
|
||||
* the same topic, this id will allow disambiguating them.
|
||||
*
|
||||
* Example parameter file:
|
||||
*
|
||||
* ```yaml
|
||||
* my_node_name:
|
||||
* ros__parameters:
|
||||
* qos_overrides:
|
||||
* /my/topic/name:
|
||||
* publisher: # publisher without provided id
|
||||
* reliability: reliable
|
||||
* depth: 100
|
||||
* publisher_my_id: # publisher with `id="my_id"
|
||||
* reliability: reliable
|
||||
* depth: 10
|
||||
* ```
|
||||
*/
|
||||
class QosOverridingOptions
|
||||
{
|
||||
public:
|
||||
/// Default constructor, no overrides allowed.
|
||||
RCLCPP_PUBLIC
|
||||
QosOverridingOptions() = default;
|
||||
|
||||
/// Construct passing a list of QoS policies and a verification callback.
|
||||
/**
|
||||
* This constructor is implicit, e.g.:
|
||||
* ```cpp
|
||||
* node->create_publisher(
|
||||
* "topic_name",
|
||||
* default_qos_profile,
|
||||
* {
|
||||
* {QosPolicyKind::Reliability},
|
||||
* [] (auto && qos) {return check_qos_validity(qos)},
|
||||
* "my_id"
|
||||
* });
|
||||
* ```
|
||||
* \param policy_kinds list of policy kinds that will be reconfigurable.
|
||||
* \param validation_callback callbak that will be called to validate the validity of
|
||||
* the qos profile set by the user.
|
||||
* \param id id of the entity.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
QosOverridingOptions(
|
||||
std::initializer_list<QosPolicyKind> policy_kinds,
|
||||
QosCallback validation_callback = nullptr,
|
||||
std::string id = {});
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::string &
|
||||
get_id() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<QosPolicyKind> &
|
||||
get_policy_kinds() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const QosCallback &
|
||||
get_validation_callback() const;
|
||||
|
||||
/// Construct passing a list of QoS policies and a verification callback.
|
||||
/**
|
||||
* Same as `QosOverridingOptions` constructor, but only declares the default policies:
|
||||
*
|
||||
* History, Depth, Reliability.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
static
|
||||
QosOverridingOptions
|
||||
with_default_policies(QosCallback validation_callback = nullptr, std::string id = {});
|
||||
|
||||
private:
|
||||
/// \internal Id of the entity requesting to create parameters.
|
||||
std::string id_;
|
||||
/// \internal Policy kinds that are allowed to be reconfigured.
|
||||
std::vector<QosPolicyKind> policy_kinds_;
|
||||
/// \internal Validation callback that will be called to verify the profile.
|
||||
QosCallback validation_callback_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__QOS_OVERRIDING_OPTIONS_HPP_
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "rclcpp/intra_process_setting.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
#include "rclcpp/qos_event.hpp"
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
#include "rclcpp/topic_statistics_state.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -72,6 +73,8 @@ struct SubscriptionOptionsBase
|
||||
};
|
||||
|
||||
TopicStatisticsOptions topic_stats_options;
|
||||
|
||||
QosOverridingOptions qos_overriding_options;
|
||||
};
|
||||
|
||||
/// Structure containing optional configuration for Subscriptions.
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define RCLCPP__WAITABLE_HPP_
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -125,8 +126,17 @@ public:
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set) = 0;
|
||||
|
||||
/// Execute any entities of the Waitable that are ready.
|
||||
/// Take the data so that it can be consumed with `execute`.
|
||||
/**
|
||||
* NOTE: take_data is a partial fix to a larger design issue with the
|
||||
* multithreaded executor. This method is likely to be removed when
|
||||
* a more permanent fix is implemented. A longterm fix is currently
|
||||
* being discussed here: https://github.com/ros2/rclcpp/pull/1276
|
||||
*
|
||||
* This method takes the data from the underlying data structure and
|
||||
* writes it to the void shared pointer `data` that is passed into the
|
||||
* method. The `data` can then be executed with the `execute` method.
|
||||
*
|
||||
* Before calling this method, the Waitable should be added to a wait set,
|
||||
* waited on, and then updated.
|
||||
*
|
||||
@@ -143,13 +153,41 @@ public:
|
||||
* // Update the Waitable
|
||||
* waitable.update(wait_set);
|
||||
* // Execute any entities of the Waitable that may be ready
|
||||
* waitable.execute();
|
||||
* std::shared_ptr<void> data = waitable.take_data();
|
||||
* ```
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::shared_ptr<void>
|
||||
take_data() = 0;
|
||||
|
||||
/// Execute data that is passed in.
|
||||
/**
|
||||
* Before calling this method, the Waitable should be added to a wait set,
|
||||
* waited on, and then updated - and the `take_data` method should be
|
||||
* called.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* ```cpp
|
||||
* // ... create a wait set and a Waitable
|
||||
* // Add the Waitable to the wait set
|
||||
* bool add_ret = waitable.add_to_wait_set(wait_set);
|
||||
* // ... error handling
|
||||
* // Wait
|
||||
* rcl_ret_t wait_ret = rcl_wait(wait_set);
|
||||
* // ... error handling
|
||||
* // Update the Waitable
|
||||
* waitable.update(wait_set);
|
||||
* // Execute any entities of the Waitable that may be ready
|
||||
* std::shared_ptr<void> data = waitable.take_data();
|
||||
* waitable.execute(data);
|
||||
* ```
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
execute() = 0;
|
||||
execute(std::shared_ptr<void> & data) = 0;
|
||||
|
||||
/// Exchange the "in use by wait set" state for this timer.
|
||||
/**
|
||||
|
||||
@@ -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>5.0.0</version>
|
||||
<version>6.1.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>
|
||||
|
||||
@@ -320,7 +320,6 @@ Context::shutdown(const std::string & reason)
|
||||
}
|
||||
// interrupt all blocking sleep_for() and all blocking executors or wait sets
|
||||
this->interrupt_all_sleep_for();
|
||||
this->interrupt_all_wait_sets();
|
||||
// remove self from the global contexts
|
||||
weak_contexts_->remove_context(this);
|
||||
// shutdown logger
|
||||
@@ -391,75 +390,6 @@ Context::interrupt_all_sleep_for()
|
||||
interrupt_condition_variable_.notify_all();
|
||||
}
|
||||
|
||||
rcl_guard_condition_t *
|
||||
Context::get_interrupt_guard_condition(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(interrupt_guard_cond_handles_mutex_);
|
||||
auto kv = interrupt_guard_cond_handles_.find(wait_set);
|
||||
if (kv != interrupt_guard_cond_handles_.end()) {
|
||||
return &kv->second;
|
||||
} else {
|
||||
rcl_guard_condition_t handle = rcl_get_zero_initialized_guard_condition();
|
||||
rcl_guard_condition_options_t options = rcl_guard_condition_get_default_options();
|
||||
auto ret = rcl_guard_condition_init(&handle, this->get_rcl_context().get(), options);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't initialize guard condition");
|
||||
}
|
||||
interrupt_guard_cond_handles_.emplace(wait_set, handle);
|
||||
return &interrupt_guard_cond_handles_[wait_set];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::release_interrupt_guard_condition(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(interrupt_guard_cond_handles_mutex_);
|
||||
auto kv = interrupt_guard_cond_handles_.find(wait_set);
|
||||
if (kv != interrupt_guard_cond_handles_.end()) {
|
||||
rcl_ret_t ret = rcl_guard_condition_fini(&kv->second);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to destroy sigint guard condition");
|
||||
}
|
||||
interrupt_guard_cond_handles_.erase(kv);
|
||||
} else {
|
||||
throw std::runtime_error("Tried to release sigint guard condition for nonexistent wait set");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::release_interrupt_guard_condition(
|
||||
rcl_wait_set_t * wait_set,
|
||||
const std::nothrow_t &) noexcept
|
||||
{
|
||||
try {
|
||||
this->release_interrupt_guard_condition(wait_set);
|
||||
} catch (const std::exception & exc) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"caught %s exception when releasing interrupt guard condition: %s",
|
||||
rmw::impl::cpp::demangle(exc).c_str(), exc.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"caught unknown exception when releasing interrupt guard condition");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::interrupt_all_wait_sets()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(interrupt_guard_cond_handles_mutex_);
|
||||
for (auto & kv : interrupt_guard_cond_handles_) {
|
||||
rcl_ret_t status = rcl_trigger_guard_condition(&(kv.second));
|
||||
if (status != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"failed to trigger guard condition in Context::interrupt_all_wait_sets(): %s",
|
||||
rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::clean_up()
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ Duration::Duration(int32_t seconds, uint32_t nanoseconds)
|
||||
rcl_duration_.nanoseconds += nanoseconds;
|
||||
}
|
||||
|
||||
Duration::Duration(int64_t nanoseconds)
|
||||
Duration::Duration(rcl_duration_value_t nanoseconds)
|
||||
{
|
||||
rcl_duration_.nanoseconds = nanoseconds;
|
||||
}
|
||||
@@ -148,7 +148,7 @@ Duration::operator+(const rclcpp::Duration & rhs) const
|
||||
this->rcl_duration_.nanoseconds,
|
||||
rhs.rcl_duration_.nanoseconds,
|
||||
std::numeric_limits<rcl_duration_value_t>::max());
|
||||
return Duration(
|
||||
return Duration::from_nanoseconds(
|
||||
rcl_duration_.nanoseconds + rhs.rcl_duration_.nanoseconds);
|
||||
}
|
||||
|
||||
@@ -177,7 +177,7 @@ Duration::operator-(const rclcpp::Duration & rhs) const
|
||||
rhs.rcl_duration_.nanoseconds,
|
||||
std::numeric_limits<rcl_duration_value_t>::max());
|
||||
|
||||
return Duration(
|
||||
return Duration::from_nanoseconds(
|
||||
rcl_duration_.nanoseconds - rhs.rcl_duration_.nanoseconds);
|
||||
}
|
||||
|
||||
@@ -208,7 +208,7 @@ Duration::operator*(double scale) const
|
||||
scale,
|
||||
std::numeric_limits<rcl_duration_value_t>::max());
|
||||
long double scale_ld = static_cast<long double>(scale);
|
||||
return Duration(
|
||||
return Duration::from_nanoseconds(
|
||||
static_cast<rcl_duration_value_t>(
|
||||
static_cast<long double>(rcl_duration_.nanoseconds) * scale_ld));
|
||||
}
|
||||
@@ -246,10 +246,41 @@ Duration::to_rmw_time() const
|
||||
return result;
|
||||
}
|
||||
|
||||
Duration
|
||||
Duration::from_rmw_time(rmw_time_t duration)
|
||||
{
|
||||
Duration ret;
|
||||
constexpr rcl_duration_value_t limit_ns = std::numeric_limits<rcl_duration_value_t>::max();
|
||||
constexpr rcl_duration_value_t limit_sec = RCL_NS_TO_S(limit_ns);
|
||||
if (duration.sec > limit_sec || duration.nsec > limit_ns) {
|
||||
// saturate if will overflow
|
||||
ret.rcl_duration_.nanoseconds = limit_ns;
|
||||
return ret;
|
||||
}
|
||||
uint64_t total_ns = RCL_S_TO_NS(duration.sec) + duration.nsec;
|
||||
if (total_ns > limit_ns) {
|
||||
// saturate if will overflow
|
||||
ret.rcl_duration_.nanoseconds = limit_ns;
|
||||
return ret;
|
||||
}
|
||||
ret.rcl_duration_.nanoseconds = static_cast<rcl_duration_value_t>(total_ns);
|
||||
return ret;
|
||||
}
|
||||
|
||||
Duration
|
||||
Duration::from_seconds(double seconds)
|
||||
{
|
||||
return Duration(static_cast<int64_t>(RCL_S_TO_NS(seconds)));
|
||||
Duration ret;
|
||||
ret.rcl_duration_.nanoseconds = static_cast<int64_t>(RCL_S_TO_NS(seconds));
|
||||
return ret;
|
||||
}
|
||||
|
||||
Duration
|
||||
Duration::from_nanoseconds(rcl_duration_value_t nanoseconds)
|
||||
{
|
||||
Duration ret;
|
||||
ret.rcl_duration_.nanoseconds = nanoseconds;
|
||||
return ret;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/executor.hpp"
|
||||
#include "rclcpp/guard_condition.hpp"
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/scope_exit.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
@@ -41,28 +43,35 @@ using rclcpp::FutureReturnCode;
|
||||
|
||||
Executor::Executor(const rclcpp::ExecutorOptions & options)
|
||||
: spinning(false),
|
||||
shutdown_guard_condition_(std::make_shared<rclcpp::GuardCondition>(options.context)),
|
||||
memory_strategy_(options.memory_strategy)
|
||||
{
|
||||
// Store the context for later use.
|
||||
context_ = options.context;
|
||||
|
||||
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&interrupt_guard_condition_, options.context->get_rcl_context().get(), guard_condition_options);
|
||||
&interrupt_guard_condition_, context_->get_rcl_context().get(), guard_condition_options);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "Failed to create interrupt guard condition in Executor constructor");
|
||||
}
|
||||
|
||||
context_->on_shutdown(
|
||||
[weak_gc = std::weak_ptr<rclcpp::GuardCondition>{shutdown_guard_condition_}]() {
|
||||
auto strong_gc = weak_gc.lock();
|
||||
if (strong_gc) {
|
||||
strong_gc->trigger();
|
||||
}
|
||||
});
|
||||
|
||||
// The number of guard conditions is always at least 2: 1 for the ctrl-c guard cond,
|
||||
// and one for the executor's guard cond (interrupt_guard_condition_)
|
||||
|
||||
// Put the global ctrl-c guard condition in
|
||||
memory_strategy_->add_guard_condition(options.context->get_interrupt_guard_condition(&wait_set_));
|
||||
memory_strategy_->add_guard_condition(&shutdown_guard_condition_->get_rcl_guard_condition());
|
||||
|
||||
// Put the executor's guard condition in
|
||||
memory_strategy_->add_guard_condition(&interrupt_guard_condition_);
|
||||
rcl_allocator_t allocator = memory_strategy_->get_allocator();
|
||||
|
||||
// Store the context for later use.
|
||||
context_ = options.context;
|
||||
|
||||
ret = rcl_wait_set_init(
|
||||
&wait_set_,
|
||||
0, 2, 0, 0, 0, 0,
|
||||
@@ -128,8 +137,7 @@ Executor::~Executor()
|
||||
rcl_reset_error();
|
||||
}
|
||||
// Remove and release the sigint guard condition
|
||||
memory_strategy_->remove_guard_condition(context_->get_interrupt_guard_condition(&wait_set_));
|
||||
context_->release_interrupt_guard_condition(&wait_set_, std::nothrow);
|
||||
memory_strategy_->remove_guard_condition(&shutdown_guard_condition_->get_rcl_guard_condition());
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
@@ -195,7 +203,7 @@ void
|
||||
Executor::add_callback_group_to_map(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
|
||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
|
||||
bool notify)
|
||||
{
|
||||
// If the callback_group already has an executor
|
||||
@@ -269,7 +277,7 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
|
||||
void
|
||||
Executor::remove_callback_group_from_map(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
|
||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
|
||||
bool notify)
|
||||
{
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr;
|
||||
@@ -342,27 +350,24 @@ Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node
|
||||
if (!found_node) {
|
||||
throw std::runtime_error("Node needs to be associated with this executor.");
|
||||
}
|
||||
std::vector<rclcpp::CallbackGroup::SharedPtr> found_group_ptrs;
|
||||
std::for_each(
|
||||
weak_groups_to_nodes_associated_with_executor_.begin(),
|
||||
weak_groups_to_nodes_associated_with_executor_.end(),
|
||||
[&found_group_ptrs, node_ptr](std::pair<rclcpp::CallbackGroup::WeakPtr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> key_value_pair) {
|
||||
auto weak_node_ptr = key_value_pair.second;
|
||||
auto shared_node_ptr = weak_node_ptr.lock();
|
||||
auto group_ptr = key_value_pair.first.lock();
|
||||
if (shared_node_ptr == node_ptr) {
|
||||
found_group_ptrs.push_back(group_ptr);
|
||||
}
|
||||
});
|
||||
std::for_each(
|
||||
found_group_ptrs.begin(), found_group_ptrs.end(), [this, notify]
|
||||
(rclcpp::CallbackGroup::SharedPtr group_ptr) {
|
||||
|
||||
for (auto it = weak_groups_to_nodes_associated_with_executor_.begin();
|
||||
it != weak_groups_to_nodes_associated_with_executor_.end(); )
|
||||
{
|
||||
auto weak_node_ptr = it->second;
|
||||
auto shared_node_ptr = weak_node_ptr.lock();
|
||||
auto group_ptr = it->first.lock();
|
||||
|
||||
// Increment iterator before removing in case it's invalidated
|
||||
it++;
|
||||
if (shared_node_ptr == node_ptr) {
|
||||
remove_callback_group_from_map(
|
||||
group_ptr,
|
||||
weak_groups_to_nodes_associated_with_executor_,
|
||||
notify);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
||||
has_executor.store(false);
|
||||
}
|
||||
@@ -506,7 +511,7 @@ Executor::execute_any_executable(AnyExecutable & any_exec)
|
||||
execute_client(any_exec.client);
|
||||
}
|
||||
if (any_exec.waitable) {
|
||||
any_exec.waitable->execute();
|
||||
any_exec.waitable->execute(any_exec.data);
|
||||
}
|
||||
// Reset the callback_group, regardless of type
|
||||
any_exec.callback_group->can_be_taken_from().store(true);
|
||||
@@ -678,8 +683,11 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
|
||||
if (weak_group_ptr.expired() || weak_node_ptr.expired()) {
|
||||
invalid_group_ptrs.push_back(weak_group_ptr);
|
||||
auto node_guard_pair = weak_nodes_to_guard_conditions_.find(weak_node_ptr);
|
||||
weak_nodes_to_guard_conditions_.erase(weak_node_ptr);
|
||||
memory_strategy_->remove_guard_condition(node_guard_pair->second);
|
||||
if (node_guard_pair != weak_nodes_to_guard_conditions_.end()) {
|
||||
auto guard_condition = node_guard_pair->second;
|
||||
weak_nodes_to_guard_conditions_.erase(weak_node_ptr);
|
||||
memory_strategy_->remove_guard_condition(guard_condition);
|
||||
}
|
||||
}
|
||||
}
|
||||
std::for_each(
|
||||
@@ -738,7 +746,7 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
Executor::get_node_by_group(
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes,
|
||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap weak_groups_to_nodes,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
if (!group) {
|
||||
@@ -796,7 +804,7 @@ Executor::get_next_ready_executable(AnyExecutable & any_executable)
|
||||
bool
|
||||
Executor::get_next_ready_executable_from_map(
|
||||
AnyExecutable & any_executable,
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes)
|
||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap weak_groups_to_nodes)
|
||||
{
|
||||
bool success = false;
|
||||
// Check the timers to see if there are any that are ready
|
||||
@@ -829,6 +837,7 @@ Executor::get_next_ready_executable_from_map(
|
||||
// Check the waitables to see if there are any that are ready
|
||||
memory_strategy_->get_next_waitable(any_executable, weak_groups_to_nodes);
|
||||
if (any_executable.waitable) {
|
||||
any_executable.data = any_executable.waitable->take_data();
|
||||
success = true;
|
||||
}
|
||||
}
|
||||
@@ -884,7 +893,7 @@ Executor::get_next_executable(AnyExecutable & any_executable, std::chrono::nanos
|
||||
bool
|
||||
Executor::has_node(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
WeakCallbackGroupsToNodesMap weak_groups_to_nodes) const
|
||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap weak_groups_to_nodes) const
|
||||
{
|
||||
return std::find_if(
|
||||
weak_groups_to_nodes.begin(),
|
||||
|
||||
@@ -77,7 +77,8 @@ StaticExecutorEntitiesCollector::init(
|
||||
// Add executor's guard condition
|
||||
memory_strategy_->add_guard_condition(executor_guard_condition);
|
||||
// Get memory strategy and executable list. Prepare wait_set_
|
||||
execute();
|
||||
std::shared_ptr<void> shared_ptr;
|
||||
execute(shared_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -87,9 +88,16 @@ StaticExecutorEntitiesCollector::fini()
|
||||
exec_list_.clear();
|
||||
}
|
||||
|
||||
void
|
||||
StaticExecutorEntitiesCollector::execute()
|
||||
std::shared_ptr<void>
|
||||
StaticExecutorEntitiesCollector::take_data()
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
StaticExecutorEntitiesCollector::execute(std::shared_ptr<void> & data)
|
||||
{
|
||||
(void) data;
|
||||
// Fill memory strategy with entities coming from weak_nodes_
|
||||
fill_memory_strategy();
|
||||
// Fill exec_list_ with entities coming from weak_nodes_ (same as memory strategy)
|
||||
@@ -155,7 +163,8 @@ StaticExecutorEntitiesCollector::fill_executable_list()
|
||||
}
|
||||
void
|
||||
StaticExecutorEntitiesCollector::fill_executable_list_from_map(
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
|
||||
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
|
||||
weak_groups_to_nodes)
|
||||
{
|
||||
for (const auto & pair : weak_groups_to_nodes) {
|
||||
auto group = pair.first.lock();
|
||||
@@ -297,7 +306,7 @@ bool
|
||||
StaticExecutorEntitiesCollector::add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
|
||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
|
||||
{
|
||||
// If the callback_group already has an executor
|
||||
std::atomic_bool & has_executor = group_ptr->get_associated_with_executor_atomic();
|
||||
@@ -341,7 +350,7 @@ StaticExecutorEntitiesCollector::remove_callback_group(
|
||||
bool
|
||||
StaticExecutorEntitiesCollector::remove_callback_group_from_map(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
|
||||
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap & weak_groups_to_nodes)
|
||||
{
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr;
|
||||
rclcpp::CallbackGroup::WeakPtr weak_group_ptr = group_ptr;
|
||||
@@ -437,7 +446,8 @@ StaticExecutorEntitiesCollector::is_ready(rcl_wait_set_t * p_wait_set)
|
||||
bool
|
||||
StaticExecutorEntitiesCollector::has_node(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) const
|
||||
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
|
||||
weak_groups_to_nodes) const
|
||||
{
|
||||
return std::find_if(
|
||||
weak_groups_to_nodes.begin(),
|
||||
|
||||
@@ -176,7 +176,8 @@ StaticSingleThreadedExecutor::execute_ready_executables()
|
||||
// Execute all the ready waitables
|
||||
for (size_t i = 0; i < entities_collector_->get_number_of_waitables(); ++i) {
|
||||
if (entities_collector_->get_waitable(i)->is_ready(&wait_set_)) {
|
||||
entities_collector_->get_waitable(i)->execute();
|
||||
std::shared_ptr<void> shared_ptr;
|
||||
entities_collector_->get_waitable(i)->execute(shared_ptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,11 +36,11 @@ namespace rclcpp
|
||||
namespace graph_listener
|
||||
{
|
||||
|
||||
GraphListener::GraphListener(std::shared_ptr<rclcpp::Context> parent_context)
|
||||
: parent_context_(parent_context),
|
||||
GraphListener::GraphListener(const std::shared_ptr<Context> & parent_context)
|
||||
: weak_parent_context_(parent_context),
|
||||
rcl_parent_context_(parent_context->get_rcl_context()),
|
||||
is_started_(false),
|
||||
is_shutdown_(false),
|
||||
shutdown_guard_condition_(nullptr)
|
||||
is_shutdown_(false)
|
||||
{
|
||||
// TODO(wjwwood): make a guard condition class in rclcpp so this can be tracked
|
||||
// automatically with the rcl guard condition
|
||||
@@ -48,13 +48,11 @@ GraphListener::GraphListener(std::shared_ptr<rclcpp::Context> parent_context)
|
||||
// guard condition is using it.
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&interrupt_guard_condition_,
|
||||
parent_context->get_rcl_context().get(),
|
||||
rcl_parent_context_.get(),
|
||||
rcl_guard_condition_get_default_options());
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
|
||||
}
|
||||
|
||||
shutdown_guard_condition_ = parent_context->get_interrupt_guard_condition(&wait_set_);
|
||||
}
|
||||
|
||||
GraphListener::~GraphListener()
|
||||
@@ -62,6 +60,23 @@ GraphListener::~GraphListener()
|
||||
this->shutdown(std::nothrow);
|
||||
}
|
||||
|
||||
void GraphListener::init_wait_set()
|
||||
{
|
||||
rcl_ret_t ret = rcl_wait_set_init(
|
||||
&wait_set_,
|
||||
0, // number_of_subscriptions
|
||||
2, // number_of_guard_conditions
|
||||
0, // number_of_timers
|
||||
0, // number_of_clients
|
||||
0, // number_of_services
|
||||
0, // number_of_events
|
||||
rcl_parent_context_.get(),
|
||||
rcl_get_default_allocator());
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to initialize wait set");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::start_if_not_started()
|
||||
{
|
||||
@@ -69,30 +84,13 @@ GraphListener::start_if_not_started()
|
||||
if (is_shutdown_.load()) {
|
||||
throw GraphListenerShutdownError();
|
||||
}
|
||||
if (!is_started_) {
|
||||
// Initialize the wait set before starting.
|
||||
auto parent_context = parent_context_.lock();
|
||||
if (!parent_context) {
|
||||
throw std::runtime_error("parent context was destroyed");
|
||||
}
|
||||
rcl_ret_t ret = rcl_wait_set_init(
|
||||
&wait_set_,
|
||||
0, // number_of_subscriptions
|
||||
2, // number_of_guard_conditions
|
||||
0, // number_of_timers
|
||||
0, // number_of_clients
|
||||
0, // number_of_services
|
||||
0, // number_of_events
|
||||
parent_context->get_rcl_context().get(),
|
||||
rcl_get_default_allocator());
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to initialize wait set");
|
||||
}
|
||||
auto parent_context = weak_parent_context_.lock();
|
||||
if (!is_started_ && parent_context) {
|
||||
// Register an on_shutdown hook to shtudown the graph listener.
|
||||
// This is important to ensure that the wait set is finalized before
|
||||
// destruction of static objects occurs.
|
||||
std::weak_ptr<GraphListener> weak_this = shared_from_this();
|
||||
rclcpp::on_shutdown(
|
||||
parent_context->on_shutdown(
|
||||
[weak_this]() {
|
||||
auto shared_this = weak_this.lock();
|
||||
if (shared_this) {
|
||||
@@ -100,6 +98,8 @@ GraphListener::start_if_not_started()
|
||||
shared_this->shutdown(std::nothrow);
|
||||
}
|
||||
});
|
||||
// Initialize the wait set before starting.
|
||||
init_wait_set();
|
||||
// Start the listener thread.
|
||||
listener_thread_ = std::thread(&GraphListener::run, this);
|
||||
is_started_ = true;
|
||||
@@ -144,14 +144,6 @@ GraphListener::run_loop()
|
||||
}
|
||||
// This lock is released when the loop continues or exits.
|
||||
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
|
||||
// Ensure that the context doesn't go out of scope.
|
||||
auto parent_context = parent_context_.lock();
|
||||
if (!parent_context) {
|
||||
// The parent context may be destroyed before this loop is stopped.
|
||||
// In that case, the loop is broken and the function just returns silently.
|
||||
return;
|
||||
}
|
||||
|
||||
// Resize the wait set if necessary.
|
||||
const size_t node_graph_interfaces_size = node_graph_interfaces_.size();
|
||||
// Add 2 for the interrupt and shutdown guard conditions
|
||||
@@ -171,13 +163,7 @@ GraphListener::run_loop()
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to add interrupt guard condition to wait set");
|
||||
}
|
||||
// Put the shutdown guard condition in the wait set.
|
||||
size_t shutdown_guard_condition_index = 0u;
|
||||
ret = rcl_wait_set_add_guard_condition(
|
||||
&wait_set_, shutdown_guard_condition_, &shutdown_guard_condition_index);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to add shutdown guard condition to wait set");
|
||||
}
|
||||
|
||||
// Put graph guard conditions for each node into the wait set.
|
||||
std::vector<size_t> graph_gc_indexes(node_graph_interfaces_size, 0u);
|
||||
for (size_t i = 0u; i < node_graph_interfaces_size; ++i) {
|
||||
@@ -206,9 +192,6 @@ GraphListener::run_loop()
|
||||
throw_from_rcl_error(ret, "failed to wait on wait set");
|
||||
}
|
||||
|
||||
// Check to see if the shutdown guard condition has been triggered.
|
||||
bool shutdown_guard_condition_triggered =
|
||||
(shutdown_guard_condition_ == wait_set_.guard_conditions[shutdown_guard_condition_index]);
|
||||
// Notify nodes who's guard conditions are set (triggered).
|
||||
for (size_t i = 0u; i < node_graph_interfaces_size; ++i) {
|
||||
const auto node_ptr = node_graph_interfaces_[i];
|
||||
@@ -219,7 +202,7 @@ GraphListener::run_loop()
|
||||
if (graph_gc == wait_set_.guard_conditions[graph_gc_indexes[i]]) {
|
||||
node_ptr->notify_graph_change();
|
||||
}
|
||||
if (shutdown_guard_condition_triggered) {
|
||||
if (is_shutdown_) {
|
||||
// If shutdown, then notify the node of this as well.
|
||||
node_ptr->notify_shutdown();
|
||||
}
|
||||
@@ -351,7 +334,16 @@ GraphListener::remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_gr
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::__shutdown(bool should_throw)
|
||||
GraphListener::cleanup_wait_set()
|
||||
{
|
||||
rcl_ret_t ret = rcl_wait_set_fini(&wait_set_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to finalize wait set");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::__shutdown()
|
||||
{
|
||||
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
|
||||
if (!is_shutdown_.exchange(true)) {
|
||||
@@ -363,33 +355,8 @@ GraphListener::__shutdown(bool should_throw)
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to finalize interrupt guard condition");
|
||||
}
|
||||
if (shutdown_guard_condition_) {
|
||||
auto parent_context_ptr = parent_context_.lock();
|
||||
if (parent_context_ptr) {
|
||||
if (should_throw) {
|
||||
parent_context_ptr->release_interrupt_guard_condition(&wait_set_);
|
||||
} else {
|
||||
parent_context_ptr->release_interrupt_guard_condition(&wait_set_, std::nothrow);
|
||||
}
|
||||
} else {
|
||||
ret = rcl_guard_condition_fini(shutdown_guard_condition_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
if (should_throw) {
|
||||
throw_from_rcl_error(ret, "failed to finalize shutdown guard condition");
|
||||
} else {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"failed to finalize shutdown guard condition");
|
||||
}
|
||||
}
|
||||
}
|
||||
shutdown_guard_condition_ = nullptr;
|
||||
}
|
||||
if (is_started_) {
|
||||
ret = rcl_wait_set_fini(&wait_set_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to finalize wait set");
|
||||
}
|
||||
cleanup_wait_set();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -397,14 +364,14 @@ GraphListener::__shutdown(bool should_throw)
|
||||
void
|
||||
GraphListener::shutdown()
|
||||
{
|
||||
this->__shutdown(true);
|
||||
this->__shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::shutdown(const std::nothrow_t &) noexcept
|
||||
{
|
||||
try {
|
||||
this->__shutdown(false);
|
||||
this->__shutdown();
|
||||
} catch (const std::exception & exc) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
|
||||
@@ -70,6 +70,7 @@ NodeBase::NodeBase(
|
||||
std::shared_ptr<std::recursive_mutex> logging_mutex = get_global_logging_mutex();
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
|
||||
// TODO(ivanpauno): /rosout Qos should be reconfigurable.
|
||||
// TODO(ivanpauno): Instead of mutually excluding rcl_node_init with the global logger mutex,
|
||||
// rcl_logging_rosout_init_publisher_for_node could be decoupled from there and be called
|
||||
// here directly.
|
||||
@@ -288,3 +289,24 @@ NodeBase::get_enable_topic_statistics_default() const
|
||||
{
|
||||
return enable_topic_statistics_default_;
|
||||
}
|
||||
|
||||
std::string
|
||||
NodeBase::resolve_topic_or_service_name(
|
||||
const std::string & name, bool is_service, bool only_expand) const
|
||||
{
|
||||
char * output_cstr = NULL;
|
||||
auto allocator = rcl_get_default_allocator();
|
||||
rcl_ret_t ret = rcl_node_resolve_name(
|
||||
node_handle_.get(),
|
||||
name.c_str(),
|
||||
allocator,
|
||||
is_service,
|
||||
only_expand,
|
||||
&output_cstr);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to resolve name", rcl_get_error_state());
|
||||
}
|
||||
std::string output{output_cstr};
|
||||
allocator.deallocate(output_cstr, allocator.state);
|
||||
return output;
|
||||
}
|
||||
|
||||
@@ -67,6 +67,7 @@ NodeParameters::NodeParameters(
|
||||
}
|
||||
|
||||
if (start_parameter_event_publisher) {
|
||||
// TODO(ivanpauno): Qos of the `/parameters_event` topic should be somehow overridable.
|
||||
events_publisher_ = rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
|
||||
node_topics,
|
||||
"/parameter_events",
|
||||
|
||||
@@ -78,3 +78,9 @@ NodeServices::add_client(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
NodeServices::resolve_service_name(const std::string & name, bool only_expand) const
|
||||
{
|
||||
return node_base_->resolve_topic_or_service_name(name, true, only_expand);
|
||||
}
|
||||
|
||||
@@ -129,3 +129,9 @@ NodeTopics::get_node_timers_interface() const
|
||||
{
|
||||
return node_timers_;
|
||||
}
|
||||
|
||||
std::string
|
||||
NodeTopics::resolve_topic_name(const std::string & name, bool only_expand) const
|
||||
{
|
||||
return node_base_->resolve_topic_or_service_name(name, false, only_expand);
|
||||
}
|
||||
|
||||
@@ -99,6 +99,13 @@ QoS::history(rmw_qos_history_policy_t history)
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::history(HistoryPolicy history)
|
||||
{
|
||||
rmw_qos_profile_.history = static_cast<rmw_qos_history_policy_t>(history);
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::keep_last(size_t depth)
|
||||
{
|
||||
@@ -122,6 +129,13 @@ QoS::reliability(rmw_qos_reliability_policy_t reliability)
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::reliability(ReliabilityPolicy reliability)
|
||||
{
|
||||
rmw_qos_profile_.reliability = static_cast<rmw_qos_reliability_policy_t>(reliability);
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::reliable()
|
||||
{
|
||||
@@ -141,6 +155,13 @@ QoS::durability(rmw_qos_durability_policy_t durability)
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::durability(DurabilityPolicy durability)
|
||||
{
|
||||
rmw_qos_profile_.durability = static_cast<rmw_qos_durability_policy_t>(durability);
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::durability_volatile()
|
||||
{
|
||||
@@ -186,6 +207,14 @@ QoS::liveliness(rmw_qos_liveliness_policy_t liveliness)
|
||||
return *this;
|
||||
}
|
||||
|
||||
QoS &
|
||||
QoS::liveliness(LivelinessPolicy liveliness)
|
||||
{
|
||||
rmw_qos_profile_.liveliness = static_cast<rmw_qos_liveliness_policy_t>(liveliness);
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
||||
QoS &
|
||||
QoS::liveliness_lease_duration(rmw_time_t liveliness_lease_duration)
|
||||
{
|
||||
@@ -206,6 +235,51 @@ QoS::avoid_ros_namespace_conventions(bool avoid_ros_namespace_conventions)
|
||||
return *this;
|
||||
}
|
||||
|
||||
HistoryPolicy
|
||||
QoS::history() const
|
||||
{
|
||||
return static_cast<HistoryPolicy>(rmw_qos_profile_.history);
|
||||
}
|
||||
|
||||
size_t
|
||||
QoS::depth() const {return rmw_qos_profile_.depth;}
|
||||
|
||||
ReliabilityPolicy
|
||||
QoS::reliability() const
|
||||
{
|
||||
return static_cast<ReliabilityPolicy>(rmw_qos_profile_.reliability);
|
||||
}
|
||||
|
||||
DurabilityPolicy
|
||||
QoS::durability() const
|
||||
{
|
||||
return static_cast<DurabilityPolicy>(rmw_qos_profile_.durability);
|
||||
}
|
||||
|
||||
Duration
|
||||
QoS::deadline() const {return Duration::from_rmw_time(rmw_qos_profile_.deadline);}
|
||||
|
||||
Duration
|
||||
QoS::lifespan() const {return Duration::from_rmw_time(rmw_qos_profile_.lifespan);}
|
||||
|
||||
LivelinessPolicy
|
||||
QoS::liveliness() const
|
||||
{
|
||||
return static_cast<LivelinessPolicy>(rmw_qos_profile_.liveliness);
|
||||
}
|
||||
|
||||
Duration
|
||||
QoS::liveliness_lease_duration() const
|
||||
{
|
||||
return Duration::from_rmw_time(rmw_qos_profile_.liveliness_lease_duration);
|
||||
}
|
||||
|
||||
bool
|
||||
QoS::avoid_ros_namespace_conventions() const
|
||||
{
|
||||
return rmw_qos_profile_.avoid_ros_namespace_conventions;
|
||||
}
|
||||
|
||||
namespace
|
||||
{
|
||||
/// Check if two rmw_time_t have the same values.
|
||||
|
||||
84
rclcpp/src/rclcpp/qos_overriding_options.cpp
Normal file
84
rclcpp/src/rclcpp/qos_overriding_options.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
|
||||
#include <initializer_list>
|
||||
#include <ostream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rmw/qos_policy_kind.h"
|
||||
#include "rmw/qos_string_conversions.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
const char *
|
||||
qos_policy_kind_to_cstr(const QosPolicyKind & qpk)
|
||||
{
|
||||
const char * ret = rmw_qos_policy_kind_to_str(static_cast<rmw_qos_policy_kind_t>(qpk));
|
||||
if (!ret) {
|
||||
throw std::invalid_argument{"unknown QoS policy kind"};
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
operator<<(std::ostream & oss, const QosPolicyKind & qpk)
|
||||
{
|
||||
return oss << qos_policy_kind_to_cstr(qpk);
|
||||
}
|
||||
|
||||
static std::initializer_list<QosPolicyKind> kDefaultPolicies =
|
||||
{QosPolicyKind::History, QosPolicyKind::Depth, QosPolicyKind::Reliability};
|
||||
|
||||
QosOverridingOptions::QosOverridingOptions(
|
||||
std::initializer_list<QosPolicyKind> policy_kinds,
|
||||
QosCallback validation_callback,
|
||||
std::string id)
|
||||
: id_{std::move(id)},
|
||||
policy_kinds_{policy_kinds},
|
||||
validation_callback_{std::move(validation_callback)}
|
||||
{}
|
||||
|
||||
QosOverridingOptions
|
||||
QosOverridingOptions::with_default_policies(
|
||||
QosCallback validation_callback,
|
||||
std::string id)
|
||||
{
|
||||
return QosOverridingOptions{kDefaultPolicies, validation_callback, id};
|
||||
}
|
||||
|
||||
const std::string &
|
||||
QosOverridingOptions::get_id() const
|
||||
{
|
||||
return id_;
|
||||
}
|
||||
|
||||
const std::vector<QosPolicyKind> &
|
||||
QosOverridingOptions::get_policy_kinds() const
|
||||
{
|
||||
return policy_kinds_;
|
||||
}
|
||||
|
||||
const QosCallback &
|
||||
QosOverridingOptions::get_validation_callback() const
|
||||
{
|
||||
return validation_callback_;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -199,7 +199,7 @@ Time::operator-(const rclcpp::Time & rhs) const
|
||||
throw std::underflow_error("time subtraction leads to int64_t underflow");
|
||||
}
|
||||
|
||||
return Duration(rcl_time_.nanoseconds - rhs.rcl_time_.nanoseconds);
|
||||
return Duration::from_nanoseconds(rcl_time_.nanoseconds - rhs.rcl_time_.nanoseconds);
|
||||
}
|
||||
|
||||
Time
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "./signal_handler.hpp"
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/detail/utilities.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
@@ -56,6 +57,47 @@ uninstall_signal_handlers()
|
||||
return SignalHandler::get_global_signal_handler().uninstall();
|
||||
}
|
||||
|
||||
static
|
||||
std::vector<std::string>
|
||||
_remove_ros_arguments(
|
||||
char const * const argv[],
|
||||
const rcl_arguments_t * args,
|
||||
rcl_allocator_t alloc)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
int nonros_argc = 0;
|
||||
const char ** nonros_argv = NULL;
|
||||
|
||||
ret = rcl_remove_ros_arguments(
|
||||
argv,
|
||||
args,
|
||||
alloc,
|
||||
&nonros_argc,
|
||||
&nonros_argv);
|
||||
|
||||
if (RCL_RET_OK != ret || nonros_argc < 0) {
|
||||
// Not using throw_from_rcl_error, because we may need to append deallocation failures.
|
||||
exceptions::RCLError exc(ret, rcl_get_error_state(), "");
|
||||
rcl_reset_error();
|
||||
if (NULL != nonros_argv) {
|
||||
alloc.deallocate(nonros_argv, alloc.state);
|
||||
}
|
||||
throw exc;
|
||||
}
|
||||
|
||||
std::vector<std::string> return_arguments(static_cast<size_t>(nonros_argc));
|
||||
|
||||
for (size_t ii = 0; ii < static_cast<size_t>(nonros_argc); ++ii) {
|
||||
return_arguments[ii] = std::string(nonros_argv[ii]);
|
||||
}
|
||||
|
||||
if (NULL != nonros_argv) {
|
||||
alloc.deallocate(nonros_argv, alloc.state);
|
||||
}
|
||||
|
||||
return return_arguments;
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
init_and_remove_ros_arguments(
|
||||
int argc,
|
||||
@@ -63,7 +105,10 @@ init_and_remove_ros_arguments(
|
||||
const InitOptions & init_options)
|
||||
{
|
||||
init(argc, argv, init_options);
|
||||
return remove_ros_arguments(argc, argv);
|
||||
|
||||
using rclcpp::contexts::get_global_default_context;
|
||||
auto rcl_context = get_global_default_context()->get_rcl_context();
|
||||
return _remove_ros_arguments(argv, &(rcl_context->global_arguments), rcl_get_default_allocator());
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
@@ -79,40 +124,17 @@ remove_ros_arguments(int argc, char const * const argv[])
|
||||
exceptions::throw_from_rcl_error(ret, "failed to parse arguments");
|
||||
}
|
||||
|
||||
int nonros_argc = 0;
|
||||
const char ** nonros_argv = NULL;
|
||||
|
||||
ret = rcl_remove_ros_arguments(
|
||||
argv,
|
||||
&parsed_args,
|
||||
alloc,
|
||||
&nonros_argc,
|
||||
&nonros_argv);
|
||||
|
||||
if (RCL_RET_OK != ret || nonros_argc < 0) {
|
||||
// Not using throw_from_rcl_error, because we may need to append deallocation failures.
|
||||
exceptions::RCLErrorBase base_exc(ret, rcl_get_error_state());
|
||||
rcl_reset_error();
|
||||
if (NULL != nonros_argv) {
|
||||
alloc.deallocate(nonros_argv, alloc.state);
|
||||
}
|
||||
std::vector<std::string> return_arguments;
|
||||
try {
|
||||
return_arguments = _remove_ros_arguments(argv, &parsed_args, alloc);
|
||||
} catch (exceptions::RCLError & exc) {
|
||||
if (RCL_RET_OK != rcl_arguments_fini(&parsed_args)) {
|
||||
base_exc.formatted_message += std::string(
|
||||
exc.formatted_message += std::string(
|
||||
", failed also to cleanup parsed arguments, leaking memory: ") +
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
throw exceptions::RCLError(base_exc, "");
|
||||
}
|
||||
|
||||
std::vector<std::string> return_arguments(static_cast<size_t>(nonros_argc));
|
||||
|
||||
for (size_t ii = 0; ii < static_cast<size_t>(nonros_argc); ++ii) {
|
||||
return_arguments[ii] = std::string(nonros_argv[ii]);
|
||||
}
|
||||
|
||||
if (NULL != nonros_argv) {
|
||||
alloc.deallocate(nonros_argv, alloc.state);
|
||||
throw exc;
|
||||
}
|
||||
|
||||
ret = rcl_arguments_fini(&parsed_args);
|
||||
|
||||
@@ -4,14 +4,7 @@ find_package(test_msgs REQUIRED)
|
||||
|
||||
include(cmake/rclcpp_add_build_failure_test.cmake)
|
||||
|
||||
rosidl_generate_interfaces(${PROJECT_NAME}_test_msgs
|
||||
msg/Header.msg
|
||||
msg/MessageWithHeader.msg
|
||||
DEPENDENCIES builtin_interfaces
|
||||
LIBRARY_NAME ${PROJECT_NAME}
|
||||
SKIP_INSTALL
|
||||
)
|
||||
|
||||
add_subdirectory(benchmark)
|
||||
add_subdirectory(rclcpp)
|
||||
|
||||
ament_add_gtest(test_rclcpp_gtest_macros utils/test_rclcpp_gtest_macros.cpp)
|
||||
|
||||
44
rclcpp/test/benchmark/CMakeLists.txt
Normal file
44
rclcpp/test/benchmark/CMakeLists.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
find_package(ament_cmake_google_benchmark REQUIRED)
|
||||
find_package(performance_test_fixture REQUIRED)
|
||||
|
||||
# These benchmarks are only being created and run for the default RMW
|
||||
# implementation. We are looking to test the performance of the ROS 2 code, not
|
||||
# the underlying middleware.
|
||||
|
||||
add_performance_test(benchmark_client benchmark_client.cpp)
|
||||
if(TARGET benchmark_client)
|
||||
target_link_libraries(benchmark_client ${PROJECT_NAME})
|
||||
ament_target_dependencies(benchmark_client test_msgs rcl_interfaces)
|
||||
endif()
|
||||
|
||||
add_performance_test(benchmark_executor benchmark_executor.cpp)
|
||||
if(TARGET benchmark_executor)
|
||||
target_link_libraries(benchmark_executor ${PROJECT_NAME})
|
||||
ament_target_dependencies(benchmark_executor test_msgs)
|
||||
endif()
|
||||
|
||||
add_performance_test(benchmark_init_shutdown benchmark_init_shutdown.cpp)
|
||||
if(TARGET benchmark_init_shutdown)
|
||||
target_link_libraries(benchmark_init_shutdown ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
add_performance_test(benchmark_node benchmark_node.cpp)
|
||||
if(TARGET benchmark_node)
|
||||
target_link_libraries(benchmark_node ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
add_performance_test(benchmark_node_parameters_interface benchmark_node_parameters_interface.cpp)
|
||||
if(TARGET benchmark_node_parameters_interface)
|
||||
target_link_libraries(benchmark_node_parameters_interface ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_google_benchmark(benchmark_parameter_client benchmark_parameter_client.cpp)
|
||||
if(TARGET benchmark_parameter_client)
|
||||
target_link_libraries(benchmark_parameter_client ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
add_performance_test(benchmark_service benchmark_service.cpp)
|
||||
if(TARGET benchmark_service)
|
||||
target_link_libraries(benchmark_service ${PROJECT_NAME})
|
||||
ament_target_dependencies(benchmark_service test_msgs rcl_interfaces)
|
||||
endif()
|
||||
153
rclcpp/test/benchmark/benchmark_client.cpp
Normal file
153
rclcpp/test/benchmark/benchmark_client.cpp
Normal file
@@ -0,0 +1,153 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "performance_test_fixture/performance_test_fixture.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
|
||||
using performance_test_fixture::PerformanceTest;
|
||||
|
||||
constexpr char empty_service_name[] = "empty_service";
|
||||
|
||||
class ClientPerformanceTest : public PerformanceTest
|
||||
{
|
||||
public:
|
||||
explicit ClientPerformanceTest(rclcpp::NodeOptions = rclcpp::NodeOptions()) {}
|
||||
void SetUp(benchmark::State & state)
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
node = std::make_unique<rclcpp::Node>("node", "ns");
|
||||
|
||||
auto empty_service_callback =
|
||||
[](
|
||||
const test_msgs::srv::Empty::Request::SharedPtr,
|
||||
test_msgs::srv::Empty::Response::SharedPtr) {};
|
||||
empty_service =
|
||||
node->create_service<test_msgs::srv::Empty>(empty_service_name, empty_service_callback);
|
||||
|
||||
performance_test_fixture::PerformanceTest::SetUp(state);
|
||||
}
|
||||
|
||||
void TearDown(benchmark::State & state)
|
||||
{
|
||||
performance_test_fixture::PerformanceTest::TearDown(state);
|
||||
empty_service.reset();
|
||||
node.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<rclcpp::Node> node;
|
||||
std::shared_ptr<rclcpp::Service<test_msgs::srv::Empty>> empty_service;
|
||||
};
|
||||
|
||||
BENCHMARK_F(ClientPerformanceTest, construct_client_no_service)(benchmark::State & state) {
|
||||
// Prime cache
|
||||
auto outer_client = node->create_client<test_msgs::srv::Empty>("not_an_existing_service");
|
||||
outer_client.reset();
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
auto client = node->create_client<test_msgs::srv::Empty>("not_an_existing_service");
|
||||
benchmark::DoNotOptimize(client);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
state.PauseTiming();
|
||||
client.reset();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ClientPerformanceTest, construct_client_empty_srv)(benchmark::State & state) {
|
||||
// Prime cache
|
||||
auto outer_client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
|
||||
outer_client.reset();
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
|
||||
benchmark::DoNotOptimize(client);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
state.PauseTiming();
|
||||
client.reset();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ClientPerformanceTest, destroy_client_empty_srv)(benchmark::State & state) {
|
||||
// Prime cache
|
||||
auto outer_client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
|
||||
outer_client.reset();
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
|
||||
state.ResumeTiming();
|
||||
benchmark::DoNotOptimize(client);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
client.reset();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ClientPerformanceTest, wait_for_service)(benchmark::State & state) {
|
||||
int count = 0;
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
const std::string service_name = std::string("service_") + std::to_string(count++);
|
||||
// Create client before service so it has to 'discover' the service after construction
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(service_name);
|
||||
auto callback =
|
||||
[](
|
||||
const test_msgs::srv::Empty::Request::SharedPtr,
|
||||
test_msgs::srv::Empty::Response::SharedPtr) {};
|
||||
auto service =
|
||||
node->create_service<test_msgs::srv::Empty>(service_name, callback);
|
||||
state.ResumeTiming();
|
||||
|
||||
client->wait_for_service(std::chrono::seconds(1));
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ClientPerformanceTest, async_send_request_only)(benchmark::State & state) {
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
|
||||
auto shared_request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
auto future = client->async_send_request(shared_request);
|
||||
benchmark::DoNotOptimize(future);
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ClientPerformanceTest, async_send_request_and_response)(benchmark::State & state) {
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
|
||||
auto shared_request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
auto future = client->async_send_request(shared_request);
|
||||
rclcpp::spin_until_future_complete(
|
||||
node->get_node_base_interface(), future, std::chrono::seconds(1));
|
||||
benchmark::DoNotOptimize(future);
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
}
|
||||
390
rclcpp/test/benchmark/benchmark_executor.cpp
Normal file
390
rclcpp/test/benchmark/benchmark_executor.cpp
Normal file
@@ -0,0 +1,390 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "performance_test_fixture/performance_test_fixture.hpp"
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp/scope_exit.hpp"
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
using performance_test_fixture::PerformanceTest;
|
||||
|
||||
constexpr unsigned int kNumberOfNodes = 10;
|
||||
|
||||
class PerformanceTestExecutor : public PerformanceTest
|
||||
{
|
||||
public:
|
||||
void SetUp(benchmark::State & st)
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
callback_count = 0;
|
||||
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
|
||||
nodes.push_back(std::make_shared<rclcpp::Node>("my_node_" + std::to_string(i)));
|
||||
|
||||
publishers.push_back(
|
||||
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++;};
|
||||
subscriptions.push_back(
|
||||
nodes[i]->create_subscription<test_msgs::msg::Empty>(
|
||||
"/empty_msgs_" + std::to_string(i), rclcpp::QoS(10), std::move(callback)));
|
||||
}
|
||||
PerformanceTest::SetUp(st);
|
||||
}
|
||||
void TearDown(benchmark::State & st)
|
||||
{
|
||||
PerformanceTest::TearDown(st);
|
||||
subscriptions.clear();
|
||||
publishers.clear();
|
||||
nodes.clear();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
test_msgs::msg::Empty empty_msgs;
|
||||
std::vector<rclcpp::Node::SharedPtr> nodes;
|
||||
std::vector<rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr> publishers;
|
||||
std::vector<rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr> subscriptions;
|
||||
int callback_count;
|
||||
};
|
||||
|
||||
BENCHMARK_F(PerformanceTestExecutor, single_thread_executor_spin_some)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
|
||||
executor.add_node(nodes[i]);
|
||||
publishers[i]->publish(empty_msgs);
|
||||
executor.spin_some(100ms);
|
||||
}
|
||||
|
||||
callback_count = 0;
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
st.PauseTiming();
|
||||
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
|
||||
publishers[i]->publish(empty_msgs);
|
||||
}
|
||||
st.ResumeTiming();
|
||||
|
||||
executor.spin_some(100ms);
|
||||
}
|
||||
if (callback_count == 0) {
|
||||
st.SkipWithError("No message was received");
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(PerformanceTestExecutor, multi_thread_executor_spin_some)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::MultiThreadedExecutor executor;
|
||||
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
|
||||
executor.add_node(nodes[i]);
|
||||
publishers[i]->publish(empty_msgs);
|
||||
executor.spin_some(100ms);
|
||||
}
|
||||
|
||||
callback_count = 0;
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
st.PauseTiming();
|
||||
for (unsigned int i = 0u; i < kNumberOfNodes; i++) {
|
||||
publishers[i]->publish(empty_msgs);
|
||||
}
|
||||
st.ResumeTiming();
|
||||
|
||||
executor.spin_some(100ms);
|
||||
}
|
||||
if (callback_count == 0) {
|
||||
st.SkipWithError("No message was received");
|
||||
}
|
||||
}
|
||||
|
||||
class PerformanceTestExecutorSimple : public PerformanceTest
|
||||
{
|
||||
public:
|
||||
void SetUp(benchmark::State & st)
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
node = std::make_shared<rclcpp::Node>("my_node");
|
||||
|
||||
PerformanceTest::SetUp(st);
|
||||
}
|
||||
void TearDown(benchmark::State & st)
|
||||
{
|
||||
PerformanceTest::TearDown(st);
|
||||
node.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
|
||||
BENCHMARK_F(PerformanceTestExecutorSimple, single_thread_executor_add_node)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
executor.add_node(node);
|
||||
st.PauseTiming();
|
||||
executor.remove_node(node);
|
||||
st.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple, single_thread_executor_remove_node)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
st.PauseTiming();
|
||||
executor.add_node(node);
|
||||
st.ResumeTiming();
|
||||
executor.remove_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(PerformanceTestExecutorSimple, multi_thread_executor_add_node)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::MultiThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
executor.add_node(node);
|
||||
st.PauseTiming();
|
||||
executor.remove_node(node);
|
||||
st.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(PerformanceTestExecutorSimple, multi_thread_executor_remove_node)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::MultiThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
st.PauseTiming();
|
||||
executor.add_node(node);
|
||||
st.ResumeTiming();
|
||||
executor.remove_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_add_node)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
executor.add_node(node);
|
||||
st.PauseTiming();
|
||||
executor.remove_node(node);
|
||||
st.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_remove_node)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
st.PauseTiming();
|
||||
executor.add_node(node);
|
||||
st.ResumeTiming();
|
||||
executor.remove_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_spin_until_future_complete)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
// test success of an immediately finishing future
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
auto shared_future = future.share();
|
||||
|
||||
auto ret = executor.spin_until_future_complete(shared_future, 100ms);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
}
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
// static_single_thread_executor has a special design. We need to add/remove the node each
|
||||
// time you call spin
|
||||
st.PauseTiming();
|
||||
executor.add_node(node);
|
||||
st.ResumeTiming();
|
||||
|
||||
ret = executor.spin_until_future_complete(shared_future, 100ms);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
break;
|
||||
}
|
||||
st.PauseTiming();
|
||||
executor.remove_node(node);
|
||||
st.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
single_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
// test success of an immediately finishing future
|
||||
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, node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
}
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
multi_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::MultiThreadedExecutor executor;
|
||||
// test success of an immediately finishing future
|
||||
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, node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
}
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
// test success of an immediately finishing future
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
auto shared_future = future.share();
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
auto ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(PerformanceTestExecutorSimple, spin_until_future_complete)(benchmark::State & st)
|
||||
{
|
||||
// test success of an immediately finishing future
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
auto shared_future = future.share();
|
||||
|
||||
auto ret = rclcpp::spin_until_future_complete(node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
}
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
ret = rclcpp::spin_until_future_complete(node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_executor_entities_collector_execute)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticExecutorEntitiesCollector::SharedPtr entities_collector_ =
|
||||
std::make_shared<rclcpp::executors::StaticExecutorEntitiesCollector>();
|
||||
entities_collector_->add_node(node->get_node_base_interface());
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_allocator_t allocator = rcl_get_default_allocator();
|
||||
auto shared_context = node->get_node_base_interface()->get_context();
|
||||
rcl_context_t * context = shared_context->get_rcl_context().get();
|
||||
rcl_ret_t ret = rcl_wait_set_init(&wait_set, 100, 100, 100, 100, 100, 100, context, allocator);
|
||||
if (ret != RCL_RET_OK) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
}
|
||||
RCLCPP_SCOPE_EXIT(
|
||||
{
|
||||
rcl_ret_t ret = rcl_wait_set_fini(&wait_set);
|
||||
if (ret != RCL_RET_OK) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
}
|
||||
});
|
||||
|
||||
auto memory_strategy = rclcpp::memory_strategies::create_default_strategy();
|
||||
rclcpp::GuardCondition guard_condition(shared_context);
|
||||
rcl_guard_condition_t rcl_guard_condition = guard_condition.get_rcl_guard_condition();
|
||||
|
||||
entities_collector_->init(&wait_set, memory_strategy, &rcl_guard_condition);
|
||||
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
std::shared_ptr<void> data = entities_collector_->take_data();
|
||||
entities_collector_->execute(data);
|
||||
}
|
||||
}
|
||||
53
rclcpp/test/benchmark/benchmark_init_shutdown.cpp
Normal file
53
rclcpp/test/benchmark/benchmark_init_shutdown.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "performance_test_fixture/performance_test_fixture.hpp"
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
using performance_test_fixture::PerformanceTest;
|
||||
|
||||
BENCHMARK_F(PerformanceTest, rclcpp_init)(benchmark::State & state)
|
||||
{
|
||||
// Warmup and prime caches
|
||||
rclcpp::init(0, nullptr);
|
||||
rclcpp::shutdown();
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
state.PauseTiming();
|
||||
rclcpp::shutdown();
|
||||
state.ResumeTiming();
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(PerformanceTest, rclcpp_shutdown)(benchmark::State & state)
|
||||
{
|
||||
// Warmup and prime caches
|
||||
rclcpp::init(0, nullptr);
|
||||
rclcpp::shutdown();
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
rclcpp::init(0, nullptr);
|
||||
state.ResumeTiming();
|
||||
|
||||
rclcpp::shutdown();
|
||||
benchmark::ClobberMemory();
|
||||
}
|
||||
}
|
||||
77
rclcpp/test/benchmark/benchmark_node.cpp
Normal file
77
rclcpp/test/benchmark/benchmark_node.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "performance_test_fixture/performance_test_fixture.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
using performance_test_fixture::PerformanceTest;
|
||||
|
||||
class NodePerformanceTest : public PerformanceTest
|
||||
{
|
||||
public:
|
||||
void SetUp(benchmark::State & state)
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
performance_test_fixture::PerformanceTest::SetUp(state);
|
||||
}
|
||||
|
||||
void TearDown(benchmark::State & state)
|
||||
{
|
||||
performance_test_fixture::PerformanceTest::TearDown(state);
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
BENCHMARK_F(NodePerformanceTest, create_node)(benchmark::State & state)
|
||||
{
|
||||
// Warmup and prime caches
|
||||
auto outer_node = std::make_shared<rclcpp::Node>("node");
|
||||
outer_node.reset();
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
// Using pointer to separate construction and destruction in timing
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
benchmark::DoNotOptimize(node);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
// Ensure destruction of node is not counted toward timing
|
||||
state.PauseTiming();
|
||||
node.reset();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(NodePerformanceTest, destroy_node)(benchmark::State & state)
|
||||
{
|
||||
// Warmup and prime caches
|
||||
auto outer_node = std::make_shared<rclcpp::Node>("node");
|
||||
outer_node.reset();
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
// Using pointer to separate construction and destruction in timing
|
||||
state.PauseTiming();
|
||||
auto node = std::make_shared<rclcpp::Node>("node");
|
||||
state.ResumeTiming();
|
||||
|
||||
benchmark::DoNotOptimize(node);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
node.reset();
|
||||
}
|
||||
}
|
||||
262
rclcpp/test/benchmark/benchmark_node_parameters_interface.cpp
Normal file
262
rclcpp/test/benchmark/benchmark_node_parameters_interface.cpp
Normal file
@@ -0,0 +1,262 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "performance_test_fixture/performance_test_fixture.hpp"
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
class NodeParametersInterfaceTest : public performance_test_fixture::PerformanceTest
|
||||
{
|
||||
public:
|
||||
NodeParametersInterfaceTest()
|
||||
: node_name("my_node"),
|
||||
param_prefix("my_prefix"),
|
||||
param1_name(param_prefix + ".my_param_1"),
|
||||
param2_name(param_prefix + ".my_param_2"),
|
||||
param3_name(param_prefix + ".my_param_3")
|
||||
{
|
||||
}
|
||||
|
||||
void SetUp(benchmark::State & state)
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
node = std::make_shared<rclcpp::Node>(node_name);
|
||||
|
||||
node->declare_parameter(param1_name);
|
||||
node->declare_parameter(param2_name);
|
||||
node->declare_parameter(param3_name);
|
||||
node->undeclare_parameter(param3_name);
|
||||
|
||||
performance_test_fixture::PerformanceTest::SetUp(state);
|
||||
}
|
||||
|
||||
void TearDown(benchmark::State & state)
|
||||
{
|
||||
performance_test_fixture::PerformanceTest::TearDown(state);
|
||||
|
||||
node.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
const std::string node_name;
|
||||
const std::string param_prefix;
|
||||
const std::string param1_name;
|
||||
const std::string param2_name;
|
||||
const std::string param3_name;
|
||||
|
||||
protected:
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, declare_undeclare)(benchmark::State & state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
node->declare_parameter(param3_name);
|
||||
node->undeclare_parameter(param3_name);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, has_parameter_hit)(benchmark::State & state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
if (!node->has_parameter(param1_name)) {
|
||||
state.SkipWithError("Parameter was expected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, has_parameter_miss)(benchmark::State & state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
if (node->has_parameter(param3_name)) {
|
||||
state.SkipWithError("Parameter was not expected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, set_parameters_bool)(benchmark::State & state)
|
||||
{
|
||||
const std::vector<rclcpp::Parameter> param_values1
|
||||
{
|
||||
rclcpp::Parameter(param1_name, true),
|
||||
rclcpp::Parameter(param2_name, false),
|
||||
};
|
||||
const std::vector<rclcpp::Parameter> param_values2
|
||||
{
|
||||
rclcpp::Parameter(param1_name, false),
|
||||
rclcpp::Parameter(param2_name, true),
|
||||
};
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : state) {
|
||||
node->set_parameters(param_values2);
|
||||
node->set_parameters(param_values1);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, set_parameters_atomically_bool)(benchmark::State & state)
|
||||
{
|
||||
const std::vector<rclcpp::Parameter> param_values1
|
||||
{
|
||||
rclcpp::Parameter(param1_name, true),
|
||||
rclcpp::Parameter(param2_name, false),
|
||||
};
|
||||
const std::vector<rclcpp::Parameter> param_values2
|
||||
{
|
||||
rclcpp::Parameter(param1_name, false),
|
||||
rclcpp::Parameter(param2_name, true),
|
||||
};
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : state) {
|
||||
node->set_parameters_atomically(param_values2);
|
||||
node->set_parameters_atomically(param_values1);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, set_parameters_callback_bool)(benchmark::State & state)
|
||||
{
|
||||
const std::vector<rclcpp::Parameter> param_values1
|
||||
{
|
||||
rclcpp::Parameter(param1_name, true),
|
||||
rclcpp::Parameter(param2_name, false),
|
||||
};
|
||||
const std::vector<rclcpp::Parameter> param_values2
|
||||
{
|
||||
rclcpp::Parameter(param1_name, false),
|
||||
rclcpp::Parameter(param2_name, true),
|
||||
};
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult callback_result;
|
||||
bool callback_received = false;
|
||||
callback_result.successful = true;
|
||||
auto callback =
|
||||
[&callback_result, &callback_received](const std::vector<rclcpp::Parameter> &) {
|
||||
callback_received = true;
|
||||
return callback_result;
|
||||
};
|
||||
auto handle = node->add_on_set_parameters_callback(callback);
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : state) {
|
||||
node->set_parameters(param_values2);
|
||||
node->set_parameters(param_values1);
|
||||
}
|
||||
|
||||
if (!callback_received) {
|
||||
state.SkipWithError("Callback is not functioning");
|
||||
}
|
||||
|
||||
node->remove_on_set_parameters_callback(handle.get());
|
||||
}
|
||||
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, set_parameters_string)(benchmark::State & state)
|
||||
{
|
||||
const std::vector<rclcpp::Parameter> param_values1
|
||||
{
|
||||
rclcpp::Parameter(param1_name, "param 1 value A"),
|
||||
rclcpp::Parameter(param2_name, "param 2 value B"),
|
||||
};
|
||||
const std::vector<rclcpp::Parameter> param_values2
|
||||
{
|
||||
rclcpp::Parameter(param1_name, "param 1 value B"),
|
||||
rclcpp::Parameter(param2_name, "param 2 value A"),
|
||||
};
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : state) {
|
||||
node->set_parameters(param_values2);
|
||||
node->set_parameters(param_values1);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, set_parameters_array)(benchmark::State & state)
|
||||
{
|
||||
const std::vector<rclcpp::Parameter> param_values1
|
||||
{
|
||||
rclcpp::Parameter(param1_name, std::vector<int> {0, 1, 2}),
|
||||
rclcpp::Parameter(param2_name, std::vector<int> {3, 4, 5}),
|
||||
};
|
||||
const std::vector<rclcpp::Parameter> param_values2
|
||||
{
|
||||
rclcpp::Parameter(param1_name, std::vector<int> {4, 5, 6}),
|
||||
rclcpp::Parameter(param2_name, std::vector<int> {7, 8, 9}),
|
||||
};
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : state) {
|
||||
node->set_parameters(param_values2);
|
||||
node->set_parameters(param_values1);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, get_parameter)(benchmark::State & state)
|
||||
{
|
||||
rclcpp::Parameter param1_value;
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : state) {
|
||||
node->get_parameter(param1_name, param1_value);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, list_parameters_hit)(benchmark::State & state)
|
||||
{
|
||||
rcl_interfaces::msg::ListParametersResult param_list;
|
||||
const std::vector<std::string> prefixes
|
||||
{
|
||||
param_prefix,
|
||||
};
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : state) {
|
||||
param_list = node->list_parameters(prefixes, 10);
|
||||
if (param_list.names.size() != 2) {
|
||||
state.SkipWithError("Expected node names");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(NodeParametersInterfaceTest, list_parameters_miss)(benchmark::State & state)
|
||||
{
|
||||
rcl_interfaces::msg::ListParametersResult param_list;
|
||||
const std::vector<std::string> prefixes
|
||||
{
|
||||
"your_param",
|
||||
};
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : state) {
|
||||
param_list = node->list_parameters(prefixes, 10);
|
||||
if (param_list.names.size() != 0) {
|
||||
state.SkipWithError("Expected no node names");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
324
rclcpp/test/benchmark/benchmark_parameter_client.cpp
Normal file
324
rclcpp/test/benchmark/benchmark_parameter_client.cpp
Normal file
@@ -0,0 +1,324 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "benchmark/benchmark.h"
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
class RemoteNodeTest : public benchmark::Fixture
|
||||
{
|
||||
public:
|
||||
RemoteNodeTest()
|
||||
: remote_node_name("my_remote_node")
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
|
||||
#endif
|
||||
void SetUp(benchmark::State &)
|
||||
{
|
||||
remote_context = std::make_shared<rclcpp::Context>();
|
||||
remote_context->init(0, nullptr, rclcpp::InitOptions().auto_initialize_logging(false));
|
||||
|
||||
rclcpp::ExecutorOptions exec_options;
|
||||
exec_options.context = remote_context;
|
||||
|
||||
remote_executor = std::make_shared<rclcpp::executors::SingleThreadedExecutor>(exec_options);
|
||||
|
||||
remote_node = std::make_shared<rclcpp::Node>(
|
||||
remote_node_name, rclcpp::NodeOptions().context(remote_context));
|
||||
remote_executor->add_node(remote_node);
|
||||
|
||||
remote_thread = std::thread(&rclcpp::executors::SingleThreadedExecutor::spin, remote_executor);
|
||||
}
|
||||
|
||||
void TearDown(benchmark::State &)
|
||||
{
|
||||
remote_executor->cancel();
|
||||
remote_context->shutdown("Test is complete");
|
||||
remote_thread.join();
|
||||
|
||||
remote_node.reset();
|
||||
remote_executor.reset();
|
||||
remote_context.reset();
|
||||
}
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
const std::string remote_node_name;
|
||||
|
||||
protected:
|
||||
rclcpp::Context::SharedPtr remote_context;
|
||||
rclcpp::executors::SingleThreadedExecutor::SharedPtr remote_executor;
|
||||
rclcpp::Node::SharedPtr remote_node;
|
||||
std::thread remote_thread;
|
||||
};
|
||||
|
||||
class ParameterClientTest : public RemoteNodeTest
|
||||
{
|
||||
public:
|
||||
ParameterClientTest()
|
||||
: node_name("my_node"),
|
||||
param_prefix("my_prefix"),
|
||||
param1_name(param_prefix + ".my_param_1"),
|
||||
param2_name(param_prefix + ".my_param_2"),
|
||||
param3_name(param_prefix + ".my_param_3")
|
||||
{
|
||||
}
|
||||
|
||||
void SetUp(benchmark::State & state)
|
||||
{
|
||||
RemoteNodeTest::SetUp(state);
|
||||
|
||||
rclcpp::init(0, nullptr);
|
||||
node = std::make_shared<rclcpp::Node>(node_name);
|
||||
|
||||
remote_node->declare_parameter(
|
||||
param1_name, rclcpp::ParameterValue("param1_value"));
|
||||
remote_node->declare_parameter(
|
||||
param2_name, rclcpp::ParameterValue(std::vector<int> {1, 2, 3}));
|
||||
remote_node->declare_parameter(param3_name);
|
||||
remote_node->undeclare_parameter(param3_name);
|
||||
|
||||
params_client = std::make_shared<rclcpp::SyncParametersClient>(node, remote_node_name);
|
||||
|
||||
if (!params_client->wait_for_service()) {
|
||||
state.SkipWithError("Client failed to become ready");
|
||||
}
|
||||
}
|
||||
|
||||
void TearDown(benchmark::State & state)
|
||||
{
|
||||
RemoteNodeTest::TearDown(state);
|
||||
|
||||
rclcpp::shutdown();
|
||||
node.reset();
|
||||
params_client.reset();
|
||||
}
|
||||
|
||||
const std::string node_name;
|
||||
const std::string param_prefix;
|
||||
const std::string param1_name;
|
||||
const std::string param2_name;
|
||||
const std::string param3_name;
|
||||
|
||||
protected:
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::SyncParametersClient::SharedPtr params_client;
|
||||
};
|
||||
|
||||
static bool result_is_successful(rcl_interfaces::msg::SetParametersResult result)
|
||||
{
|
||||
return result.successful;
|
||||
}
|
||||
|
||||
BENCHMARK_F(ParameterClientTest, create_destroy_client)(benchmark::State & state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
params_client.reset();
|
||||
params_client = std::make_shared<rclcpp::SyncParametersClient>(node, remote_node_name);
|
||||
if (!params_client->wait_for_service()) {
|
||||
state.SkipWithError("Client failed to become ready");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ParameterClientTest, has_parameter_hit)(benchmark::State & state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
if (!params_client->has_parameter(param1_name)) {
|
||||
state.SkipWithError("Parameter was expected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ParameterClientTest, has_parameter_miss)(benchmark::State & state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
if (params_client->has_parameter(param3_name)) {
|
||||
state.SkipWithError("Parameter was not expected");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ParameterClientTest, set_parameters_bool)(benchmark::State & state)
|
||||
{
|
||||
const std::vector<rclcpp::Parameter> param_values1
|
||||
{
|
||||
rclcpp::Parameter(param1_name, true),
|
||||
rclcpp::Parameter(param2_name, false),
|
||||
};
|
||||
const std::vector<rclcpp::Parameter> param_values2
|
||||
{
|
||||
rclcpp::Parameter(param1_name, false),
|
||||
rclcpp::Parameter(param2_name, true),
|
||||
};
|
||||
|
||||
for (auto _ : state) {
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult> results =
|
||||
params_client->set_parameters(param_values2);
|
||||
if (!std::all_of(results.begin(), results.end(), result_is_successful)) {
|
||||
state.SkipWithError("Failed to set one or more parameters");
|
||||
break;
|
||||
}
|
||||
|
||||
results = params_client->set_parameters(param_values1);
|
||||
if (!std::all_of(results.begin(), results.end(), result_is_successful)) {
|
||||
state.SkipWithError("Failed to set one or more parameters");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ParameterClientTest, set_parameters_atomically_bool)(benchmark::State & state)
|
||||
{
|
||||
const std::vector<rclcpp::Parameter> param_values1
|
||||
{
|
||||
rclcpp::Parameter(param1_name, true),
|
||||
rclcpp::Parameter(param2_name, false),
|
||||
};
|
||||
const std::vector<rclcpp::Parameter> param_values2
|
||||
{
|
||||
rclcpp::Parameter(param1_name, false),
|
||||
rclcpp::Parameter(param2_name, true),
|
||||
};
|
||||
|
||||
for (auto _ : state) {
|
||||
rcl_interfaces::msg::SetParametersResult result =
|
||||
params_client->set_parameters_atomically(param_values2);
|
||||
if (!result.successful) {
|
||||
state.SkipWithError(("Failed to set parameters: " + result.reason).c_str());
|
||||
break;
|
||||
}
|
||||
|
||||
result = params_client->set_parameters_atomically(param_values1);
|
||||
if (!result.successful) {
|
||||
state.SkipWithError(("Failed to set parameters: " + result.reason).c_str());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ParameterClientTest, set_parameters_string)(benchmark::State & state)
|
||||
{
|
||||
const std::vector<rclcpp::Parameter> param_values1
|
||||
{
|
||||
rclcpp::Parameter(param1_name, "param 1 value A"),
|
||||
rclcpp::Parameter(param2_name, "param 2 value B"),
|
||||
};
|
||||
const std::vector<rclcpp::Parameter> param_values2
|
||||
{
|
||||
rclcpp::Parameter(param1_name, "param 1 value B"),
|
||||
rclcpp::Parameter(param2_name, "param 2 value A"),
|
||||
};
|
||||
|
||||
for (auto _ : state) {
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult> results =
|
||||
params_client->set_parameters(param_values2);
|
||||
if (!std::all_of(results.begin(), results.end(), result_is_successful)) {
|
||||
state.SkipWithError("Failed to set one or more parameters");
|
||||
break;
|
||||
}
|
||||
|
||||
results = params_client->set_parameters(param_values1);
|
||||
if (!std::all_of(results.begin(), results.end(), result_is_successful)) {
|
||||
state.SkipWithError("Failed to set one or more parameters");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ParameterClientTest, set_parameters_array)(benchmark::State & state)
|
||||
{
|
||||
const std::vector<rclcpp::Parameter> param_values1
|
||||
{
|
||||
rclcpp::Parameter(param1_name, std::vector<int> {0, 1, 2}),
|
||||
rclcpp::Parameter(param2_name, std::vector<int> {3, 4, 5}),
|
||||
};
|
||||
const std::vector<rclcpp::Parameter> param_values2
|
||||
{
|
||||
rclcpp::Parameter(param1_name, std::vector<int> {4, 5, 6}),
|
||||
rclcpp::Parameter(param2_name, std::vector<int> {7, 8, 9}),
|
||||
};
|
||||
|
||||
for (auto _ : state) {
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult> results =
|
||||
params_client->set_parameters(param_values2);
|
||||
if (!std::all_of(results.begin(), results.end(), result_is_successful)) {
|
||||
state.SkipWithError("Failed to set one or more parameters");
|
||||
break;
|
||||
}
|
||||
|
||||
results = params_client->set_parameters(param_values1);
|
||||
if (!std::all_of(results.begin(), results.end(), result_is_successful)) {
|
||||
state.SkipWithError("Failed to set one or more parameters");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ParameterClientTest, get_parameters)(benchmark::State & state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
std::vector<rclcpp::Parameter> results = params_client->get_parameters({param1_name});
|
||||
if (results.size() != 1 || results[0].get_name() != param1_name) {
|
||||
state.SkipWithError("Got the wrong parameter(s)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ParameterClientTest, list_parameters_hit)(benchmark::State & state)
|
||||
{
|
||||
const std::vector<std::string> prefixes
|
||||
{
|
||||
param_prefix,
|
||||
};
|
||||
|
||||
for (auto _ : state) {
|
||||
rcl_interfaces::msg::ListParametersResult param_list =
|
||||
params_client->list_parameters(prefixes, 10);
|
||||
if (param_list.names.size() != 2) {
|
||||
state.SkipWithError("Expected parameters");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ParameterClientTest, list_parameters_miss)(benchmark::State & state)
|
||||
{
|
||||
const std::vector<std::string> prefixes
|
||||
{
|
||||
"your_prefix",
|
||||
};
|
||||
|
||||
for (auto _ : state) {
|
||||
rcl_interfaces::msg::ListParametersResult param_list =
|
||||
params_client->list_parameters(prefixes, 10);
|
||||
if (param_list.names.size() != 0) {
|
||||
state.SkipWithError("Expected no parameters");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
141
rclcpp/test/benchmark/benchmark_service.cpp
Normal file
141
rclcpp/test/benchmark/benchmark_service.cpp
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "performance_test_fixture/performance_test_fixture.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
|
||||
using performance_test_fixture::PerformanceTest;
|
||||
|
||||
constexpr char empty_service_name[] = "empty_service";
|
||||
|
||||
class ServicePerformanceTest : public PerformanceTest
|
||||
{
|
||||
public:
|
||||
ServicePerformanceTest()
|
||||
: callback_count(0) {}
|
||||
|
||||
void SetUp(benchmark::State & state)
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
node = std::make_unique<rclcpp::Node>("node", "ns");
|
||||
empty_client = node->create_client<test_msgs::srv::Empty>(empty_service_name);
|
||||
callback_count = 0;
|
||||
|
||||
performance_test_fixture::PerformanceTest::SetUp(state);
|
||||
}
|
||||
|
||||
void TearDown(benchmark::State & state)
|
||||
{
|
||||
performance_test_fixture::PerformanceTest::TearDown(state);
|
||||
|
||||
empty_client.reset();
|
||||
node.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void ServiceCallback(
|
||||
const test_msgs::srv::Empty::Request::SharedPtr,
|
||||
test_msgs::srv::Empty::Response::SharedPtr)
|
||||
{
|
||||
callback_count++;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::unique_ptr<rclcpp::Node> node;
|
||||
std::shared_ptr<rclcpp::Client<test_msgs::srv::Empty>> empty_client;
|
||||
int callback_count;
|
||||
};
|
||||
|
||||
BENCHMARK_F(ServicePerformanceTest, construct_service_no_client)(benchmark::State & state) {
|
||||
auto callback = std::bind(
|
||||
&ServicePerformanceTest::ServiceCallback,
|
||||
this, std::placeholders::_1, std::placeholders::_2);
|
||||
|
||||
auto outer_service = node->create_service<test_msgs::srv::Empty>("not_a_service", callback);
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
auto service = node->create_service<test_msgs::srv::Empty>("not_a_service", callback);
|
||||
benchmark::DoNotOptimize(service);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
state.PauseTiming();
|
||||
service.reset();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ServicePerformanceTest, construct_service_empty_srv)(benchmark::State & state) {
|
||||
auto callback = std::bind(
|
||||
&ServicePerformanceTest::ServiceCallback,
|
||||
this, std::placeholders::_1, std::placeholders::_2);
|
||||
auto outer_service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
auto service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
|
||||
benchmark::DoNotOptimize(service);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
state.PauseTiming();
|
||||
service.reset();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ServicePerformanceTest, destroy_service_empty_srv)(benchmark::State & state) {
|
||||
auto callback = std::bind(
|
||||
&ServicePerformanceTest::ServiceCallback,
|
||||
this, std::placeholders::_1, std::placeholders::_2);
|
||||
auto outer_service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
auto service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
|
||||
state.ResumeTiming();
|
||||
benchmark::DoNotOptimize(service);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
service.reset();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ServicePerformanceTest, async_send_response)(benchmark::State & state) {
|
||||
auto callback = std::bind(
|
||||
&ServicePerformanceTest::ServiceCallback,
|
||||
this, std::placeholders::_1, std::placeholders::_2);
|
||||
auto service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
// Clear executor queue
|
||||
rclcpp::spin_some(node->get_node_base_interface());
|
||||
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
auto future = empty_client->async_send_request(request);
|
||||
state.ResumeTiming();
|
||||
benchmark::DoNotOptimize(service);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
rclcpp::spin_until_future_complete(node->get_node_base_interface(), future);
|
||||
}
|
||||
if (callback_count == 0) {
|
||||
state.SkipWithError("Service callback was not called");
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,17 @@
|
||||
add_subdirectory(benchmark)
|
||||
|
||||
find_package(ament_cmake_gtest REQUIRED)
|
||||
|
||||
find_package(rmw_implementation_cmake REQUIRED)
|
||||
|
||||
add_definitions(-DTEST_RESOURCES_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/../resources")
|
||||
|
||||
rosidl_generate_interfaces(${PROJECT_NAME}_test_msgs
|
||||
../msg/Header.msg
|
||||
../msg/MessageWithHeader.msg
|
||||
DEPENDENCIES builtin_interfaces
|
||||
LIBRARY_NAME ${PROJECT_NAME}
|
||||
SKIP_INSTALL
|
||||
)
|
||||
|
||||
ament_add_gtest(
|
||||
test_allocator_common
|
||||
allocator/test_allocator_common.cpp)
|
||||
@@ -363,6 +369,18 @@ if(TARGET test_qos_event)
|
||||
mimick
|
||||
)
|
||||
endif()
|
||||
ament_add_gmock(test_qos_overriding_options test_qos_overriding_options.cpp)
|
||||
if(TARGET test_qos_overriding_options)
|
||||
target_link_libraries(test_qos_overriding_options
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
ament_add_gmock(test_qos_parameters test_qos_parameters.cpp)
|
||||
if(TARGET test_qos_parameters)
|
||||
target_link_libraries(test_qos_parameters
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_rate test_rate.cpp)
|
||||
if(TARGET test_rate)
|
||||
ament_target_dependencies(test_rate
|
||||
|
||||
@@ -23,7 +23,9 @@ TEST(TestAllocatorCommon, retyped_allocate) {
|
||||
void * untyped_allocator = &allocator;
|
||||
void * allocated_mem =
|
||||
rclcpp::allocator::retyped_allocate<std::allocator<char>>(1u, untyped_allocator);
|
||||
ASSERT_NE(nullptr, allocated_mem);
|
||||
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
|
||||
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
|
||||
ASSERT_TRUE(nullptr != allocated_mem);
|
||||
|
||||
auto code = [&untyped_allocator, allocated_mem]() {
|
||||
rclcpp::allocator::retyped_deallocate<int, std::allocator<int>>(
|
||||
@@ -32,11 +34,15 @@ TEST(TestAllocatorCommon, retyped_allocate) {
|
||||
EXPECT_NO_THROW(code());
|
||||
|
||||
allocated_mem = allocator.allocate(1);
|
||||
ASSERT_NE(nullptr, allocated_mem);
|
||||
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
|
||||
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
|
||||
ASSERT_TRUE(nullptr != allocated_mem);
|
||||
void * reallocated_mem =
|
||||
rclcpp::allocator::retyped_reallocate<int, std::allocator<int>>(
|
||||
allocated_mem, 2u, untyped_allocator);
|
||||
ASSERT_NE(nullptr, reallocated_mem);
|
||||
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
|
||||
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
|
||||
ASSERT_TRUE(nullptr != reallocated_mem);
|
||||
|
||||
auto code2 = [&untyped_allocator, reallocated_mem]() {
|
||||
rclcpp::allocator::retyped_deallocate<int, std::allocator<int>>(
|
||||
|
||||
@@ -38,7 +38,9 @@ TEST(TestAllocatorDeleter, construct_destruct) {
|
||||
TEST(TestAllocatorDeleter, delete) {
|
||||
std::allocator<int> allocator;
|
||||
int * some_mem = allocator.allocate(1u);
|
||||
ASSERT_NE(nullptr, some_mem);
|
||||
// The more natural check here is ASSERT_NE(nullptr, ptr), but clang static
|
||||
// analysis throws a false-positive memory leak warning. Use ASSERT_TRUE instead.
|
||||
ASSERT_TRUE(nullptr != some_mem);
|
||||
|
||||
rclcpp::allocator::AllocatorDeleter<std::allocator<int>> deleter(&allocator);
|
||||
EXPECT_NO_THROW(deleter(some_mem));
|
||||
|
||||
@@ -1,29 +0,0 @@
|
||||
# Give cppcheck hints about macro definitions coming from outside this package
|
||||
get_target_property(
|
||||
ament_cmake_cppcheck_ADDITIONAL_INCLUDE_DIRS
|
||||
performance_test_fixture::performance_test_fixture
|
||||
INTERFACE_INCLUDE_DIRECTORIES)
|
||||
|
||||
function(add_rclcpp_benchmark NAME SOURCE)
|
||||
cmake_parse_arguments(RCLCPP_BENCHMARK "" "LIBRARIES" "")
|
||||
if(RCLCPP_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unrecognized arguments for 'add_rclcpp_benchmark' (${RCLCPP_UNPARSED_ARGUMENTS})")
|
||||
return()
|
||||
endif()
|
||||
find_package(${rmw_implementation} REQUIRED)
|
||||
message(STATUS "Adding ${NAME} for '${rmw_implementation}'")
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
|
||||
set(full_benchmark_name ${NAME}${target_suffix})
|
||||
add_performance_test(${full_benchmark_name} ${SOURCE})
|
||||
if(TARGET ${full_benchmark_name})
|
||||
target_link_libraries(${full_benchmark_name} ${PROJECT_NAME} ${LIBRARIES})
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Add new benchmarks inside this macro
|
||||
macro(rclcpp_benchmarks)
|
||||
add_rclcpp_benchmark(benchmark_client benchmark_client.cpp)
|
||||
endmacro()
|
||||
|
||||
call_for_each_rmw_implementation(rclcpp_benchmarks)
|
||||
@@ -1,13 +0,0 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
@@ -115,9 +115,9 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
// TYPED_TEST_CASE is deprecated as of gtest 1.9, use TYPED_TEST_SUITE when gtest dependency
|
||||
// TYPED_TEST_SUITE is deprecated as of gtest 1.9, use TYPED_TEST_SUITE when gtest dependency
|
||||
// is updated.
|
||||
TYPED_TEST_CASE(TestExecutors, ExecutorTypes, ExecutorTypeNames);
|
||||
TYPED_TEST_SUITE(TestExecutors, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
// StaticSingleThreadedExecutor is not included in these tests for now, due to:
|
||||
// https://github.com/ros2/rclcpp/issues/1219
|
||||
@@ -125,7 +125,7 @@ using StandardExecutors =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor>;
|
||||
TYPED_TEST_CASE(TestExecutorsStable, StandardExecutors, ExecutorTypeNames);
|
||||
TYPED_TEST_SUITE(TestExecutorsStable, StandardExecutors, ExecutorTypeNames);
|
||||
|
||||
// Make sure that executors detach from nodes when destructing
|
||||
TYPED_TEST(TestExecutors, detachOnDestruction) {
|
||||
@@ -436,9 +436,16 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
execute() override
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -235,7 +235,16 @@ public:
|
||||
|
||||
bool is_ready(rcl_wait_set_t *) override {return true;}
|
||||
|
||||
void execute() override {}
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override
|
||||
{
|
||||
(void) data;
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestStaticExecutorEntitiesCollector, add_remove_node_with_entities) {
|
||||
@@ -363,8 +372,9 @@ TEST_F(TestStaticExecutorEntitiesCollector, prepare_wait_set_rcl_wait_set_clear_
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
|
||||
std::shared_ptr<void> data = entities_collector_->take_data();
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->execute(),
|
||||
entities_collector_->execute(data),
|
||||
std::runtime_error("Couldn't clear wait set"));
|
||||
}
|
||||
|
||||
@@ -393,8 +403,9 @@ TEST_F(TestStaticExecutorEntitiesCollector, prepare_wait_set_rcl_wait_set_resize
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_resize, RCL_RET_ERROR);
|
||||
std::shared_ptr<void> data = entities_collector_->take_data();
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
entities_collector_->execute(),
|
||||
entities_collector_->execute(data),
|
||||
std::runtime_error("Couldn't resize the wait set: error not set"));
|
||||
}
|
||||
|
||||
@@ -616,7 +627,8 @@ TEST_F(
|
||||
RCLCPP_SCOPE_EXIT(entities_collector_->fini());
|
||||
|
||||
cb_group->get_associated_with_executor_atomic().exchange(false);
|
||||
entities_collector_->execute();
|
||||
std::shared_ptr<void> data = entities_collector_->take_data();
|
||||
entities_collector_->execute(data);
|
||||
|
||||
EXPECT_TRUE(entities_collector_->remove_node(node->get_node_base_interface()));
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/node_options.h"
|
||||
#include "rclcpp/node.hpp"
|
||||
@@ -55,7 +56,9 @@ public:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
|
||||
node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
rclcpp::NodeOptions options{};
|
||||
options.arguments(std::vector<std::string>{"-r", "foo:=bar"});
|
||||
node = std::make_shared<rclcpp::Node>("node", "ns", options);
|
||||
|
||||
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
|
||||
// the proper type is being tested and covered.
|
||||
@@ -129,3 +132,13 @@ TEST_F(TestNodeService, add_client_rcl_trigger_guard_condition_error)
|
||||
node_services->add_client(client, callback_group),
|
||||
std::runtime_error("Failed to notify wait set on client creation: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeService, resolve_service_name)
|
||||
{
|
||||
EXPECT_EQ("/ns/bar", node_services->resolve_service_name("foo", false));
|
||||
EXPECT_EQ("/ns/foo", node_services->resolve_service_name("foo", true));
|
||||
EXPECT_EQ("/foo", node_services->resolve_service_name("/foo", true));
|
||||
EXPECT_THROW(
|
||||
node_services->resolve_service_name("this is not a valid name!~>", true),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/node_options.h"
|
||||
#include "rclcpp/node.hpp"
|
||||
@@ -80,7 +81,9 @@ public:
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
rclcpp::NodeOptions options{};
|
||||
options.arguments(std::vector<std::string>{"-r", "foo:=bar"});
|
||||
node = std::make_shared<rclcpp::Node>("node", "ns", options);
|
||||
|
||||
// This dynamic cast is not necessary for the unittest itself, but instead is used to ensure
|
||||
// the proper type is being tested and covered.
|
||||
@@ -154,3 +157,13 @@ TEST_F(TestNodeTopics, add_subscription_rcl_trigger_guard_condition_error)
|
||||
node_topics->add_subscription(subscription, callback_group),
|
||||
std::runtime_error("failed to notify wait set on subscription creation: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestNodeTopics, resolve_topic_name)
|
||||
{
|
||||
EXPECT_EQ("/ns/bar", node_topics->resolve_topic_name("foo", false));
|
||||
EXPECT_EQ("/ns/foo", node_topics->resolve_topic_name("foo", true));
|
||||
EXPECT_EQ("/foo", node_topics->resolve_topic_name("/foo", true));
|
||||
EXPECT_THROW(
|
||||
node_topics->resolve_topic_name("this is not a valid name!~>", true),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
@@ -30,7 +30,17 @@ class TestWaitable : public rclcpp::Waitable
|
||||
public:
|
||||
bool add_to_wait_set(rcl_wait_set_t *) override {return false;}
|
||||
bool is_ready(rcl_wait_set_t *) override {return false;}
|
||||
void execute() override {}
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void execute(std::shared_ptr<void> & data) override
|
||||
{
|
||||
(void) data;
|
||||
}
|
||||
};
|
||||
|
||||
class TestNodeWaitables : public ::testing::Test
|
||||
|
||||
@@ -49,7 +49,16 @@ public:
|
||||
return test_waitable_result;
|
||||
}
|
||||
|
||||
void execute() override {}
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void execute(std::shared_ptr<void> & data) override
|
||||
{
|
||||
(void) data;
|
||||
}
|
||||
};
|
||||
|
||||
struct RclWaitSetSizes
|
||||
|
||||
@@ -79,7 +79,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
TYPED_TEST_CASE(TestAddCallbackGroupsToExecutor, ExecutorTypes, ExecutorTypeNames);
|
||||
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutor, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
/*
|
||||
* Test adding callback groups.
|
||||
|
||||
@@ -50,6 +50,34 @@ TEST_F(TestCreateSubscription, create) {
|
||||
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
|
||||
}
|
||||
|
||||
TEST_F(TestCreateSubscription, create_with_overriding_options) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
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 subscription =
|
||||
rclcpp::create_subscription<test_msgs::msg::Empty>(node, "topic_name", qos, callback, options);
|
||||
|
||||
ASSERT_NE(nullptr, subscription);
|
||||
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
|
||||
}
|
||||
|
||||
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 node_parameters = node->get_node_parameters_interface();
|
||||
auto node_topics = node->get_node_topics_interface();
|
||||
auto subscription = rclcpp::create_subscription<test_msgs::msg::Empty>(
|
||||
node_parameters, node_topics, "topic_name", qos, callback, options);
|
||||
|
||||
ASSERT_NE(nullptr, subscription);
|
||||
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
|
||||
}
|
||||
|
||||
TEST_F(TestCreateSubscription, create_with_statistics) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
const rclcpp::QoS qos(10);
|
||||
|
||||
@@ -68,7 +68,7 @@ TEST_F(TestDuration, operators) {
|
||||
TEST_F(TestDuration, chrono_overloads) {
|
||||
int64_t ns = 123456789l;
|
||||
auto chrono_ns = std::chrono::nanoseconds(ns);
|
||||
auto d1 = rclcpp::Duration(ns);
|
||||
auto d1 = rclcpp::Duration::from_nanoseconds(ns);
|
||||
auto d2 = rclcpp::Duration(chrono_ns);
|
||||
auto d3 = rclcpp::Duration(123456789ns);
|
||||
EXPECT_EQ(d1, d2);
|
||||
@@ -85,11 +85,11 @@ TEST_F(TestDuration, chrono_overloads) {
|
||||
}
|
||||
|
||||
TEST_F(TestDuration, overflows) {
|
||||
rclcpp::Duration max(std::numeric_limits<rcl_duration_value_t>::max());
|
||||
rclcpp::Duration min(std::numeric_limits<rcl_duration_value_t>::min());
|
||||
auto max = rclcpp::Duration::from_nanoseconds(std::numeric_limits<rcl_duration_value_t>::max());
|
||||
auto min = rclcpp::Duration::from_nanoseconds(std::numeric_limits<rcl_duration_value_t>::min());
|
||||
|
||||
rclcpp::Duration one(1);
|
||||
rclcpp::Duration negative_one(-1);
|
||||
rclcpp::Duration one(1ns);
|
||||
rclcpp::Duration negative_one(-1ns);
|
||||
|
||||
EXPECT_THROW(max + one, std::overflow_error);
|
||||
EXPECT_THROW(min - one, std::underflow_error);
|
||||
@@ -106,7 +106,7 @@ TEST_F(TestDuration, overflows) {
|
||||
}
|
||||
|
||||
TEST_F(TestDuration, negative_duration) {
|
||||
rclcpp::Duration assignable_duration = rclcpp::Duration(0) - rclcpp::Duration(5, 0);
|
||||
rclcpp::Duration assignable_duration = rclcpp::Duration(0ns) - rclcpp::Duration(5, 0);
|
||||
|
||||
{
|
||||
// avoid windows converting a literal number less than -INT_MAX to unsigned int C4146
|
||||
@@ -140,22 +140,52 @@ static const int64_t ONE_SEC_IN_NS = 1000 * 1000 * 1000;
|
||||
static const int64_t ONE_AND_HALF_SEC_IN_NS = 3 * HALF_SEC_IN_NS;
|
||||
|
||||
TEST_F(TestDuration, from_seconds) {
|
||||
EXPECT_EQ(rclcpp::Duration(0), rclcpp::Duration::from_seconds(0.0));
|
||||
EXPECT_EQ(rclcpp::Duration(0), rclcpp::Duration::from_seconds(0));
|
||||
EXPECT_EQ(rclcpp::Duration(0ns), rclcpp::Duration::from_seconds(0.0));
|
||||
EXPECT_EQ(rclcpp::Duration(0ns), rclcpp::Duration::from_seconds(0));
|
||||
EXPECT_EQ(rclcpp::Duration(1, HALF_SEC_IN_NS), rclcpp::Duration::from_seconds(1.5));
|
||||
EXPECT_EQ(rclcpp::Duration(-ONE_AND_HALF_SEC_IN_NS), rclcpp::Duration::from_seconds(-1.5));
|
||||
EXPECT_EQ(
|
||||
rclcpp::Duration::from_nanoseconds(-ONE_AND_HALF_SEC_IN_NS),
|
||||
rclcpp::Duration::from_seconds(-1.5));
|
||||
}
|
||||
|
||||
TEST_F(TestDuration, from_rmw_time) {
|
||||
constexpr auto max_rcl_duration = std::numeric_limits<rcl_duration_value_t>::max();
|
||||
{
|
||||
rmw_time_t rmw_duration{};
|
||||
rmw_duration.sec = RCL_NS_TO_S(max_rcl_duration) + 1uLL;
|
||||
EXPECT_EQ(rclcpp::Duration::from_rmw_time(rmw_duration).nanoseconds(), max_rcl_duration);
|
||||
}
|
||||
{
|
||||
rmw_time_t rmw_duration{};
|
||||
rmw_duration.nsec = max_rcl_duration + 1uLL;
|
||||
EXPECT_EQ(rclcpp::Duration::from_rmw_time(rmw_duration).nanoseconds(), max_rcl_duration);
|
||||
}
|
||||
{
|
||||
rmw_time_t rmw_duration{};
|
||||
rmw_duration.nsec = max_rcl_duration;
|
||||
rmw_duration.sec = RCL_NS_TO_S(max_rcl_duration);
|
||||
EXPECT_EQ(rclcpp::Duration::from_rmw_time(rmw_duration).nanoseconds(), max_rcl_duration);
|
||||
}
|
||||
{
|
||||
rmw_time_t rmw_duration{};
|
||||
rmw_duration.sec = 1u;
|
||||
rmw_duration.nsec = 1000u;
|
||||
EXPECT_EQ(
|
||||
rclcpp::Duration::from_rmw_time(rmw_duration).nanoseconds(),
|
||||
static_cast<rcl_duration_value_t>(RCL_S_TO_NS(rmw_duration.sec) + rmw_duration.nsec));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestDuration, std_chrono_constructors) {
|
||||
EXPECT_EQ(rclcpp::Duration(0), rclcpp::Duration(0.0s));
|
||||
EXPECT_EQ(rclcpp::Duration(0), rclcpp::Duration(0s));
|
||||
EXPECT_EQ(rclcpp::Duration(0ns), rclcpp::Duration(0.0s));
|
||||
EXPECT_EQ(rclcpp::Duration(0ns), rclcpp::Duration(0s));
|
||||
EXPECT_EQ(rclcpp::Duration(1, HALF_SEC_IN_NS), rclcpp::Duration(1.5s));
|
||||
EXPECT_EQ(rclcpp::Duration(-1, 0), rclcpp::Duration(-1s));
|
||||
}
|
||||
|
||||
TEST_F(TestDuration, conversions) {
|
||||
{
|
||||
const rclcpp::Duration duration(HALF_SEC_IN_NS);
|
||||
auto duration = rclcpp::Duration::from_nanoseconds(HALF_SEC_IN_NS);
|
||||
const auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
|
||||
EXPECT_EQ(duration_msg.sec, 0);
|
||||
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
|
||||
@@ -170,7 +200,7 @@ TEST_F(TestDuration, conversions) {
|
||||
}
|
||||
|
||||
{
|
||||
const rclcpp::Duration duration(ONE_SEC_IN_NS);
|
||||
auto duration = rclcpp::Duration::from_nanoseconds(ONE_SEC_IN_NS);
|
||||
const auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
|
||||
EXPECT_EQ(duration_msg.sec, 1);
|
||||
EXPECT_EQ(duration_msg.nanosec, 0u);
|
||||
@@ -185,7 +215,7 @@ TEST_F(TestDuration, conversions) {
|
||||
}
|
||||
|
||||
{
|
||||
const rclcpp::Duration duration(ONE_AND_HALF_SEC_IN_NS);
|
||||
auto duration = rclcpp::Duration::from_nanoseconds(ONE_AND_HALF_SEC_IN_NS);
|
||||
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
|
||||
EXPECT_EQ(duration_msg.sec, 1);
|
||||
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
|
||||
@@ -200,7 +230,7 @@ TEST_F(TestDuration, conversions) {
|
||||
}
|
||||
|
||||
{
|
||||
rclcpp::Duration duration(-HALF_SEC_IN_NS);
|
||||
auto duration = rclcpp::Duration::from_nanoseconds(-HALF_SEC_IN_NS);
|
||||
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
|
||||
EXPECT_EQ(duration_msg.sec, -1);
|
||||
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
|
||||
@@ -213,7 +243,7 @@ TEST_F(TestDuration, conversions) {
|
||||
}
|
||||
|
||||
{
|
||||
rclcpp::Duration duration(-ONE_SEC_IN_NS);
|
||||
auto duration = rclcpp::Duration::from_nanoseconds(-ONE_SEC_IN_NS);
|
||||
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
|
||||
EXPECT_EQ(duration_msg.sec, -1);
|
||||
EXPECT_EQ(duration_msg.nanosec, 0u);
|
||||
@@ -226,7 +256,7 @@ TEST_F(TestDuration, conversions) {
|
||||
}
|
||||
|
||||
{
|
||||
rclcpp::Duration duration(-ONE_AND_HALF_SEC_IN_NS);
|
||||
auto duration = rclcpp::Duration::from_nanoseconds(-ONE_AND_HALF_SEC_IN_NS);
|
||||
auto duration_msg = static_cast<builtin_interfaces::msg::Duration>(duration);
|
||||
EXPECT_EQ(duration_msg.sec, -2);
|
||||
EXPECT_EQ(duration_msg.nanosec, HALF_SEC_IN_NS);
|
||||
@@ -253,9 +283,10 @@ TEST_F(TestDuration, test_some_constructors) {
|
||||
}
|
||||
|
||||
TEST_F(TestDuration, test_some_exceptions) {
|
||||
rclcpp::Duration test_duration(0u);
|
||||
rclcpp::Duration test_duration(0ns);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_duration = rclcpp::Duration(INT64_MAX) - rclcpp::Duration(-1),
|
||||
test_duration =
|
||||
rclcpp::Duration::from_nanoseconds(INT64_MAX) - rclcpp::Duration(-1ns),
|
||||
std::overflow_error("duration subtraction leads to int64_t overflow"));
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_duration = test_duration * (std::numeric_limits<double>::infinity()),
|
||||
|
||||
@@ -90,10 +90,9 @@ MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcutils_allocator_t, >)
|
||||
TEST_F(TestExecutor, constructor_bad_guard_condition_init) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_guard_condition_init, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
EXPECT_THROW(
|
||||
new DummyExecutor(),
|
||||
std::runtime_error(
|
||||
"Failed to create interrupt guard condition in Executor constructor: error not set"));
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
TEST_F(TestExecutor, constructor_bad_wait_set_init) {
|
||||
|
||||
@@ -125,16 +125,14 @@ public:
|
||||
this->run();
|
||||
}
|
||||
|
||||
void mock_start_thread()
|
||||
void mock_init_wait_set()
|
||||
{
|
||||
// This function prepares the loop thread to be run, but leave
|
||||
// early with the failure thrown. That way the graph_listener wait_set
|
||||
// is init, without being started
|
||||
auto mock_wait_set_init = mocking_utils::inject_on_return(
|
||||
"lib:rclcpp", rcl_wait_set_init, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
this->start_if_not_started(),
|
||||
std::runtime_error("failed to initialize wait set: error not set"));
|
||||
this->init_wait_set();
|
||||
}
|
||||
|
||||
void mock_cleanup_wait_set()
|
||||
{
|
||||
this->cleanup_wait_set();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -145,14 +143,16 @@ TEST_F(TestGraphListener, error_run_graph_listener_destroy_context) {
|
||||
auto graph_listener_error =
|
||||
std::make_shared<TestGraphListenerProtectedMethods>(context_to_destroy);
|
||||
context_to_destroy.reset();
|
||||
EXPECT_NO_THROW(graph_listener_error->run_protected());
|
||||
EXPECT_THROW(
|
||||
graph_listener_error->run_protected(),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
TEST_F(TestGraphListener, error_run_graph_listener_mock_wait_set_clear) {
|
||||
auto global_context = rclcpp::contexts::get_global_default_context();
|
||||
auto graph_listener_test =
|
||||
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
|
||||
graph_listener_test->mock_start_thread();
|
||||
graph_listener_test->mock_init_wait_set();
|
||||
auto mock_wait_set_clear = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
@@ -164,7 +164,7 @@ TEST_F(TestGraphListener, error_run_graph_listener_mock_wait_set_add_guard_condi
|
||||
auto global_context = rclcpp::contexts::get_global_default_context();
|
||||
auto graph_listener_test =
|
||||
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
|
||||
graph_listener_test->mock_start_thread();
|
||||
graph_listener_test->mock_init_wait_set();
|
||||
auto mock_wait_set_clear = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_wait_set_add_guard_condition, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
@@ -172,31 +172,11 @@ TEST_F(TestGraphListener, error_run_graph_listener_mock_wait_set_add_guard_condi
|
||||
std::runtime_error("failed to add interrupt guard condition to wait set: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestGraphListener, error_run_graph_listener_mock_wait_set_add_guard_condition_twice) {
|
||||
auto global_context = rclcpp::contexts::get_global_default_context();
|
||||
auto graph_listener_test =
|
||||
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
|
||||
graph_listener_test->mock_start_thread();
|
||||
auto mock = mocking_utils::patch(
|
||||
"lib:rclcpp", rcl_wait_set_add_guard_condition, [](auto, ...) {
|
||||
static int counter = 1;
|
||||
if (counter == 1) {
|
||||
counter++;
|
||||
return RCL_RET_OK;
|
||||
} else {
|
||||
return RCL_RET_ERROR;
|
||||
}
|
||||
});
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
graph_listener_test->run_protected(),
|
||||
std::runtime_error("failed to add shutdown guard condition to wait set: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestGraphListener, error_run_graph_listener_mock_wait_error) {
|
||||
auto global_context = rclcpp::contexts::get_global_default_context();
|
||||
auto graph_listener_test =
|
||||
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
|
||||
graph_listener_test->mock_start_thread();
|
||||
graph_listener_test->mock_init_wait_set();
|
||||
auto mock_wait_set_clear = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_wait, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
@@ -208,7 +188,7 @@ TEST_F(TestGraphListener, error_run_graph_listener_mock_wait_timeout) {
|
||||
auto global_context = rclcpp::contexts::get_global_default_context();
|
||||
auto graph_listener_test =
|
||||
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
|
||||
graph_listener_test->mock_start_thread();
|
||||
graph_listener_test->mock_init_wait_set();
|
||||
auto mock_wait_set_clear = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_wait, RCL_RET_TIMEOUT);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
@@ -267,27 +247,54 @@ TEST_F(TestGraphListener, test_errors_graph_listener_add_remove_node) {
|
||||
|
||||
/* Shutdown errors */
|
||||
TEST_F(TestGraphListener, test_graph_listener_shutdown_wait_fini_error_nothrow) {
|
||||
graph_listener()->start_if_not_started();
|
||||
auto mock_wait_set_fini = mocking_utils::inject_on_return(
|
||||
"lib:rclcpp", rcl_wait_set_fini, RCL_RET_ERROR);
|
||||
// Exception is logged when using nothrow_t
|
||||
EXPECT_NO_THROW(graph_listener()->shutdown(std::nothrow_t()));
|
||||
auto global_context = rclcpp::contexts::get_global_default_context();
|
||||
auto graph_listener_test =
|
||||
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
|
||||
|
||||
graph_listener_test->start_if_not_started();
|
||||
|
||||
{
|
||||
auto mock_wait_set_fini = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_wait_set_fini, RCL_RET_ERROR);
|
||||
// Exception is logged when using nothrow_t
|
||||
EXPECT_NO_THROW(graph_listener_test->shutdown(std::nothrow_t()));
|
||||
}
|
||||
|
||||
graph_listener_test->mock_cleanup_wait_set();
|
||||
}
|
||||
|
||||
TEST_F(TestGraphListener, test_graph_listener_shutdown_wait_fini_error_throw) {
|
||||
auto global_context = rclcpp::contexts::get_global_default_context();
|
||||
auto graph_listener_test =
|
||||
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
|
||||
|
||||
graph_listener()->start_if_not_started();
|
||||
auto mock_wait_set_fini = mocking_utils::inject_on_return(
|
||||
"lib:rclcpp", rcl_wait_set_fini, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
graph_listener()->shutdown(),
|
||||
std::runtime_error("failed to finalize wait set: error not set"));
|
||||
|
||||
{
|
||||
auto mock_wait_set_fini = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_wait_set_fini, RCL_RET_ERROR);
|
||||
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
graph_listener()->shutdown(),
|
||||
std::runtime_error("failed to finalize wait set: error not set"));
|
||||
}
|
||||
|
||||
graph_listener_test->mock_cleanup_wait_set();
|
||||
}
|
||||
|
||||
TEST_F(TestGraphListener, test_graph_listener_shutdown_guard_fini_error_throw) {
|
||||
graph_listener()->start_if_not_started();
|
||||
auto mock_wait_set_fini = mocking_utils::inject_on_return(
|
||||
auto global_context = rclcpp::contexts::get_global_default_context();
|
||||
auto graph_listener_test =
|
||||
std::make_shared<TestGraphListenerProtectedMethods>(global_context);
|
||||
|
||||
graph_listener_test->start_if_not_started();
|
||||
|
||||
auto mock_wait_set_fini = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_guard_condition_fini, RCL_RET_ERROR);
|
||||
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
graph_listener()->shutdown(),
|
||||
graph_listener_test->shutdown(),
|
||||
std::runtime_error("failed to finalize interrupt guard condition: error not set"));
|
||||
|
||||
graph_listener_test->mock_cleanup_wait_set();
|
||||
}
|
||||
|
||||
@@ -37,7 +37,8 @@ class TestWaitable : public rclcpp::Waitable
|
||||
public:
|
||||
bool add_to_wait_set(rcl_wait_set_t *) override {return true;}
|
||||
bool is_ready(rcl_wait_set_t *) override {return true;}
|
||||
void execute() override {}
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
void execute(std::shared_ptr<void> & data) override {(void)data;}
|
||||
};
|
||||
|
||||
class TestMemoryStrategy : public ::testing::Test
|
||||
|
||||
@@ -151,6 +151,20 @@ TEST_F(TestPublisher, various_creation_signatures) {
|
||||
rclcpp::create_publisher<Empty>(node, "topic", 42, rclcpp::PublisherOptions());
|
||||
(void)publisher;
|
||||
}
|
||||
{
|
||||
rclcpp::PublisherOptions options;
|
||||
options.qos_overriding_options = rclcpp::QosOverridingOptions::with_default_policies();
|
||||
auto publisher =
|
||||
rclcpp::create_publisher<Empty>(node, "topic", 42, options);
|
||||
(void)publisher;
|
||||
}
|
||||
{
|
||||
auto node_parameters = node->get_node_parameters_interface();
|
||||
auto node_topics = node->get_node_topics_interface();
|
||||
auto publisher = rclcpp::create_publisher<Empty>(
|
||||
node_parameters, node_topics, "topic", 42, rclcpp::PublisherOptions());
|
||||
(void)publisher;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -184,7 +198,7 @@ static std::vector<TestParameters> invalid_qos_profiles()
|
||||
return parameters;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TestPublisherThrows, TestPublisherInvalidIntraprocessQos,
|
||||
::testing::ValuesIn(invalid_qos_profiles()),
|
||||
::testing::PrintToStringParamName());
|
||||
@@ -489,7 +503,9 @@ TEST_F(TestPublisher, default_incompatible_qos_callback) {
|
||||
TEST_F(TestPublisher, run_event_handlers) {
|
||||
initialize();
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("topic", 10);
|
||||
|
||||
for (const auto & handler : publisher->get_event_handlers()) {
|
||||
EXPECT_NO_THROW(handler->execute());
|
||||
std::shared_ptr<void> data = handler->take_data();
|
||||
EXPECT_THROW(handler->execute(data), std::runtime_error);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -192,7 +192,7 @@ using AllTestDescriptions = ::testing::Types<
|
||||
TwoSubscriptionsInTwoContextsWithIntraprocessComm,
|
||||
TwoSubscriptionsInTwoContextsWithoutIntraprocessComm
|
||||
>;
|
||||
TYPED_TEST_CASE(TestPublisherSubscriptionCount, AllTestDescriptions, PrintTestDescription);
|
||||
TYPED_TEST_SUITE(TestPublisherSubscriptionCount, AllTestDescriptions, PrintTestDescription);
|
||||
|
||||
|
||||
using test_msgs::msg::Empty;
|
||||
|
||||
@@ -78,33 +78,41 @@ TEST(TestQoS, equality_namespace) {
|
||||
EXPECT_NE(a, b);
|
||||
}
|
||||
|
||||
TEST(TestQoS, setters) {
|
||||
TEST(TestQoS, setters_and_getters) {
|
||||
rclcpp::QoS qos(10);
|
||||
|
||||
qos.keep_all();
|
||||
EXPECT_EQ(RMW_QOS_POLICY_HISTORY_KEEP_ALL, qos.get_rmw_qos_profile().history);
|
||||
EXPECT_EQ(rclcpp::HistoryPolicy::KeepAll, qos.history());
|
||||
|
||||
qos.keep_last(20);
|
||||
EXPECT_EQ(RMW_QOS_POLICY_HISTORY_KEEP_LAST, qos.get_rmw_qos_profile().history);
|
||||
EXPECT_EQ(20u, qos.get_rmw_qos_profile().depth);
|
||||
EXPECT_EQ(rclcpp::HistoryPolicy::KeepLast, qos.history());
|
||||
EXPECT_EQ(20u, qos.depth());
|
||||
|
||||
qos.reliable();
|
||||
EXPECT_EQ(RMW_QOS_POLICY_RELIABILITY_RELIABLE, qos.get_rmw_qos_profile().reliability);
|
||||
EXPECT_EQ(rclcpp::ReliabilityPolicy::Reliable, qos.reliability());
|
||||
|
||||
qos.reliability(rclcpp::ReliabilityPolicy::BestEffort);
|
||||
EXPECT_EQ(rclcpp::ReliabilityPolicy::BestEffort, qos.reliability());
|
||||
|
||||
qos.durability_volatile();
|
||||
EXPECT_EQ(RMW_QOS_POLICY_DURABILITY_VOLATILE, qos.get_rmw_qos_profile().durability);
|
||||
EXPECT_EQ(rclcpp::DurabilityPolicy::Volatile, qos.durability());
|
||||
|
||||
qos.transient_local();
|
||||
EXPECT_EQ(RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL, qos.get_rmw_qos_profile().durability);
|
||||
EXPECT_EQ(rclcpp::DurabilityPolicy::TransientLocal, qos.durability());
|
||||
|
||||
qos.durability(rclcpp::DurabilityPolicy::Volatile);
|
||||
EXPECT_EQ(rclcpp::DurabilityPolicy::Volatile, qos.durability());
|
||||
|
||||
qos.history(RMW_QOS_POLICY_HISTORY_KEEP_ALL);
|
||||
EXPECT_EQ(RMW_QOS_POLICY_HISTORY_KEEP_ALL, qos.get_rmw_qos_profile().history);
|
||||
EXPECT_EQ(rclcpp::HistoryPolicy::KeepAll, qos.history());
|
||||
|
||||
constexpr size_t duration_ns = 12345;
|
||||
qos.history(rclcpp::HistoryPolicy::KeepLast);
|
||||
EXPECT_EQ(rclcpp::HistoryPolicy::KeepLast, qos.history());
|
||||
|
||||
constexpr rcl_duration_value_t duration_ns = 12345;
|
||||
constexpr std::chrono::nanoseconds duration(duration_ns);
|
||||
qos.deadline(duration);
|
||||
EXPECT_EQ(0u, qos.get_rmw_qos_profile().deadline.sec);
|
||||
EXPECT_EQ(duration_ns, qos.get_rmw_qos_profile().deadline.nsec);
|
||||
EXPECT_EQ(duration_ns, qos.deadline().nanoseconds());
|
||||
|
||||
const rmw_time_t rmw_time {0, 54321};
|
||||
qos.deadline(rmw_time);
|
||||
@@ -112,28 +120,29 @@ TEST(TestQoS, setters) {
|
||||
EXPECT_EQ(rmw_time.nsec, qos.get_rmw_qos_profile().deadline.nsec);
|
||||
|
||||
qos.lifespan(duration);
|
||||
EXPECT_EQ(0u, qos.get_rmw_qos_profile().lifespan.sec);
|
||||
EXPECT_EQ(duration_ns, qos.get_rmw_qos_profile().lifespan.nsec);
|
||||
EXPECT_EQ(duration_ns, qos.lifespan().nanoseconds());
|
||||
|
||||
qos.lifespan(rmw_time);
|
||||
EXPECT_EQ(rmw_time.sec, qos.get_rmw_qos_profile().lifespan.sec);
|
||||
EXPECT_EQ(rmw_time.nsec, qos.get_rmw_qos_profile().lifespan.nsec);
|
||||
|
||||
qos.liveliness(RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC);
|
||||
EXPECT_EQ(RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC, qos.get_rmw_qos_profile().liveliness);
|
||||
EXPECT_EQ(rclcpp::LivelinessPolicy::ManualByTopic, qos.liveliness());
|
||||
|
||||
qos.liveliness(rclcpp::LivelinessPolicy::Automatic);
|
||||
EXPECT_EQ(rclcpp::LivelinessPolicy::Automatic, qos.liveliness());
|
||||
|
||||
qos.liveliness_lease_duration(duration);
|
||||
EXPECT_EQ(0u, qos.get_rmw_qos_profile().liveliness_lease_duration.sec);
|
||||
EXPECT_EQ(duration_ns, qos.get_rmw_qos_profile().liveliness_lease_duration.nsec);
|
||||
EXPECT_EQ(duration_ns, qos.liveliness_lease_duration().nanoseconds());
|
||||
|
||||
qos.liveliness_lease_duration(rmw_time);
|
||||
EXPECT_EQ(rmw_time.sec, qos.get_rmw_qos_profile().liveliness_lease_duration.sec);
|
||||
EXPECT_EQ(rmw_time.nsec, qos.get_rmw_qos_profile().liveliness_lease_duration.nsec);
|
||||
|
||||
qos.avoid_ros_namespace_conventions(true);
|
||||
EXPECT_TRUE(qos.get_rmw_qos_profile().avoid_ros_namespace_conventions);
|
||||
EXPECT_TRUE(qos.avoid_ros_namespace_conventions());
|
||||
qos.avoid_ros_namespace_conventions(false);
|
||||
EXPECT_FALSE(qos.get_rmw_qos_profile().avoid_ros_namespace_conventions);
|
||||
EXPECT_FALSE(qos.avoid_ros_namespace_conventions());
|
||||
}
|
||||
|
||||
bool operator==(const rmw_qos_profile_t & lhs, const rmw_qos_profile_t & rhs)
|
||||
|
||||
@@ -285,14 +285,16 @@ TEST_F(TestQosEvent, execute) {
|
||||
rclcpp::QOSEventHandler<decltype(callback), decltype(rcl_handle)> handler(
|
||||
callback, rcl_publisher_event_init, rcl_handle, event_type);
|
||||
|
||||
EXPECT_NO_THROW(handler.execute());
|
||||
std::shared_ptr<void> data = handler.take_data();
|
||||
EXPECT_NO_THROW(handler.execute(data));
|
||||
EXPECT_TRUE(handler_callback_executed);
|
||||
|
||||
{
|
||||
handler_callback_executed = false;
|
||||
// Logs error and returns early
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_take_event, RCL_RET_ERROR);
|
||||
EXPECT_NO_THROW(handler.execute());
|
||||
std::shared_ptr<void> data = handler.take_data();
|
||||
EXPECT_THROW(handler.execute(data), std::runtime_error);
|
||||
EXPECT_FALSE(handler_callback_executed);
|
||||
}
|
||||
}
|
||||
|
||||
34
rclcpp/test/rclcpp/test_qos_overriding_options.cpp
Normal file
34
rclcpp/test/rclcpp/test_qos_overriding_options.cpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
|
||||
TEST(TestQosOverridingOptions, test_overriding_options) {
|
||||
auto options = rclcpp::QosOverridingOptions::with_default_policies();
|
||||
EXPECT_EQ(options.get_id(), "");
|
||||
EXPECT_EQ(options.get_validation_callback(), nullptr);
|
||||
EXPECT_THAT(
|
||||
options.get_policy_kinds(), testing::ElementsAre(
|
||||
rclcpp::QosPolicyKind::History,
|
||||
rclcpp::QosPolicyKind::Depth,
|
||||
rclcpp::QosPolicyKind::Reliability));
|
||||
}
|
||||
|
||||
TEST(TestQosOverridingOptions, test_qos_policy_kind_to_cstr) {
|
||||
EXPECT_THROW(
|
||||
rclcpp::qos_policy_kind_to_cstr(rclcpp::QosPolicyKind::Invalid),
|
||||
std::invalid_argument);
|
||||
}
|
||||
256
rclcpp/test/rclcpp/test_qos_parameters.cpp
Normal file
256
rclcpp/test/rclcpp/test_qos_parameters.cpp
Normal file
@@ -0,0 +1,256 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "gmock/gmock.h"
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp/qos_overriding_options.hpp"
|
||||
#include "rclcpp/detail/qos_parameters.hpp"
|
||||
|
||||
TEST(TestQosParameters, declare) {
|
||||
rclcpp::init(0, nullptr);
|
||||
auto node = std::make_shared<rclcpp::Node>(
|
||||
"my_node", "/ns", rclcpp::NodeOptions().parameter_overrides(
|
||||
{
|
||||
rclcpp::Parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.publisher.reliability", "best_effort"),
|
||||
}));
|
||||
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
// The first iteration will declare parameters, the second will get the previosuly declared
|
||||
// ones, check both have the same result.
|
||||
rclcpp::QoS qos{rclcpp::KeepLast(10)};
|
||||
qos = rclcpp::detail::declare_qos_parameters(
|
||||
rclcpp::QosOverridingOptions::with_default_policies(),
|
||||
node,
|
||||
"/my/fully/qualified/topic_name",
|
||||
qos,
|
||||
rclcpp::detail::PublisherQosParametersTraits{});
|
||||
|
||||
EXPECT_EQ(
|
||||
node->get_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.publisher.history").get_value<std::string>(),
|
||||
"keep_last");
|
||||
EXPECT_EQ(
|
||||
node->get_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.publisher.depth").get_value<int64_t>(),
|
||||
10);
|
||||
EXPECT_EQ(
|
||||
node->get_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.publisher.reliability"
|
||||
).get_value<std::string>(),
|
||||
"best_effort");
|
||||
EXPECT_EQ(RMW_QOS_POLICY_HISTORY_KEEP_LAST, qos.get_rmw_qos_profile().history);
|
||||
EXPECT_EQ(RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT, qos.get_rmw_qos_profile().reliability);
|
||||
EXPECT_EQ(10u, qos.get_rmw_qos_profile().depth);
|
||||
|
||||
std::map<std::string, rclcpp::Parameter> qos_params;
|
||||
EXPECT_TRUE(
|
||||
node->get_node_parameters_interface()->get_parameters_by_prefix(
|
||||
"qos_overrides./my/fully/qualified/topic_name.publisher", qos_params));
|
||||
EXPECT_EQ(3u, qos_params.size());
|
||||
}
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestQosParameters, declare_with_callback) {
|
||||
rclcpp::init(0, nullptr);
|
||||
auto node = std::make_shared<rclcpp::Node>(
|
||||
"my_node", "/ns", rclcpp::NodeOptions().parameter_overrides(
|
||||
{
|
||||
rclcpp::Parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.publisher.reliability", "best_effort"),
|
||||
}));
|
||||
|
||||
rclcpp::QoS qos{rclcpp::KeepLast(10)};
|
||||
// *INDENT-OFF*, uncrustify suggestion makes the code unreadable
|
||||
EXPECT_THROW(
|
||||
rclcpp::detail::declare_qos_parameters(
|
||||
{
|
||||
{rclcpp::QosPolicyKind::Lifespan},
|
||||
[](const rclcpp::QoS &) {
|
||||
return rclcpp::QosCallbackResult{};
|
||||
}
|
||||
},
|
||||
node,
|
||||
"/my/fully/qualified/topic_name/fails_validation",
|
||||
qos,
|
||||
rclcpp::detail::PublisherQosParametersTraits{}),
|
||||
rclcpp::exceptions::InvalidQosOverridesException);
|
||||
|
||||
rclcpp::detail::declare_qos_parameters(
|
||||
rclcpp::QosOverridingOptions::with_default_policies([](const rclcpp::QoS &) {
|
||||
rclcpp::QosCallbackResult result;
|
||||
result.successful = true;
|
||||
return result;
|
||||
}),
|
||||
node,
|
||||
"/my/fully/qualified/topic_name",
|
||||
qos,
|
||||
rclcpp::detail::PublisherQosParametersTraits{});
|
||||
// *INDENT-ON*
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
constexpr int64_t kDuration{1000000};
|
||||
|
||||
TEST(TestQosParameters, declare_qos_subscription_parameters) {
|
||||
rclcpp::init(0, nullptr);
|
||||
auto node = std::make_shared<rclcpp::Node>(
|
||||
"my_node", "/ns", rclcpp::NodeOptions().parameter_overrides(
|
||||
{
|
||||
rclcpp::Parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.reliability", "best_effort"),
|
||||
rclcpp::Parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.deadline", kDuration),
|
||||
rclcpp::Parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.liveliness_lease_duration",
|
||||
kDuration),
|
||||
}));
|
||||
|
||||
for (size_t i = 0; i < 2; ++i) {
|
||||
// The first iteration will declare parameters, the second will get the previosuly declared
|
||||
// ones, check both have the same result.
|
||||
rclcpp::QoS qos{rclcpp::KeepLast(10)};
|
||||
qos = rclcpp::detail::declare_qos_parameters(
|
||||
{
|
||||
rclcpp::QosPolicyKind::AvoidRosNamespaceConventions, rclcpp::QosPolicyKind::Deadline,
|
||||
rclcpp::QosPolicyKind::Depth, rclcpp::QosPolicyKind::Durability,
|
||||
// lifespan will be ignored
|
||||
rclcpp::QosPolicyKind::History, rclcpp::QosPolicyKind::Lifespan,
|
||||
rclcpp::QosPolicyKind::Liveliness, rclcpp::QosPolicyKind::LivelinessLeaseDuration,
|
||||
rclcpp::QosPolicyKind::Reliability
|
||||
},
|
||||
node,
|
||||
"/my/fully/qualified/topic_name",
|
||||
qos,
|
||||
rclcpp::detail::SubscriptionQosParametersTraits{});
|
||||
|
||||
EXPECT_EQ(
|
||||
node->get_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.avoid_ros_namespace_conventions"
|
||||
).get_value<bool>(), false);
|
||||
EXPECT_EQ(
|
||||
node->get_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.deadline"
|
||||
).get_value<int64_t>(), kDuration);
|
||||
EXPECT_EQ(
|
||||
node->get_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.depth"
|
||||
).get_value<int64_t>(), 10);
|
||||
EXPECT_EQ(
|
||||
node->get_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.durability"
|
||||
).get_value<std::string>(), "volatile");
|
||||
EXPECT_EQ(
|
||||
node->get_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.history"
|
||||
).get_value<std::string>(), "keep_last");
|
||||
EXPECT_FALSE(
|
||||
node->has_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.lifespan"));
|
||||
EXPECT_EQ(
|
||||
node->get_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.liveliness"
|
||||
).get_value<std::string>(), "system_default");
|
||||
EXPECT_EQ(
|
||||
node->get_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.liveliness_lease_duration"
|
||||
).get_value<int64_t>(), kDuration);
|
||||
EXPECT_EQ(
|
||||
node->get_parameter(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription.reliability"
|
||||
).get_value<std::string>(),
|
||||
"best_effort");
|
||||
|
||||
std::map<std::string, rclcpp::Parameter> qos_params;
|
||||
EXPECT_TRUE(
|
||||
node->get_node_parameters_interface()->get_parameters_by_prefix(
|
||||
"qos_overrides./my/fully/qualified/topic_name.subscription", qos_params));
|
||||
EXPECT_EQ(8u, qos_params.size());
|
||||
}
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestQosParameters, declare_with_id) {
|
||||
rclcpp::init(0, nullptr);
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
rclcpp::QoS qos{rclcpp::KeepLast{10}};
|
||||
qos = rclcpp::detail::declare_qos_parameters(
|
||||
rclcpp::QosOverridingOptions::with_default_policies(nullptr, "my_id"),
|
||||
node,
|
||||
"/my/fully/qualified/topic_name",
|
||||
qos,
|
||||
rclcpp::detail::PublisherQosParametersTraits{});
|
||||
|
||||
std::map<std::string, rclcpp::Parameter> qos_params;
|
||||
EXPECT_TRUE(
|
||||
node->get_node_parameters_interface()->get_parameters_by_prefix(
|
||||
"qos_overrides./my/fully/qualified/topic_name.publisher_my_id", qos_params));
|
||||
EXPECT_EQ(3u, qos_params.size());
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestQosParameters, declare_no_parameters_interface) {
|
||||
rclcpp::init(0, nullptr);
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
rclcpp::QoS qos{rclcpp::KeepLast{10}};
|
||||
auto node_base_interface = node->get_node_base_interface();
|
||||
EXPECT_THROW(
|
||||
rclcpp::detail::declare_qos_parameters(
|
||||
rclcpp::QosOverridingOptions::with_default_policies(),
|
||||
node_base_interface,
|
||||
"/my/fully/qualified/topic_name",
|
||||
qos,
|
||||
rclcpp::detail::PublisherQosParametersTraits{}),
|
||||
std::runtime_error);
|
||||
|
||||
qos = rclcpp::detail::declare_qos_parameters(
|
||||
rclcpp::QosOverridingOptions{},
|
||||
node_base_interface,
|
||||
"/my/fully/qualified/topic_name",
|
||||
qos,
|
||||
rclcpp::detail::PublisherQosParametersTraits{});
|
||||
|
||||
std::map<std::string, rclcpp::Parameter> qos_params;
|
||||
EXPECT_FALSE(
|
||||
node->get_node_parameters_interface()->get_parameters_by_prefix("qos_overrides", qos_params));
|
||||
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestQosParameters, internal_functions_failure_modes) {
|
||||
rclcpp::QoS qos{rclcpp::KeepLast{10}};
|
||||
EXPECT_THROW(
|
||||
rclcpp::detail::apply_qos_override(
|
||||
rclcpp::QosPolicyKind::Invalid, rclcpp::ParameterValue{}, qos),
|
||||
std::invalid_argument);
|
||||
EXPECT_THROW(
|
||||
rclcpp::detail::get_default_qos_param_value(
|
||||
rclcpp::QosPolicyKind::Invalid, qos),
|
||||
std::invalid_argument);
|
||||
EXPECT_THROW(
|
||||
rclcpp::detail::check_if_stringified_policy_is_null(
|
||||
nullptr, rclcpp::QosPolicyKind::Reliability),
|
||||
std::invalid_argument);
|
||||
}
|
||||
@@ -506,7 +506,7 @@ static std::vector<TestParameters> invalid_qos_profiles()
|
||||
return parameters;
|
||||
}
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
TestSubscriptionThrows, TestSubscriptionInvalidIntraprocessQos,
|
||||
::testing::ValuesIn(invalid_qos_profiles()),
|
||||
::testing::PrintToStringParamName());
|
||||
|
||||
@@ -129,7 +129,7 @@ struct TwoContextsPerTest
|
||||
};
|
||||
|
||||
using AllTestDescriptions = ::testing::Types<OneContextPerTest, TwoContextsPerTest>;
|
||||
TYPED_TEST_CASE(TestSubscriptionPublisherCount, AllTestDescriptions, PrintTestDescription);
|
||||
TYPED_TEST_SUITE(TestSubscriptionPublisherCount, AllTestDescriptions, PrintTestDescription);
|
||||
|
||||
|
||||
using test_msgs::msg::Empty;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
|
||||
@@ -30,6 +31,8 @@
|
||||
namespace
|
||||
{
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
bool logical_eq(const bool a, const bool b)
|
||||
{
|
||||
return (a && b) || ((!a) && !(b));
|
||||
@@ -221,7 +224,7 @@ TEST_F(TestTime, operators) {
|
||||
EXPECT_EQ(sub, young - old);
|
||||
|
||||
rclcpp::Time young_changed(young);
|
||||
young_changed -= rclcpp::Duration(old.nanoseconds());
|
||||
young_changed -= rclcpp::Duration::from_nanoseconds(old.nanoseconds());
|
||||
EXPECT_EQ(sub.nanoseconds(), young_changed.nanoseconds());
|
||||
|
||||
rclcpp::Time system_time(0, 0, RCL_SYSTEM_TIME);
|
||||
@@ -320,8 +323,8 @@ TEST_F(TestTime, overflow_detectors) {
|
||||
TEST_F(TestTime, overflows) {
|
||||
rclcpp::Time max_time(std::numeric_limits<rcl_time_point_value_t>::max());
|
||||
rclcpp::Time min_time(std::numeric_limits<rcl_time_point_value_t>::min());
|
||||
rclcpp::Duration one(1);
|
||||
rclcpp::Duration two(2);
|
||||
rclcpp::Duration one(1ns);
|
||||
rclcpp::Duration two(2ns);
|
||||
|
||||
// Cross min/max
|
||||
EXPECT_THROW(max_time + one, std::overflow_error);
|
||||
@@ -394,7 +397,7 @@ TEST_F(TestTime, test_assignment_operator_from_builtin_msg_time) {
|
||||
}
|
||||
|
||||
TEST_F(TestTime, test_sum_operator) {
|
||||
const rclcpp::Duration one(1);
|
||||
const rclcpp::Duration one(1ns);
|
||||
const rclcpp::Time test_time(0u);
|
||||
EXPECT_EQ(0u, test_time.nanoseconds());
|
||||
|
||||
@@ -406,41 +409,41 @@ TEST_F(TestTime, test_overflow_underflow_throws) {
|
||||
rclcpp::Time test_time(0u);
|
||||
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Time(INT64_MAX) + rclcpp::Duration(1),
|
||||
test_time = rclcpp::Time(INT64_MAX) + rclcpp::Duration(1ns),
|
||||
std::overflow_error("addition leads to int64_t overflow"));
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Time(INT64_MIN) + rclcpp::Duration(-1),
|
||||
test_time = rclcpp::Time(INT64_MIN) + rclcpp::Duration(-1ns),
|
||||
std::underflow_error("addition leads to int64_t underflow"));
|
||||
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Time(INT64_MAX) - rclcpp::Duration(-1),
|
||||
test_time = rclcpp::Time(INT64_MAX) - rclcpp::Duration(-1ns),
|
||||
std::overflow_error("time subtraction leads to int64_t overflow"));
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Time(INT64_MIN) - rclcpp::Duration(1),
|
||||
test_time = rclcpp::Time(INT64_MIN) - rclcpp::Duration(1ns),
|
||||
std::underflow_error("time subtraction leads to int64_t underflow"));
|
||||
|
||||
test_time = rclcpp::Time(INT64_MAX);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time += rclcpp::Duration(1),
|
||||
test_time += rclcpp::Duration(1ns),
|
||||
std::overflow_error("addition leads to int64_t overflow"));
|
||||
test_time = rclcpp::Time(INT64_MIN);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time += rclcpp::Duration(-1),
|
||||
test_time += rclcpp::Duration(-1ns),
|
||||
std::underflow_error("addition leads to int64_t underflow"));
|
||||
|
||||
test_time = rclcpp::Time(INT64_MAX);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time -= rclcpp::Duration(-1),
|
||||
test_time -= rclcpp::Duration(-1ns),
|
||||
std::overflow_error("time subtraction leads to int64_t overflow"));
|
||||
test_time = rclcpp::Time(INT64_MIN);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time -= rclcpp::Duration(1),
|
||||
test_time -= rclcpp::Duration(1ns),
|
||||
std::underflow_error("time subtraction leads to int64_t underflow"));
|
||||
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Duration(INT64_MAX) + rclcpp::Time(1),
|
||||
test_time = rclcpp::Duration::from_nanoseconds(INT64_MAX) + rclcpp::Time(1),
|
||||
std::overflow_error("addition leads to int64_t overflow"));
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Duration(INT64_MIN) + rclcpp::Time(-1),
|
||||
test_time = rclcpp::Duration::from_nanoseconds(INT64_MIN) + rclcpp::Time(-1),
|
||||
std::underflow_error("addition leads to int64_t underflow"));
|
||||
}
|
||||
|
||||
@@ -61,6 +61,29 @@ TEST(TestUtilities, init_with_args) {
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestUtilities, init_with_args_contains_ros) {
|
||||
EXPECT_FALSE(rclcpp::signal_handlers_installed());
|
||||
const char * const argv[] = {
|
||||
"process_name",
|
||||
"-d", "--ros-args",
|
||||
"-r", "__ns:=/foo/bar",
|
||||
"-r", "__ns:=/fiz/buz",
|
||||
"--", "--foo=bar", "--baz"
|
||||
};
|
||||
int argc = sizeof(argv) / sizeof(const char *);
|
||||
auto args = rclcpp::init_and_remove_ros_arguments(argc, argv);
|
||||
|
||||
ASSERT_EQ(4u, args.size());
|
||||
ASSERT_EQ(std::string{"process_name"}, args[0]);
|
||||
ASSERT_EQ(std::string{"-d"}, args[1]);
|
||||
ASSERT_EQ(std::string{"--foo=bar"}, args[2]);
|
||||
ASSERT_EQ(std::string{"--baz"}, args[3]);
|
||||
EXPECT_TRUE(rclcpp::signal_handlers_installed());
|
||||
|
||||
EXPECT_TRUE(rclcpp::ok());
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestUtilities, multi_init) {
|
||||
auto context1 = std::make_shared<rclcpp::contexts::DefaultContext>();
|
||||
auto context2 = std::make_shared<rclcpp::contexts::DefaultContext>();
|
||||
@@ -108,59 +131,6 @@ MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, !=)
|
||||
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, >)
|
||||
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, <)
|
||||
|
||||
TEST(TestUtilities, test_context_release_interrupt_guard_condition) {
|
||||
auto context1 = std::make_shared<rclcpp::contexts::DefaultContext>();
|
||||
context1->init(0, nullptr);
|
||||
RCLCPP_SCOPE_EXIT(rclcpp::shutdown(context1););
|
||||
|
||||
rcl_wait_set_t wait_set = rcl_get_zero_initialized_wait_set();
|
||||
rcl_ret_t ret = rcl_wait_set_init(
|
||||
&wait_set, 0, 2, 0, 0, 0, 0, context1->get_rcl_context().get(),
|
||||
rcl_get_default_allocator());
|
||||
ASSERT_EQ(RCL_RET_OK, ret);
|
||||
|
||||
// Expected usage
|
||||
rcl_guard_condition_t * interrupt_guard_condition =
|
||||
context1->get_interrupt_guard_condition(&wait_set);
|
||||
EXPECT_NE(nullptr, interrupt_guard_condition);
|
||||
EXPECT_NO_THROW(context1->release_interrupt_guard_condition(&wait_set));
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_guard_condition_init, RCL_RET_ERROR);
|
||||
EXPECT_THROW(
|
||||
{interrupt_guard_condition = context1->get_interrupt_guard_condition(&wait_set);},
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
{
|
||||
interrupt_guard_condition = context1->get_interrupt_guard_condition(&wait_set);
|
||||
auto mock = mocking_utils::inject_on_return(
|
||||
"lib:rclcpp", rcl_guard_condition_fini, RCL_RET_ERROR);
|
||||
EXPECT_THROW(
|
||||
{context1->release_interrupt_guard_condition(&wait_set);},
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
{
|
||||
interrupt_guard_condition = context1->get_interrupt_guard_condition(&wait_set);
|
||||
auto mock = mocking_utils::inject_on_return(
|
||||
"lib:rclcpp", rcl_guard_condition_fini, RCL_RET_ERROR);
|
||||
EXPECT_NO_THROW({context1->release_interrupt_guard_condition(&wait_set, std::nothrow);});
|
||||
}
|
||||
|
||||
{
|
||||
EXPECT_THROW(
|
||||
context1->release_interrupt_guard_condition(nullptr),
|
||||
std::runtime_error);
|
||||
}
|
||||
|
||||
// Test it works after restore mocks
|
||||
interrupt_guard_condition = context1->get_interrupt_guard_condition(&wait_set);
|
||||
EXPECT_NE(nullptr, interrupt_guard_condition);
|
||||
EXPECT_NO_THROW(context1->release_interrupt_guard_condition(&wait_set));
|
||||
}
|
||||
|
||||
TEST(TestUtilities, test_context_init_shutdown_fails) {
|
||||
{
|
||||
auto context = std::make_shared<rclcpp::contexts::DefaultContext>();
|
||||
|
||||
@@ -55,7 +55,10 @@ public:
|
||||
|
||||
bool is_ready(rcl_wait_set_t *) override {return is_ready_;}
|
||||
|
||||
void execute() override {}
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override {(void)data;}
|
||||
|
||||
void set_is_ready(bool value) {is_ready_ = value;}
|
||||
|
||||
|
||||
@@ -54,7 +54,10 @@ public:
|
||||
|
||||
bool is_ready(rcl_wait_set_t *) override {return is_ready_;}
|
||||
|
||||
void execute() override {}
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override {(void)data;}
|
||||
|
||||
void set_is_ready(bool value) {is_ready_ = value;}
|
||||
|
||||
|
||||
@@ -54,7 +54,10 @@ public:
|
||||
|
||||
bool is_ready(rcl_wait_set_t *) override {return is_ready_;}
|
||||
|
||||
void execute() override {}
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override {(void)data;}
|
||||
|
||||
void set_is_ready(bool value) {is_ready_ = value;}
|
||||
|
||||
|
||||
@@ -54,7 +54,10 @@ public:
|
||||
|
||||
bool is_ready(rcl_wait_set_t *) override {return is_ready_;}
|
||||
|
||||
void execute() override {}
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
|
||||
void
|
||||
execute(std::shared_ptr<void> & data) override {(void)data;}
|
||||
|
||||
void set_is_ready(bool value) {is_ready_ = value;}
|
||||
|
||||
|
||||
@@ -3,6 +3,28 @@ Changelog for package rclcpp_action
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
|
||||
6.1.0 (2020-12-10)
|
||||
------------------
|
||||
* Update QDs to QL 1 (`#1477 <https://github.com/ros2/rclcpp/issues/1477>`_)
|
||||
* Contributors: Stephen Brawner
|
||||
|
||||
6.0.0 (2020-11-18)
|
||||
------------------
|
||||
* Add `take_data` to `Waitable` and `data` to `AnyExecutable` (`#1241 <https://github.com/ros2/rclcpp/issues/1241>`_)
|
||||
* Fix test crashes on CentOS 7 (`#1449 <https://github.com/ros2/rclcpp/issues/1449>`_)
|
||||
* Bump rclcpp packages to Quality Level 2 (`#1445 <https://github.com/ros2/rclcpp/issues/1445>`_)
|
||||
* Add rclcpp_action action_server benchmarks (`#1433 <https://github.com/ros2/rclcpp/issues/1433>`_)
|
||||
* Contributors: Audrow Nash, Chris Lalancette, Louise Poubel, brawner
|
||||
|
||||
5.1.0 (2020-11-02)
|
||||
------------------
|
||||
* Benchmark rclcpp_action action_client (`#1429 <https://github.com/ros2/rclcpp/issues/1429>`_)
|
||||
* Add missing locking to the rclcpp_action::ServerBase. (`#1421 <https://github.com/ros2/rclcpp/issues/1421>`_)
|
||||
* Increase test timeouts of slow running tests with rmw_connext_cpp (`#1400 <https://github.com/ros2/rclcpp/issues/1400>`_)
|
||||
* Update maintainers (`#1384 <https://github.com/ros2/rclcpp/issues/1384>`_)
|
||||
* Increase coverage rclcpp_action to 95% (`#1290 <https://github.com/ros2/rclcpp/issues/1290>`_)
|
||||
* Contributors: Chris Lalancette, Ivan Santiago Paunovic, brawner
|
||||
|
||||
5.0.0 (2020-09-18)
|
||||
------------------
|
||||
* Pass goal handle to goal response callback instead of a future (`#1311 <https://github.com/ros2/rclcpp/issues/1311>`_)
|
||||
|
||||
@@ -74,6 +74,8 @@ if(BUILD_TESTING)
|
||||
set(ament_cmake_cppcheck_ADDITIONAL_INCLUDE_DIRS ${rclcpp_INCLUDE_DIRS})
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
|
||||
add_subdirectory(test/benchmark)
|
||||
|
||||
ament_add_gtest(test_client test/test_client.cpp TIMEOUT 180)
|
||||
if(TARGET test_client)
|
||||
ament_target_dependencies(test_client
|
||||
|
||||
@@ -2,7 +2,7 @@ This document is a declaration of software quality for the `rclcpp_action` packa
|
||||
|
||||
# rclcpp_action Quality Declaration
|
||||
|
||||
The package `rclcpp_action` claims to be in the **Quality Level 3** category.
|
||||
The package `rclcpp_action` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware.
|
||||
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#package-quality-categories) of the ROS2 developer guide.
|
||||
|
||||
@@ -80,13 +80,13 @@ The API is publicly available in its [ROS 2 API documentation](http://docs.ros2.
|
||||
|
||||
The license for `rclcpp_action` is Apache 2.0, and a summary is in each source file, the type is declared in the [`package.xml`](./package.xml) manifest file, and a full copy of the license is in the [`LICENSE`](../LICENSE) file.
|
||||
|
||||
There is an automated test which runs a linter that ensures each file has a license statement. [Here](http://build.ros2.org/view/Fpr/job/Fpr__rclcpp__ubuntu_focal_amd64/lastSuccessfulBuild/testReport/rclcpp_action/) can be found a list with the latest results of the various linters being run on the package.
|
||||
There is an automated test which runs a linter that ensures each file has a license statement. [Here](http://build.ros2.org/view/Rpr/job/Rpr__rclcpp__ubuntu_focal_amd64/lastCompletedBuild/testReport/rclcpp_action/) can be found a list with the latest results of the various linters being run on the package.
|
||||
|
||||
### Copyright Statements [3.iv]
|
||||
|
||||
The copyright holders each provide a statement of copyright in each source code file in `rclcpp_action`.
|
||||
|
||||
There is an automated test which runs a linter that ensures each file has at least one copyright statement. Latest linter result report can be seen [here](http://build.ros2.org/view/Fpr/job/Fpr__rclcpp__ubuntu_focal_amd64/lastSuccessfulBuild/testReport/rclcpp_action/copyright/).
|
||||
There is an automated test which runs a linter that ensures each file has at least one copyright statement. Latest linter result report can be seen [here](http://build.ros2.org/view/Rpr/job/Rpr__rclcpp__ubuntu_focal_amd64/lastCompletedBuild/testReport/rclcpp_action/copyright/).
|
||||
|
||||
## Testing [4]
|
||||
|
||||
@@ -119,11 +119,19 @@ 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/lastSuccessfulBuild/cobertura/src_ros2_rclcpp_rclcpp_action_src/). A description of how coverage statistics are calculated is summarized in this page ["ROS 2 Onboarding Guide"](https://index.ros.org/doc/ros2/Contributing/ROS-2-On-boarding-Guide/#note-on-coverage-runs).
|
||||
Current coverage statistics can be viewed [here](https://ci.ros2.org/job/nightly_linux_coverage/lastCompletedBuild/cobertura/src_ros2_rclcpp_rclcpp_action_src/). A description of how coverage statistics are calculated is summarized in this page ["ROS 2 Onboarding Guide"](https://index.ros.org/doc/ros2/Contributing/ROS-2-On-boarding-Guide/#note-on-coverage-runs).
|
||||
|
||||
### Performance [4.iv]
|
||||
|
||||
It is not yet defined if this package requires performance testing and how addresses this topic.
|
||||
`rclcpp_action` follows the recommendations for performance testing of C/C++ code in the [ROS 2 Developer Guide](https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#performance), and opts to do performance analysis on each release rather than each change.
|
||||
|
||||
The performance tests of `rclcpp_action` are located in the [test/benchmark directory](https://github.com/ros2/rclcpp/tree/master/rclcpp_action/test/benchmark).
|
||||
|
||||
System level performance benchmarks that cover features of `rclcpp_action` can be found at:
|
||||
* [Benchmarks](http://build.ros2.org/view/Rci/job/Rci__benchmark_ubuntu_focal_amd64/BenchmarkTable/)
|
||||
* [Performance](http://build.ros2.org/view/Rci/job/Rci__nightly-performance_ubuntu_focal_amd64/lastCompletedBuild/)
|
||||
|
||||
Changes that introduce regressions in performance must be adequately justified in order to be accepted and merged.
|
||||
|
||||
### Linters and Static Analysis [4.v]
|
||||
|
||||
@@ -151,19 +159,19 @@ It also has several test dependencies, which do not affect the resulting quality
|
||||
|
||||
`action_msgs` provides messages and services for ROS 2 actions.
|
||||
|
||||
It is **Quality Level 3**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/action_msgs/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/action_msgs/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rclcpp`
|
||||
|
||||
The `rclcpp` package provides the ROS client library in C++.
|
||||
|
||||
It is **Quality Level 3**, see its [Quality Declaration document](https://github.com/ros2/rclcpp/blob/master/rclcpp/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rclcpp/blob/master/rclcpp/QUALITY_DECLARATION.md).
|
||||
|
||||
#### `rcl_action`
|
||||
|
||||
The `rcl_action` package provides C-based ROS action implementation.
|
||||
|
||||
It is **Quality Level 3**, see its [Quality Declaration document](https://github.com/ros2/rcl/blob/master/rcl_action/QUALITY_DECLARATION.md).
|
||||
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcl/blob/master/rcl_action/QUALITY_DECLARATION.md).
|
||||
|
||||
### Direct Runtime non-ROS Dependency [5.iii]
|
||||
|
||||
|
||||
@@ -6,4 +6,4 @@ Visit the [rclcpp_action API documentation](http://docs.ros2.org/latest/api/rclc
|
||||
|
||||
## Quality Declaration
|
||||
|
||||
This package claims to be in the **Quality Level 3** category, see the [Quality Declaration](QUALITY_DECLARATION.md) for more details.
|
||||
This package claims to be in the **Quality Level 1** category, see the [Quality Declaration](QUALITY_DECLARATION.md) for more details.
|
||||
|
||||
@@ -116,10 +116,15 @@ public:
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set) override;
|
||||
|
||||
/// \internal
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
std::shared_ptr<void>
|
||||
take_data() override;
|
||||
|
||||
/// \internal
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
void
|
||||
execute() override;
|
||||
execute(std::shared_ptr<void> & data) override;
|
||||
|
||||
// End Waitables API
|
||||
// -----------------
|
||||
|
||||
@@ -118,11 +118,15 @@ public:
|
||||
bool
|
||||
is_ready(rcl_wait_set_t *) override;
|
||||
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
std::shared_ptr<void>
|
||||
take_data() override;
|
||||
|
||||
/// Act on entities in the wait set which are ready to be acted upon.
|
||||
/// \internal
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
void
|
||||
execute() override;
|
||||
execute(std::shared_ptr<void> & data) override;
|
||||
|
||||
// End Waitables API
|
||||
// -----------------
|
||||
@@ -229,19 +233,19 @@ private:
|
||||
/// \internal
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
void
|
||||
execute_goal_request_received();
|
||||
execute_goal_request_received(std::shared_ptr<void> & data);
|
||||
|
||||
/// Handle a request to cancel goals on the server
|
||||
/// \internal
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
void
|
||||
execute_cancel_request_received();
|
||||
execute_cancel_request_received(std::shared_ptr<void> & data);
|
||||
|
||||
/// Handle a request to get the result of an action
|
||||
/// \internal
|
||||
RCLCPP_ACTION_PUBLIC
|
||||
void
|
||||
execute_result_request_received();
|
||||
execute_result_request_received(std::shared_ptr<void> & data);
|
||||
|
||||
/// Handle a timeout indicating a completed goal should be forgotten by the server
|
||||
/// \internal
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp_action</name>
|
||||
<version>5.0.0</version>
|
||||
<version>6.1.0</version>
|
||||
<description>Adds action APIs for C++.</description>
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
|
||||
@@ -26,6 +26,7 @@
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
<test_depend>mimick_vendor</test_depend>
|
||||
<test_depend>performance_test_fixture</test_depend>
|
||||
<test_depend>test_msgs</test_depend>
|
||||
|
||||
<export>
|
||||
|
||||
@@ -22,6 +22,8 @@
|
||||
#include <memory>
|
||||
#include <random>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp_action/client.hpp"
|
||||
#include "rclcpp_action/exceptions.hpp"
|
||||
@@ -380,58 +382,111 @@ ClientBase::generate_goal_id()
|
||||
return goal_id;
|
||||
}
|
||||
|
||||
void
|
||||
ClientBase::execute()
|
||||
std::shared_ptr<void>
|
||||
ClientBase::take_data()
|
||||
{
|
||||
if (pimpl_->is_feedback_ready) {
|
||||
std::shared_ptr<void> feedback_message = this->create_feedback_message();
|
||||
rcl_ret_t ret = rcl_action_take_feedback(
|
||||
pimpl_->client_handle.get(), feedback_message.get());
|
||||
pimpl_->is_feedback_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
this->handle_feedback_message(feedback_message);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking feedback");
|
||||
}
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(
|
||||
ret, feedback_message));
|
||||
} else if (pimpl_->is_status_ready) {
|
||||
std::shared_ptr<void> status_message = this->create_status_message();
|
||||
rcl_ret_t ret = rcl_action_take_status(
|
||||
pimpl_->client_handle.get(), status_message.get());
|
||||
pimpl_->is_status_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
this->handle_status_message(status_message);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking status");
|
||||
}
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(
|
||||
ret, status_message));
|
||||
} else if (pimpl_->is_goal_response_ready) {
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> goal_response = this->create_goal_response();
|
||||
rcl_ret_t ret = rcl_action_take_goal_response(
|
||||
pimpl_->client_handle.get(), &response_header, goal_response.get());
|
||||
pimpl_->is_goal_response_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
this->handle_goal_response(response_header, goal_response);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking goal response");
|
||||
}
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
|
||||
ret, response_header, goal_response));
|
||||
} else if (pimpl_->is_result_response_ready) {
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> result_response = this->create_result_response();
|
||||
rcl_ret_t ret = rcl_action_take_result_response(
|
||||
pimpl_->client_handle.get(), &response_header, result_response.get());
|
||||
pimpl_->is_result_response_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
this->handle_result_response(response_header, result_response);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking result response");
|
||||
}
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
|
||||
ret, response_header, result_response));
|
||||
} else if (pimpl_->is_cancel_response_ready) {
|
||||
rmw_request_id_t response_header;
|
||||
std::shared_ptr<void> cancel_response = this->create_cancel_response();
|
||||
rcl_ret_t ret = rcl_action_take_cancel_response(
|
||||
pimpl_->client_handle.get(), &response_header, cancel_response.get());
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(
|
||||
ret, response_header, cancel_response));
|
||||
} else {
|
||||
throw std::runtime_error("Taking data from action client but nothing is ready");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ClientBase::execute(std::shared_ptr<void> & data)
|
||||
{
|
||||
if (!data) {
|
||||
throw std::runtime_error("'data' is empty");
|
||||
}
|
||||
|
||||
if (pimpl_->is_feedback_ready) {
|
||||
auto shared_ptr = std::static_pointer_cast<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
pimpl_->is_feedback_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
auto feedback_message = std::get<1>(*shared_ptr);
|
||||
this->handle_feedback_message(feedback_message);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking feedback");
|
||||
}
|
||||
} else if (pimpl_->is_status_ready) {
|
||||
auto shared_ptr = std::static_pointer_cast<std::tuple<rcl_ret_t, std::shared_ptr<void>>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
pimpl_->is_status_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
auto status_message = std::get<1>(*shared_ptr);
|
||||
this->handle_status_message(status_message);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking status");
|
||||
}
|
||||
} else if (pimpl_->is_goal_response_ready) {
|
||||
auto shared_ptr = std::static_pointer_cast<
|
||||
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
pimpl_->is_goal_response_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
auto response_header = std::get<1>(*shared_ptr);
|
||||
auto goal_response = std::get<2>(*shared_ptr);
|
||||
this->handle_goal_response(response_header, goal_response);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking goal response");
|
||||
}
|
||||
} else if (pimpl_->is_result_response_ready) {
|
||||
auto shared_ptr = std::static_pointer_cast<
|
||||
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
pimpl_->is_result_response_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
auto response_header = std::get<1>(*shared_ptr);
|
||||
auto result_response = std::get<2>(*shared_ptr);
|
||||
this->handle_result_response(response_header, result_response);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking result response");
|
||||
}
|
||||
} else if (pimpl_->is_cancel_response_ready) {
|
||||
auto shared_ptr = std::static_pointer_cast<
|
||||
std::tuple<rcl_ret_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
pimpl_->is_cancel_response_ready = false;
|
||||
if (RCL_RET_OK == ret) {
|
||||
auto response_header = std::get<1>(*shared_ptr);
|
||||
auto cancel_response = std::get<2>(*shared_ptr);
|
||||
this->handle_cancel_response(response_header, cancel_response);
|
||||
} else if (RCL_RET_ACTION_CLIENT_TAKE_FAILED != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "error taking cancel response");
|
||||
|
||||
@@ -24,7 +24,9 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
using rclcpp_action::ServerBase;
|
||||
@@ -44,7 +46,7 @@ public:
|
||||
}
|
||||
|
||||
// Lock everything except user callbacks
|
||||
std::recursive_mutex reentrant_mutex_;
|
||||
std::recursive_mutex action_server_reentrant_mutex_;
|
||||
|
||||
std::shared_ptr<rcl_action_server_t> action_server_;
|
||||
|
||||
@@ -88,12 +90,13 @@ ServerBase::ServerBase(
|
||||
if (nullptr != ptr) {
|
||||
rcl_node_t * rcl_node = node_base->get_rcl_node_handle();
|
||||
rcl_ret_t ret = rcl_action_server_fini(ptr, rcl_node);
|
||||
(void)ret;
|
||||
RCLCPP_DEBUG(
|
||||
rclcpp::get_logger("rclcpp_action"),
|
||||
"failed to fini rcl_action_server_t in deleter");
|
||||
if (RCL_RET_OK != ret) {
|
||||
RCLCPP_DEBUG(
|
||||
rclcpp::get_logger("rclcpp_action"),
|
||||
"failed to fini rcl_action_server_t in deleter");
|
||||
}
|
||||
delete ptr;
|
||||
}
|
||||
delete ptr;
|
||||
};
|
||||
|
||||
pimpl_->action_server_.reset(new rcl_action_server_t, deleter);
|
||||
@@ -159,7 +162,7 @@ ServerBase::get_number_of_ready_guard_conditions()
|
||||
bool
|
||||
ServerBase::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->reentrant_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
rcl_ret_t ret = rcl_action_wait_set_add_action_server(
|
||||
wait_set, pimpl_->action_server_.get(), NULL);
|
||||
return RCL_RET_OK == ret;
|
||||
@@ -168,7 +171,7 @@ ServerBase::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
bool
|
||||
ServerBase::is_ready(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->reentrant_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
rcl_ret_t ret = rcl_action_server_wait_set_get_entities_ready(
|
||||
wait_set,
|
||||
pimpl_->action_server_.get(),
|
||||
@@ -187,15 +190,77 @@ ServerBase::is_ready(rcl_wait_set_t * wait_set)
|
||||
pimpl_->goal_expired_;
|
||||
}
|
||||
|
||||
void
|
||||
ServerBase::execute()
|
||||
std::shared_ptr<void>
|
||||
ServerBase::take_data()
|
||||
{
|
||||
if (pimpl_->goal_request_ready_) {
|
||||
execute_goal_request_received();
|
||||
rcl_ret_t ret;
|
||||
rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info();
|
||||
rmw_request_id_t request_header;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
|
||||
std::shared_ptr<void> message = create_goal_request();
|
||||
ret = rcl_action_take_goal_request(
|
||||
pimpl_->action_server_.get(),
|
||||
&request_header,
|
||||
message.get());
|
||||
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared
|
||||
<std::tuple<rcl_ret_t, rcl_action_goal_info_t, rmw_request_id_t, std::shared_ptr<void>>>(
|
||||
ret,
|
||||
goal_info,
|
||||
request_header, message));
|
||||
} else if (pimpl_->cancel_request_ready_) {
|
||||
execute_cancel_request_received();
|
||||
rcl_ret_t ret;
|
||||
rmw_request_id_t request_header;
|
||||
|
||||
// Initialize cancel request
|
||||
auto request = std::make_shared<action_msgs::srv::CancelGoal::Request>();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
ret = rcl_action_take_cancel_request(
|
||||
pimpl_->action_server_.get(),
|
||||
&request_header,
|
||||
request.get());
|
||||
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared
|
||||
<std::tuple<rcl_ret_t, std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
|
||||
rmw_request_id_t>>(ret, request, request_header));
|
||||
} else if (pimpl_->result_request_ready_) {
|
||||
execute_result_request_received();
|
||||
rcl_ret_t ret;
|
||||
// Get the result request message
|
||||
rmw_request_id_t request_header;
|
||||
std::shared_ptr<void> result_request = create_result_request();
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
ret = rcl_action_take_result_request(
|
||||
pimpl_->action_server_.get(), &request_header, result_request.get());
|
||||
|
||||
return std::static_pointer_cast<void>(
|
||||
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>>(
|
||||
ret, result_request, request_header));
|
||||
} else if (pimpl_->goal_expired_) {
|
||||
return nullptr;
|
||||
} else {
|
||||
throw std::runtime_error("Taking data from action server but nothing is ready");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServerBase::execute(std::shared_ptr<void> & data)
|
||||
{
|
||||
if (!data && !pimpl_->goal_expired_) {
|
||||
throw std::runtime_error("'data' is empty");
|
||||
}
|
||||
|
||||
if (pimpl_->goal_request_ready_) {
|
||||
execute_goal_request_received(data);
|
||||
} else if (pimpl_->cancel_request_ready_) {
|
||||
execute_cancel_request_received(data);
|
||||
} else if (pimpl_->result_request_ready_) {
|
||||
execute_result_request_received(data);
|
||||
} else if (pimpl_->goal_expired_) {
|
||||
execute_check_expired_goals();
|
||||
} else {
|
||||
@@ -204,22 +269,11 @@ ServerBase::execute()
|
||||
}
|
||||
|
||||
void
|
||||
ServerBase::execute_goal_request_received()
|
||||
ServerBase::execute_goal_request_received(std::shared_ptr<void> & data)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info();
|
||||
rmw_request_id_t request_header;
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->reentrant_mutex_);
|
||||
|
||||
std::shared_ptr<void> message = create_goal_request();
|
||||
ret = rcl_action_take_goal_request(
|
||||
pimpl_->action_server_.get(),
|
||||
&request_header,
|
||||
message.get());
|
||||
|
||||
pimpl_->goal_request_ready_ = false;
|
||||
|
||||
auto shared_ptr = std::static_pointer_cast
|
||||
<std::tuple<rcl_ret_t, rcl_action_goal_info_t, rmw_request_id_t, std::shared_ptr<void>>>(data);
|
||||
rcl_ret_t ret = std::get<0>(*shared_ptr);
|
||||
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
|
||||
// Ignore take failure because connext fails if it receives a sample without valid data.
|
||||
// This happens when a client shuts down and connext receives a sample saying the client is
|
||||
@@ -228,6 +282,14 @@ ServerBase::execute_goal_request_received()
|
||||
} else if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
rcl_action_goal_info_t goal_info = std::get<1>(*shared_ptr);
|
||||
rmw_request_id_t request_header = std::get<2>(*shared_ptr);
|
||||
std::shared_ptr<void> message = std::get<3>(*shared_ptr);
|
||||
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
|
||||
pimpl_->goal_request_ready_ = false;
|
||||
|
||||
GoalUUID uuid = get_goal_id_from_goal_request(message.get());
|
||||
convert(uuid, &goal_info);
|
||||
@@ -254,10 +316,11 @@ ServerBase::execute_goal_request_received()
|
||||
{
|
||||
if (nullptr != ptr) {
|
||||
rcl_ret_t fail_ret = rcl_action_goal_handle_fini(ptr);
|
||||
(void)fail_ret;
|
||||
RCLCPP_DEBUG(
|
||||
rclcpp::get_logger("rclcpp_action"),
|
||||
"failed to fini rcl_action_goal_handle_t in deleter");
|
||||
if (RCL_RET_OK != fail_ret) {
|
||||
RCLCPP_DEBUG(
|
||||
rclcpp::get_logger("rclcpp_action"),
|
||||
"failed to fini rcl_action_goal_handle_t in deleter");
|
||||
}
|
||||
delete ptr;
|
||||
}
|
||||
};
|
||||
@@ -286,25 +349,17 @@ ServerBase::execute_goal_request_received()
|
||||
// Tell user to start executing action
|
||||
call_goal_accepted_callback(handle, uuid, message);
|
||||
}
|
||||
data.reset();
|
||||
}
|
||||
|
||||
void
|
||||
ServerBase::execute_cancel_request_received()
|
||||
ServerBase::execute_cancel_request_received(std::shared_ptr<void> & data)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
rmw_request_id_t request_header;
|
||||
|
||||
// Initialize cancel request
|
||||
auto request = std::make_shared<action_msgs::srv::CancelGoal::Request>();
|
||||
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->reentrant_mutex_);
|
||||
ret = rcl_action_take_cancel_request(
|
||||
pimpl_->action_server_.get(),
|
||||
&request_header,
|
||||
request.get());
|
||||
|
||||
pimpl_->cancel_request_ready_ = false;
|
||||
|
||||
auto shared_ptr = std::static_pointer_cast
|
||||
<std::tuple<rcl_ret_t, std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
|
||||
rmw_request_id_t>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
|
||||
// Ignore take failure because connext fails if it receives a sample without valid data.
|
||||
// This happens when a client shuts down and connext receives a sample saying the client is
|
||||
@@ -313,6 +368,8 @@ ServerBase::execute_cancel_request_received()
|
||||
} else if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
auto request = std::get<1>(*shared_ptr);
|
||||
auto request_header = std::get<2>(*shared_ptr);
|
||||
|
||||
// Convert c++ message to C message
|
||||
rcl_action_cancel_request_t cancel_request = rcl_action_get_zero_initialized_cancel_request();
|
||||
@@ -374,21 +431,16 @@ ServerBase::execute_cancel_request_received()
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
data.reset();
|
||||
}
|
||||
|
||||
void
|
||||
ServerBase::execute_result_request_received()
|
||||
ServerBase::execute_result_request_received(std::shared_ptr<void> & data)
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
// Get the result request message
|
||||
rmw_request_id_t request_header;
|
||||
std::shared_ptr<void> result_request = create_result_request();
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->reentrant_mutex_);
|
||||
ret = rcl_action_take_result_request(
|
||||
pimpl_->action_server_.get(), &request_header, result_request.get());
|
||||
|
||||
pimpl_->result_request_ready_ = false;
|
||||
|
||||
auto shared_ptr = std::static_pointer_cast
|
||||
<std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>>(data);
|
||||
auto ret = std::get<0>(*shared_ptr);
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
if (RCL_RET_ACTION_SERVER_TAKE_FAILED == ret) {
|
||||
// Ignore take failure because connext fails if it receives a sample without valid data.
|
||||
// This happens when a client shuts down and connext receives a sample saying the client is
|
||||
@@ -397,7 +449,10 @@ ServerBase::execute_result_request_received()
|
||||
} else if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
auto result_request = std::get<1>(*shared_ptr);
|
||||
auto request_header = std::get<2>(*shared_ptr);
|
||||
|
||||
pimpl_->result_request_ready_ = false;
|
||||
std::shared_ptr<void> result_response;
|
||||
|
||||
// check if the goal exists
|
||||
@@ -419,7 +474,7 @@ ServerBase::execute_result_request_received()
|
||||
|
||||
if (result_response) {
|
||||
// Send the result now
|
||||
ret = rcl_action_send_result_response(
|
||||
rcl_ret_t ret = rcl_action_send_result_response(
|
||||
pimpl_->action_server_.get(), &request_header, result_response.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
@@ -428,6 +483,7 @@ ServerBase::execute_result_request_received()
|
||||
// Store the request so it can be responded to later
|
||||
pimpl_->result_requests_[uuid].push_back(request_header);
|
||||
}
|
||||
data.reset();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -439,7 +495,7 @@ ServerBase::execute_check_expired_goals()
|
||||
|
||||
// Loop in case more than 1 goal expired
|
||||
while (num_expired > 0u) {
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->reentrant_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
rcl_ret_t ret;
|
||||
ret = rcl_action_expire_goals(pimpl_->action_server_.get(), expired_goals, 1, &num_expired);
|
||||
if (RCL_RET_OK != ret) {
|
||||
@@ -461,6 +517,11 @@ ServerBase::publish_status()
|
||||
{
|
||||
rcl_ret_t ret;
|
||||
|
||||
// We need to hold the lock across this entire method because
|
||||
// rcl_action_server_get_goal_handles() returns an internal pointer to the
|
||||
// goal data.
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
|
||||
// Get all goal handles known to C action server
|
||||
rcl_action_goal_handle_t ** goal_handles = NULL;
|
||||
size_t num_goals = 0;
|
||||
@@ -516,7 +577,7 @@ ServerBase::publish_result(const GoalUUID & uuid, std::shared_ptr<void> result_m
|
||||
// Check that the goal exists
|
||||
rcl_action_goal_info_t goal_info;
|
||||
convert(uuid, &goal_info);
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->reentrant_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
bool goal_exists;
|
||||
goal_exists = rcl_action_server_goal_exists(pimpl_->action_server_.get(), &goal_info);
|
||||
|
||||
@@ -542,7 +603,7 @@ ServerBase::publish_result(const GoalUUID & uuid, std::shared_ptr<void> result_m
|
||||
void
|
||||
ServerBase::notify_goal_terminal_state()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->reentrant_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
rcl_ret_t ret = rcl_action_notify_goal_done(pimpl_->action_server_.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
@@ -552,7 +613,7 @@ ServerBase::notify_goal_terminal_state()
|
||||
void
|
||||
ServerBase::publish_feedback(std::shared_ptr<void> feedback_msg)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->reentrant_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
|
||||
rcl_ret_t ret = rcl_action_publish_feedback(pimpl_->action_server_.get(), feedback_msg.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to publish feedback");
|
||||
|
||||
23
rclcpp_action/test/benchmark/CMakeLists.txt
Normal file
23
rclcpp_action/test/benchmark/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
||||
find_package(performance_test_fixture REQUIRED)
|
||||
|
||||
# These benchmarks are only being created and run for the default RMW
|
||||
# implementation. We are looking to test the performance of the ROS 2 code, not
|
||||
# the underlying middleware.
|
||||
|
||||
add_performance_test(
|
||||
benchmark_action_client
|
||||
benchmark_action_client.cpp
|
||||
TIMEOUT 120)
|
||||
if(TARGET benchmark_action_client)
|
||||
target_link_libraries(benchmark_action_client ${PROJECT_NAME})
|
||||
ament_target_dependencies(benchmark_action_client rclcpp test_msgs)
|
||||
endif()
|
||||
|
||||
add_performance_test(
|
||||
benchmark_action_server
|
||||
benchmark_action_server.cpp
|
||||
TIMEOUT 120)
|
||||
if(TARGET benchmark_action_server)
|
||||
target_link_libraries(benchmark_action_server ${PROJECT_NAME})
|
||||
ament_target_dependencies(benchmark_action_server rclcpp test_msgs)
|
||||
endif()
|
||||
354
rclcpp_action/test/benchmark/benchmark_action_client.cpp
Normal file
354
rclcpp_action/test/benchmark/benchmark_action_client.cpp
Normal file
@@ -0,0 +1,354 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "performance_test_fixture/performance_test_fixture.hpp"
|
||||
#include "rclcpp_action/rclcpp_action.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "test_msgs/action/fibonacci.hpp"
|
||||
|
||||
using performance_test_fixture::PerformanceTest;
|
||||
|
||||
using Fibonacci = test_msgs::action::Fibonacci;
|
||||
using GoalHandle = rclcpp_action::ServerGoalHandle<Fibonacci>;
|
||||
using CancelResponse = typename Fibonacci::Impl::CancelGoalService::Response;
|
||||
using GoalUUID = rclcpp_action::GoalUUID;
|
||||
|
||||
constexpr char fibonacci_action_name[] = "fibonacci";
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
test_msgs::action::Fibonacci::Goal GetGoalOfOrder(int order)
|
||||
{
|
||||
test_msgs::action::Fibonacci::Goal goal;
|
||||
goal.order = order;
|
||||
return goal;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
class ActionClientPerformanceTest : public PerformanceTest
|
||||
{
|
||||
public:
|
||||
void SetUp(benchmark::State & state)
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
// Use same node for server and client to avoid interprocess communication
|
||||
node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
performance_test_fixture::PerformanceTest::SetUp(state);
|
||||
}
|
||||
|
||||
void SetUpServer(const std::string & action_name)
|
||||
{
|
||||
// This action server accepts and defers so that execution can be timed separately from
|
||||
// accepting the goal
|
||||
action_server = rclcpp_action::create_server<Fibonacci>(
|
||||
node, action_name,
|
||||
[](const GoalUUID &, std::shared_ptr<const Fibonacci::Goal> goal) {
|
||||
if (goal->order <= 0) {
|
||||
return rclcpp_action::GoalResponse::REJECT;
|
||||
}
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_DEFER;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
},
|
||||
[this](std::shared_ptr<GoalHandle> goal_handle) {
|
||||
current_goal_handle = goal_handle;
|
||||
});
|
||||
}
|
||||
|
||||
void ComputeFibonacciAndSetSuccess()
|
||||
{
|
||||
// This method is suprisingly slow, primarily due to the goal_handle->execute/succeed calls.
|
||||
current_goal_handle->execute();
|
||||
const auto goal = current_goal_handle->get_goal();
|
||||
auto result = std::make_shared<Fibonacci::Result>();
|
||||
|
||||
// Should be checked by the server above
|
||||
assert(goal->order > 0);
|
||||
result->sequence.resize(goal->order);
|
||||
result->sequence[0] = 0;
|
||||
if (goal->order == 1) {
|
||||
current_goal_handle->succeed(result);
|
||||
return;
|
||||
}
|
||||
result->sequence[1] = 1;
|
||||
if (goal->order == 2) {
|
||||
current_goal_handle->succeed(result);
|
||||
return;
|
||||
}
|
||||
for (int i = 2; i < goal->order; ++i) {
|
||||
result->sequence[i] =
|
||||
result->sequence[i - 1] + result->sequence[i - 2];
|
||||
}
|
||||
current_goal_handle->succeed(result);
|
||||
}
|
||||
|
||||
void TearDown(benchmark::State & state)
|
||||
{
|
||||
performance_test_fixture::PerformanceTest::TearDown(state);
|
||||
// Ensure proper sequencing of destruction
|
||||
current_goal_handle.reset();
|
||||
action_server.reset();
|
||||
node.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<rclcpp::Node> node;
|
||||
std::shared_ptr<rclcpp_action::Server<test_msgs::action::Fibonacci>> action_server;
|
||||
|
||||
// Goal handle needs to be kept alive by the server in order for client request specific to the
|
||||
// goal to succeed.
|
||||
std::shared_ptr<GoalHandle> current_goal_handle;
|
||||
};
|
||||
|
||||
BENCHMARK_F(ActionClientPerformanceTest, construct_client_without_server)(benchmark::State & state)
|
||||
{
|
||||
constexpr char action_name[] = "no_corresponding_server";
|
||||
for (auto _ : state) {
|
||||
auto client = rclcpp_action::create_client<Fibonacci>(node, action_name);
|
||||
|
||||
// Only timing construction, so destruction needs to happen explicitly while timing is paused
|
||||
state.PauseTiming();
|
||||
client.reset();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionClientPerformanceTest, construct_client_with_server)(benchmark::State & state)
|
||||
{
|
||||
SetUpServer(fibonacci_action_name);
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
auto client = rclcpp_action::create_client<Fibonacci>(node, fibonacci_action_name);
|
||||
|
||||
// Only timing construction, so destruction needs to happen explicitly while timing is paused
|
||||
state.PauseTiming();
|
||||
client.reset();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionClientPerformanceTest, destroy_client)(benchmark::State & state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
// This client does not have a corresponding server
|
||||
state.PauseTiming();
|
||||
auto client = rclcpp_action::create_client<Fibonacci>(node, fibonacci_action_name);
|
||||
state.ResumeTiming();
|
||||
|
||||
client.reset();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionClientPerformanceTest, async_send_goal_only)(benchmark::State & state)
|
||||
{
|
||||
auto client = rclcpp_action::create_client<Fibonacci>(node, fibonacci_action_name);
|
||||
SetUpServer(fibonacci_action_name);
|
||||
if (!client->wait_for_action_server(std::chrono::seconds(1))) {
|
||||
state.SkipWithError("Waiting for server timed out");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto goal = GetGoalOfOrder(5);
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
auto future_goal_handle = client->async_send_goal(goal);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionClientPerformanceTest, async_send_goal_rejected)(benchmark::State & state)
|
||||
{
|
||||
auto client = rclcpp_action::create_client<Fibonacci>(node, fibonacci_action_name);
|
||||
SetUpServer(fibonacci_action_name);
|
||||
if (!client->wait_for_action_server(std::chrono::seconds(1))) {
|
||||
state.SkipWithError("Waiting for server timed out");
|
||||
return;
|
||||
}
|
||||
|
||||
// Order of 0 is invalid
|
||||
const auto goal = GetGoalOfOrder(0);
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
auto future_goal_handle = client->async_send_goal(goal);
|
||||
rclcpp::spin_until_future_complete(node, future_goal_handle, std::chrono::seconds(1));
|
||||
if (!future_goal_handle.valid()) {
|
||||
state.SkipWithError("Shared future was invalid");
|
||||
return;
|
||||
}
|
||||
if (nullptr != future_goal_handle.get()) {
|
||||
state.SkipWithError("Invalid goal was not rejected");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionClientPerformanceTest, async_send_goal_get_accepted_response)(
|
||||
benchmark::State & state)
|
||||
{
|
||||
auto client = rclcpp_action::create_client<Fibonacci>(node, fibonacci_action_name);
|
||||
SetUpServer(fibonacci_action_name);
|
||||
if (!client->wait_for_action_server(std::chrono::seconds(1))) {
|
||||
state.SkipWithError("Waiting for server timed out");
|
||||
return;
|
||||
}
|
||||
|
||||
const auto goal = GetGoalOfOrder(10);
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
// This server's execution is deferred
|
||||
auto future_goal_handle = client->async_send_goal(goal);
|
||||
rclcpp::spin_until_future_complete(node, future_goal_handle, std::chrono::seconds(1));
|
||||
|
||||
if (!future_goal_handle.valid()) {
|
||||
state.SkipWithError("Shared future was invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
auto goal_handle = future_goal_handle.get();
|
||||
if (rclcpp_action::GoalStatus::STATUS_ACCEPTED != goal_handle->get_status()) {
|
||||
state.SkipWithError("Valid goal was not accepted");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionClientPerformanceTest, async_get_result)(benchmark::State & state)
|
||||
{
|
||||
auto client = rclcpp_action::create_client<Fibonacci>(node, fibonacci_action_name);
|
||||
SetUpServer(fibonacci_action_name);
|
||||
if (!client->wait_for_action_server(std::chrono::seconds(1))) {
|
||||
state.SkipWithError("Waiting for server timed out");
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int expected_order = 5;
|
||||
const auto goal = GetGoalOfOrder(expected_order);
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
// Send goal, accept and execute while timing is paused
|
||||
state.PauseTiming();
|
||||
auto future_goal_handle = client->async_send_goal(goal);
|
||||
|
||||
// Action server accepts and defers, so this spin doesn't include result
|
||||
rclcpp::spin_until_future_complete(node, future_goal_handle, std::chrono::seconds(1));
|
||||
|
||||
if (!future_goal_handle.valid()) {
|
||||
state.SkipWithError("Shared future was invalid");
|
||||
return;
|
||||
}
|
||||
auto goal_handle = future_goal_handle.get();
|
||||
if (nullptr == goal_handle) {
|
||||
state.SkipWithError("Goal handle was a nullptr");
|
||||
break;
|
||||
}
|
||||
|
||||
// Perform actual execution and set success
|
||||
ComputeFibonacciAndSetSuccess();
|
||||
state.ResumeTiming();
|
||||
|
||||
// Measure how long it takes client to receive the succeeded result
|
||||
auto future_result = client->async_get_result(goal_handle);
|
||||
rclcpp::spin_until_future_complete(node, future_result, std::chrono::seconds(1));
|
||||
const auto & wrapped_result = future_result.get();
|
||||
if (rclcpp_action::ResultCode::SUCCEEDED != wrapped_result.code) {
|
||||
state.SkipWithError("Fibonacci action did not succeed");
|
||||
break;
|
||||
}
|
||||
|
||||
const auto & sequence = wrapped_result.result->sequence;
|
||||
if (sequence.size() != expected_order || sequence.back() != 3) {
|
||||
state.SkipWithError("Fibonacci result was not correct");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionClientPerformanceTest, async_cancel_goal)(benchmark::State & state)
|
||||
{
|
||||
auto client = rclcpp_action::create_client<Fibonacci>(node, fibonacci_action_name);
|
||||
SetUpServer(fibonacci_action_name);
|
||||
if (!client->wait_for_action_server(std::chrono::seconds(1))) {
|
||||
state.SkipWithError("Waiting for server timed out");
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int expected_order = 5;
|
||||
const auto goal = GetGoalOfOrder(expected_order);
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
auto future_goal_handle = client->async_send_goal(goal);
|
||||
|
||||
// Action server accepts and defers, so action can be canceled
|
||||
rclcpp::spin_until_future_complete(node, future_goal_handle, std::chrono::seconds(1));
|
||||
auto goal_handle = future_goal_handle.get();
|
||||
state.ResumeTiming();
|
||||
|
||||
auto future_cancel = client->async_cancel_goal(goal_handle);
|
||||
rclcpp::spin_until_future_complete(node, future_cancel, std::chrono::seconds(1));
|
||||
auto cancel_response = future_cancel.get();
|
||||
|
||||
using CancelResponse = test_msgs::action::Fibonacci::Impl::CancelGoalService::Response;
|
||||
if (CancelResponse::ERROR_NONE != cancel_response->return_code) {
|
||||
state.SkipWithError("Cancel request did not succeed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionClientPerformanceTest, async_cancel_all_goals)(benchmark::State & state)
|
||||
{
|
||||
auto client = rclcpp_action::create_client<Fibonacci>(node, fibonacci_action_name);
|
||||
SetUpServer(fibonacci_action_name);
|
||||
if (!client->wait_for_action_server(std::chrono::seconds(1))) {
|
||||
state.SkipWithError("Waiting for server timed out");
|
||||
return;
|
||||
}
|
||||
|
||||
constexpr int expected_order = 5;
|
||||
const auto goal = GetGoalOfOrder(expected_order);
|
||||
constexpr int num_concurrently_inflight_goals = 10u;
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
for (int i = 0; i < num_concurrently_inflight_goals; ++i) {
|
||||
auto future_goal_handle = client->async_send_goal(goal);
|
||||
rclcpp::spin_until_future_complete(node, future_goal_handle, std::chrono::seconds(1));
|
||||
}
|
||||
// Action server accepts and defers, so action can be canceled
|
||||
state.ResumeTiming();
|
||||
|
||||
auto future_cancel_all = client->async_cancel_all_goals();
|
||||
rclcpp::spin_until_future_complete(node, future_cancel_all, std::chrono::seconds(1));
|
||||
auto cancel_response = future_cancel_all.get();
|
||||
|
||||
using CancelResponse = test_msgs::action::Fibonacci::Impl::CancelGoalService::Response;
|
||||
if (CancelResponse::ERROR_NONE != cancel_response->return_code) {
|
||||
state.SkipWithError("Cancel request did not succeed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
318
rclcpp_action/test/benchmark/benchmark_action_server.cpp
Normal file
318
rclcpp_action/test/benchmark/benchmark_action_server.cpp
Normal file
@@ -0,0 +1,318 @@
|
||||
// Copyright 2020 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "performance_test_fixture/performance_test_fixture.hpp"
|
||||
#include "rclcpp_action/rclcpp_action.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "test_msgs/action/fibonacci.hpp"
|
||||
|
||||
using performance_test_fixture::PerformanceTest;
|
||||
|
||||
using Fibonacci = test_msgs::action::Fibonacci;
|
||||
using GoalHandle = rclcpp_action::ServerGoalHandle<Fibonacci>;
|
||||
using CancelResponse = typename Fibonacci::Impl::CancelGoalService::Response;
|
||||
using GoalUUID = rclcpp_action::GoalUUID;
|
||||
|
||||
constexpr char fibonacci_action_name[] = "fibonacci";
|
||||
|
||||
class ActionServerPerformanceTest : public PerformanceTest
|
||||
{
|
||||
public:
|
||||
void SetUp(benchmark::State & state)
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
action_client =
|
||||
rclcpp_action::create_client<Fibonacci>(node, fibonacci_action_name);
|
||||
performance_test_fixture::PerformanceTest::SetUp(state);
|
||||
}
|
||||
|
||||
void TearDown(benchmark::State & state)
|
||||
{
|
||||
performance_test_fixture::PerformanceTest::TearDown(state);
|
||||
|
||||
action_client.reset();
|
||||
node.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
auto AsyncSendGoalOfOrder(const int order)
|
||||
{
|
||||
test_msgs::action::Fibonacci::Goal goal;
|
||||
goal.order = order;
|
||||
|
||||
return action_client->async_send_goal(goal);
|
||||
}
|
||||
|
||||
protected:
|
||||
std::shared_ptr<rclcpp::Node> node;
|
||||
std::shared_ptr<rclcpp_action::Client<Fibonacci>> action_client;
|
||||
};
|
||||
|
||||
BENCHMARK_F(ActionServerPerformanceTest, construct_server_without_client)(benchmark::State & state)
|
||||
{
|
||||
constexpr char action_name[] = "no_corresponding_client";
|
||||
for (auto _ : state) {
|
||||
auto action_server = rclcpp_action::create_server<Fibonacci>(
|
||||
node, action_name,
|
||||
[](const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>) {
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_DEFER;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {});
|
||||
benchmark::DoNotOptimize(action_server);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
state.PauseTiming();
|
||||
action_server.reset();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionServerPerformanceTest, construct_server_with_client)(benchmark::State & state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
auto action_server = rclcpp_action::create_server<Fibonacci>(
|
||||
node, fibonacci_action_name,
|
||||
[](const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>) {
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_DEFER;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {});
|
||||
benchmark::DoNotOptimize(action_server);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
state.PauseTiming();
|
||||
action_server.reset();
|
||||
state.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionServerPerformanceTest, destroy_server)(benchmark::State & state)
|
||||
{
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
auto action_server = rclcpp_action::create_server<Fibonacci>(
|
||||
node, fibonacci_action_name,
|
||||
[](const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>) {
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_DEFER;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {});
|
||||
state.ResumeTiming();
|
||||
benchmark::DoNotOptimize(action_server);
|
||||
benchmark::ClobberMemory();
|
||||
|
||||
action_server.reset();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionServerPerformanceTest, action_server_accept_goal)(benchmark::State & state)
|
||||
{
|
||||
std::shared_ptr<GoalHandle> current_goal_handle = nullptr;
|
||||
auto action_server = rclcpp_action::create_server<Fibonacci>(
|
||||
node, fibonacci_action_name,
|
||||
[](const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>) {
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_DEFER;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
},
|
||||
[¤t_goal_handle](std::shared_ptr<GoalHandle> goal_handle) {
|
||||
current_goal_handle = goal_handle;
|
||||
});
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
auto client_goal_handle_future = AsyncSendGoalOfOrder(1);
|
||||
state.ResumeTiming();
|
||||
|
||||
rclcpp::spin_until_future_complete(node, client_goal_handle_future);
|
||||
auto goal_handle = client_goal_handle_future.get();
|
||||
if (rclcpp_action::GoalStatus::STATUS_ACCEPTED != goal_handle->get_status()) {
|
||||
state.SkipWithError("Valid goal was not accepted");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionServerPerformanceTest, action_server_cancel_goal)(benchmark::State & state)
|
||||
{
|
||||
// The goal handle needs to be assigned to a variable for the lifetime of the goal so that it is
|
||||
// not cleaned up before the cancel request is received and processed.
|
||||
std::shared_ptr<GoalHandle> server_goal_handle = nullptr;
|
||||
auto action_server = rclcpp_action::create_server<Fibonacci>(
|
||||
node, fibonacci_action_name,
|
||||
[](const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>) {
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_DEFER;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
},
|
||||
[&server_goal_handle](std::shared_ptr<GoalHandle> goal_handle) {
|
||||
server_goal_handle = goal_handle;
|
||||
});
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
auto client_goal_handle_future = AsyncSendGoalOfOrder(1);
|
||||
// This spin completes when the goal has been accepted, but not executed because server
|
||||
// responds with ACCEPT_AND_DEFER
|
||||
rclcpp::spin_until_future_complete(node, client_goal_handle_future, std::chrono::seconds(1));
|
||||
auto client_goal_handle = client_goal_handle_future.get();
|
||||
auto future_cancel = action_client->async_cancel_goal(client_goal_handle);
|
||||
state.ResumeTiming();
|
||||
|
||||
rclcpp::spin_until_future_complete(node, future_cancel, std::chrono::seconds(1));
|
||||
auto cancel_response = future_cancel.get();
|
||||
using CancelResponse = test_msgs::action::Fibonacci::Impl::CancelGoalService::Response;
|
||||
if (CancelResponse::ERROR_NONE != cancel_response->return_code) {
|
||||
state.SkipWithError("Cancel request did not succeed");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionServerPerformanceTest, action_server_execute_goal)(benchmark::State & state)
|
||||
{
|
||||
std::shared_ptr<GoalHandle> server_goal_handle = nullptr;
|
||||
auto action_server = rclcpp_action::create_server<Fibonacci>(
|
||||
node, fibonacci_action_name,
|
||||
[](const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>) {
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_DEFER;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
},
|
||||
[&server_goal_handle](std::shared_ptr<GoalHandle> goal_handle) {
|
||||
server_goal_handle = goal_handle;
|
||||
});
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
auto client_goal_handle_future = AsyncSendGoalOfOrder(1);
|
||||
|
||||
rclcpp::spin_until_future_complete(node, client_goal_handle_future);
|
||||
auto goal_handle = client_goal_handle_future.get();
|
||||
if (rclcpp_action::GoalStatus::STATUS_ACCEPTED != goal_handle->get_status()) {
|
||||
state.SkipWithError("Valid goal was not accepted");
|
||||
return;
|
||||
}
|
||||
state.ResumeTiming();
|
||||
|
||||
server_goal_handle->execute();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionServerPerformanceTest, action_server_set_success)(benchmark::State & state)
|
||||
{
|
||||
constexpr int goal_order = 1;
|
||||
std::shared_ptr<GoalHandle> server_goal_handle = nullptr;
|
||||
auto action_server = rclcpp_action::create_server<Fibonacci>(
|
||||
node, fibonacci_action_name,
|
||||
[](const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>) {
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_DEFER;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
},
|
||||
[&server_goal_handle](std::shared_ptr<GoalHandle> goal_handle) {
|
||||
server_goal_handle = goal_handle;
|
||||
});
|
||||
|
||||
// MSVC and Clang disagree how goal_order should be captured here. Though this capture is a bit
|
||||
// too wide, they at least could agree it was fine. In my testing MSVC errored if goal_order was
|
||||
// not captured, but clang would warn if it was explicitly captured.
|
||||
const auto result = [&]() {
|
||||
auto result = std::make_shared<Fibonacci::Result>();
|
||||
for (int i = 0; i < goal_order; ++i) {
|
||||
// Not the fibonacci sequence, but that's not important to this benchmark
|
||||
result->sequence.push_back(i);
|
||||
}
|
||||
return result;
|
||||
} ();
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
auto client_goal_handle_future = AsyncSendGoalOfOrder(goal_order);
|
||||
|
||||
rclcpp::spin_until_future_complete(node, client_goal_handle_future);
|
||||
auto goal_handle = client_goal_handle_future.get();
|
||||
if (rclcpp_action::GoalStatus::STATUS_ACCEPTED != goal_handle->get_status()) {
|
||||
state.SkipWithError("Valid goal was not accepted");
|
||||
return;
|
||||
}
|
||||
server_goal_handle->execute();
|
||||
state.ResumeTiming();
|
||||
|
||||
server_goal_handle->succeed(result);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(ActionServerPerformanceTest, action_server_abort)(benchmark::State & state)
|
||||
{
|
||||
constexpr int goal_order = 1;
|
||||
std::shared_ptr<GoalHandle> server_goal_handle = nullptr;
|
||||
auto action_server = rclcpp_action::create_server<Fibonacci>(
|
||||
node, fibonacci_action_name,
|
||||
[](const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>) {
|
||||
return rclcpp_action::GoalResponse::ACCEPT_AND_DEFER;
|
||||
},
|
||||
[](std::shared_ptr<GoalHandle>) {
|
||||
return rclcpp_action::CancelResponse::ACCEPT;
|
||||
},
|
||||
[&server_goal_handle](std::shared_ptr<GoalHandle> goal_handle) {
|
||||
server_goal_handle = goal_handle;
|
||||
});
|
||||
|
||||
// Capturing with & because MSVC and Clang disagree about how to capture goal_order
|
||||
const auto result = [&]() {
|
||||
auto result = std::make_shared<Fibonacci::Result>();
|
||||
for (int i = 0; i < goal_order; ++i) {
|
||||
// Not the fibonacci sequence, but that's not important to this benchmark
|
||||
result->sequence.push_back(i);
|
||||
}
|
||||
return result;
|
||||
} ();
|
||||
|
||||
reset_heap_counters();
|
||||
for (auto _ : state) {
|
||||
state.PauseTiming();
|
||||
auto client_goal_handle_future = AsyncSendGoalOfOrder(goal_order);
|
||||
|
||||
rclcpp::spin_until_future_complete(node, client_goal_handle_future);
|
||||
auto goal_handle = client_goal_handle_future.get();
|
||||
if (rclcpp_action::GoalStatus::STATUS_ACCEPTED != goal_handle->get_status()) {
|
||||
state.SkipWithError("Valid goal was not accepted");
|
||||
return;
|
||||
}
|
||||
server_goal_handle->execute();
|
||||
state.ResumeTiming();
|
||||
|
||||
server_goal_handle->abort(result);
|
||||
}
|
||||
}
|
||||
@@ -332,7 +332,7 @@ TEST_F(TestClient, construction_and_destruction_callback_group)
|
||||
TEST_F(TestClient, construction_and_destruction_rcl_errors)
|
||||
{
|
||||
{
|
||||
auto mock = mocking_utils::inject_on_return(
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp_action", rcl_action_client_fini, RCL_RET_ERROR);
|
||||
// It just logs an error message and continues
|
||||
EXPECT_NO_THROW(
|
||||
|
||||
@@ -1156,7 +1156,7 @@ TEST_F(TestGoalRequestServer, publish_status_publish_status_errors)
|
||||
|
||||
TEST_F(TestGoalRequestServer, execute_goal_request_received_take_failed)
|
||||
{
|
||||
auto mock = mocking_utils::inject_on_return(
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp_action", rcl_action_take_goal_request, RCL_RET_ACTION_SERVER_TAKE_FAILED);
|
||||
try {
|
||||
SendClientGoalRequest(std::chrono::milliseconds(100));
|
||||
|
||||
@@ -2,6 +2,23 @@
|
||||
Changelog for package rclcpp_components
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
6.1.0 (2020-12-10)
|
||||
------------------
|
||||
* Update QDs to QL 1 (`#1477 <https://github.com/ros2/rclcpp/issues/1477>`_)
|
||||
* Add benchmarks for components (`#1476 <https://github.com/ros2/rclcpp/issues/1476>`_)
|
||||
* Contributors: Scott K Logan, Stephen Brawner
|
||||
|
||||
6.0.0 (2020-11-18)
|
||||
------------------
|
||||
* Bump rclcpp packages to Quality Level 2 (`#1445 <https://github.com/ros2/rclcpp/issues/1445>`_)
|
||||
* Contributors: Louise Poubel
|
||||
|
||||
5.1.0 (2020-11-02)
|
||||
------------------
|
||||
* Update maintainers (`#1384 <https://github.com/ros2/rclcpp/issues/1384>`_)
|
||||
* ComponentManager: switch off parameter services and event publisher (`#1333 <https://github.com/ros2/rclcpp/issues/1333>`_)
|
||||
* Contributors: Ivan Santiago Paunovic, Martijn Buijs
|
||||
|
||||
5.0.0 (2020-09-18)
|
||||
------------------
|
||||
|
||||
|
||||
@@ -65,6 +65,10 @@ endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
find_package(ament_cmake_google_benchmark REQUIRED)
|
||||
find_package(benchmark REQUIRED)
|
||||
# Give cppcheck hints about macro definitions coming from outside this package
|
||||
get_target_property(ament_cmake_cppcheck_ADDITIONAL_INCLUDE_DIRS benchmark::benchmark INTERFACE_INCLUDE_DIRECTORIES)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
|
||||
set(components "")
|
||||
@@ -109,6 +113,14 @@ if(BUILD_TESTING)
|
||||
if(TARGET test_component_manager_api)
|
||||
target_link_libraries(test_component_manager_api component_manager)
|
||||
endif()
|
||||
|
||||
ament_add_google_benchmark(benchmark_components
|
||||
test/benchmark/benchmark_components.cpp
|
||||
APPEND_ENV AMENT_PREFIX_PATH=${CMAKE_CURRENT_BINARY_DIR}/test_ament_index/$<CONFIG>
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET benchmark_components)
|
||||
target_link_libraries(benchmark_components component_manager)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
install(
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user