Compare commits

...

44 Commits

Author SHA1 Message Date
Chris Lalancette
83af414811 6.3.0 2021-01-25 21:20:57 +00:00
Chris Lalancette
a7500478d8 Changelog.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2021-01-25 21:20:40 +00:00
Christophe Bedard
b1ff2d5bdc Add instrumentation for linking a timer to a node (#1500)
Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>
2021-01-20 17:56:13 -03:00
mauropasse
f38cd8a8bb Fix error when using IPC with StaticSingleThreadExecutor (#1520)
Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
2021-01-20 17:24:02 -03:00
Chris Lalancette
064a021c7a Change to using unique_ptrs for DummyExecutor. (#1517)
* Change to using unique_ptrs for DummyExecutor.

clang static analysis suggested that there was a possible memory
leak here, and it is right.  The test is expecting to throw during
the constructor, in which case the memory would be automatically
freed.  However, if the test did *not* throw for some reason,
we would leak the memory.  Switch to using a unique_ptr here
which will free the memory in all cases at the end of the scope.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2021-01-20 10:49:48 -05:00
Ivan Santiago Paunovic
5cb2274e8c Allow reconfiguring 'clock' topic qos (#1512)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-01-18 13:51:51 -03:00
Daisuke Sato
ca3ad7da2f Fix action server deadlock (#1285) (#1313)
* unlock action_server_reentrant_mutex_ before calling user callback functions
add an additional lock to keep previous behavior broken by deadlock fix

Also add a test case to reproduce deadlock situation in rclcpp_action

Signed-off-by: Daisuke Sato <daisukes@cmu.edu>
2021-01-14 17:18:19 -05:00
Ivan Santiago Paunovic
7ccd64c9c1 Allow to add/remove nodes thread safely in rclcpp::Executor (#1505)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-01-14 13:35:34 -03:00
eboasson
8d2c682c09 Call rclcpp::shutdown in test_node for clean shutdown on Windows (#1515)
The graph_listener thread is started in the background in some of the tests and
this thread is killed by Windows prior to executing global destructors if it is
still running when leaving main().  This then corrupts state because the RMW
layer is blocking in a waitset and causes Cyclone to hang trying to destroy the
waitset.

Signed-off-by: Erik Boasson <eb@ilities.com>
2021-01-13 15:02:45 -05:00
Ivan Santiago Paunovic
56a037a3da Reapply "Add get_logging_directory method to rclcpp::Logger (#1509)" (#1513)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-01-13 10:24:09 -03:00
tomoya
8d5af66858 use describe_parameters of parameter client for test (#1499)
* use describe_parameters of parameter client for test

Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>
2021-01-12 09:54:36 -05:00
Ivan Santiago Paunovic
438822fe13 Revert "Add get_logging_directory method to rclcpp::Logger (#1509)" (#1511)
This reverts commit 7a31d7c01b.

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-01-11 10:05:29 -05:00
Ivan Santiago Paunovic
7a31d7c01b Add get_logging_directory method to rclcpp::Logger (#1509)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-01-08 16:24:40 -03:00
Chris Lalancette
700e3c47bf 6.2.0 2021-01-08 19:12:56 +00:00
Chris Lalancette
069b20b7af Changelog.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2021-01-08 19:12:42 +00:00
Nikolai Morin
bcceecb24b Better documentation for the QoS class (#1508)
* Better documentation for the QoS class

Signed-off-by: Nikolai Morin <nikolai.morin@apex.ai>

* PR Review suggestions

Co-authored-by: William Woodall <william+github@osrfoundation.org>
Signed-off-by: Nikolai Morin <nikolai.morin@apex.ai>

* Shorter brief

Signed-off-by: nnmm <nnmmgit@gmail.com>

Co-authored-by: William Woodall <william+github@osrfoundation.org>
2021-01-07 15:30:38 -08:00
hsgwa
7ed61e52fe Modify excluding callback duration from topic statistics (#1492)
Signed-off-by: hsgwa <hasegawa@isp.co.jp>
2021-01-07 15:04:18 -03:00
Chris Lalancette
85c32a94a5 Make the test of graph users more robust. (#1504)
In particular, we don't know for sure that the graph will only
have 1 user; we just know that it should have *at least* one
user.  So change that check to be more robust.  This also
lets us get rid of the slightly strange call to 'notify_graph_change'
that was being used to cleanup graph users.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2021-01-06 10:30:24 -05:00
Chris Lalancette
5821361dcb Make sure to wait for graph change events in test_node_graph. (#1503)
* Make sure to wait for graph change events in test_node_graph.

While debugging something else, I came across this fairly rare
flake in the test_node_graph tests.  Essentially, it may take
some time for node changes to propagate into the graph.  If
the NodeGraph client asks for data before the changes are
in the graph, they may get something they don't expect.  The
fix is to wait for an event on the NodeGraph to happen, and
then look for the data you are interested in.

Before this change, I could get the flake to happen on a loaded
aarch64 system fairly regularly.  After this change, I can no
longer make the flake happen on a loaded system.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2021-01-06 08:41:31 -05:00
Josh Langsfeld
b9ef1fedfa Use std compliant non-method std::filesystem::exists function (#1502)
Signed-off-by: Josh Langsfeld <josh.langsfeld@gmail.com>
2021-01-05 11:18:46 -05:00
tomoya
adebda52c5 add timeout to SyncParametersClient methods (#1493)
* add timeout to SyncParametersClient methods

Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>

* update parameter client test with timeout.

Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>

* use template thin interface to keep the implementations in library.

Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>

* test duration should be long enough not to detect unnecessary timeout

Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>
2020-12-22 13:13:37 -08:00
Ivan Santiago Paunovic
715b0e9008 Fix wrong test expectations (#1497)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-12-21 19:33:54 -03:00
Ivan Santiago Paunovic
6257103e76 Goal response callback compatibility shim with deprecation of old signature (#1495)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-12-21 14:54:02 -03:00
Audrow Nash
543a3c39c1 [rclcpp_action] Add warnings (#1405)
* Add warnings

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Simplify for loop in test_client.cpp

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Fix conversion warning in test_client static cast to size_t

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Fix new warnings after rebasing on master

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Fix shadowing in the benchmark action server

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Static cast goal order to size_t

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Remove unnecessary include

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>
2020-12-18 21:59:34 -08:00
Ivan Santiago Paunovic
62c958be30 Update create_publisher/subscription documentation, clarifying when a parameters interface is required (#1494)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-12-18 12:05:33 -08:00
Audrow Nash
febe86e6b5 Fix string literal warnings (#1442)
* Add warnings

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Fix nonliteral string warnings

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Fix compile error from new logging functions in rclcpp_components

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Use "%s" to make log string evaluation secure

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Fix logging warnings in rclcpp

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Fix coding style to pass linter

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Get the c_str of streams

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Remove test to check string logging

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>

* Add in stream logging tests

Signed-off-by: Audrow Nash <audrow.nash@gmail.com>
2020-12-18 09:44:59 -08:00
tomoya
504b082d8b support describe_parameters methods to parameter client. (#1453)
* support describe_parameters methods to parameter client.

Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>
2020-12-14 12:30:54 -05:00
Chris Lalancette
9c62c1c946 6.1.0 2020-12-10 17:13:45 +00:00
Chris Lalancette
5b6e0af339 Changelog.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-12-10 17:13:33 +00:00
Ivan Santiago Paunovic
c64ba72bbd Add getters to rclcpp::qos and rclcpp::*Policy enum classes (#1467)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-12-10 12:05:07 -03:00
Chris Lalancette
eddb938aec Change nullptr checks to use ASSERT_TRUE. (#1486)
* Change nullptr checks to use ASSERT_TRUE.

clang static analysis gets a bit confused going through the
gtest macros, so switch from ASSERT_NE to ASSERT_TRUE as
we've done elsewhere.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2020-12-08 09:06:16 -05:00
tomoya
0dc782aca8 add LifecycleNode::get_transition_graph to match services. (#1472)
* add LifecycleNode::get_transition_graph along with service.

Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>

* add concrete test cases for get_available_transitions with each primary state.

Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>
2020-12-02 11:25:01 -08:00
Stephen Brawner
ea0ee50318 Adjust logic around finding and erasing guard_condition (#1474)
Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-11-30 10:30:26 -08:00
Stephen Brawner
35c73aa61e Update QDs to QL 1 (#1477)
Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-11-25 16:34:19 -08:00
Scott K Logan
08963df926 Add benchmarks for components (#1476)
Signed-off-by: Scott K Logan <logans@cottsay.net>
2020-11-25 14:51:59 -08:00
Scott K Logan
f5e35bda86 Add performance tests for parameter transport (#1463)
Note that these tests are written without using
performance_test_fixture. Because the parameter server is running in the
same process, any allocations happening in the spin thread for the
server get picked up by the allocation statistics even though those
functions aren't invoked in the tests.

If we can find a way to turn off the memory tracking on a per-thread
basis, we can enable memory tracking. Until then, leaving the memory
statistics enabled could be misleading.

Signed-off-by: Scott K Logan <logans@cottsay.net>
2020-11-18 20:39:57 -08:00
brawner
a7db1dcca2 Benchmark lifecycle features (#1462)
* Benchmark lifecycle features

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* Cleanup

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-11-18 18:41:29 -08:00
Ivan Santiago Paunovic
ba1056fe63 6.0.0 2020-11-18 21:51:48 +00:00
Ivan Santiago Paunovic
048d9b57c0 Changelogs
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-11-18 18:50:39 -03:00
brawner
de353f9e45 Reserve vector capacities and use emplace_back for constructing vectors (#1464)
* Reserve vector capacities and use emplace_back for constructing vectors

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* Use resize instead of reserve

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* Remove push_back

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-11-17 13:06:58 -08:00
brawner
cba6f20988 Move ownership of shutdown_guard_condition to executors/graph_listener (#1404)
Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-11-17 17:40:55 -03:00
Ivan Santiago Paunovic
71a58d40f1 Qos configurability (take 2) (#1465)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-11-17 17:12:30 -03:00
brawner
add6d61231 [rclcpp_lifecycle] Change uint8_t iterator variables to size_t (#1461)
* Change uint8_t iterator variables to size_t

Signed-off-by: Stephen Brawner <brawner@gmail.com>

* Change to unsigned int

Signed-off-by: Stephen Brawner <brawner@gmail.com>
2020-11-17 11:22:39 -08:00
Audrow Nash
8c8c268aad Add take_data to Waitable and data to AnyExecutable (#1241)
Signed-off-by: Audrow <audrow.nash@gmail.com>
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-11-17 11:42:03 -03:00
110 changed files with 4443 additions and 840 deletions

View File

@@ -2,6 +2,55 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6.3.0 (2021-01-25)
------------------
* Add instrumentation for linking a timer to a node (`#1500 <https://github.com/ros2/rclcpp/issues/1500>`_)
* Fix error when using IPC with StaticSingleThreadExecutor (`#1520 <https://github.com/ros2/rclcpp/issues/1520>`_)
* Change to using unique_ptrs for DummyExecutor. (`#1517 <https://github.com/ros2/rclcpp/issues/1517>`_)
* Allow reconfiguring 'clock' topic qos (`#1512 <https://github.com/ros2/rclcpp/issues/1512>`_)
* Allow to add/remove nodes thread safely in rclcpp::Executor (`#1505 <https://github.com/ros2/rclcpp/issues/1505>`_)
* Call rclcpp::shutdown in test_node for clean shutdown on Windows (`#1515 <https://github.com/ros2/rclcpp/issues/1515>`_)
* Reapply "Add get_logging_directory method to rclcpp::Logger (`#1509 <https://github.com/ros2/rclcpp/issues/1509>`_)" (`#1513 <https://github.com/ros2/rclcpp/issues/1513>`_)
* use describe_parameters of parameter client for test (`#1499 <https://github.com/ros2/rclcpp/issues/1499>`_)
* Revert "Add get_logging_directory method to rclcpp::Logger (`#1509 <https://github.com/ros2/rclcpp/issues/1509>`_)" (`#1511 <https://github.com/ros2/rclcpp/issues/1511>`_)
* Add get_logging_directory method to rclcpp::Logger (`#1509 <https://github.com/ros2/rclcpp/issues/1509>`_)
* Contributors: Chris Lalancette, Christophe Bedard, Ivan Santiago Paunovic, eboasson, mauropasse, tomoya
6.2.0 (2021-01-08)
------------------
* Better documentation for the QoS class (`#1508 <https://github.com/ros2/rclcpp/issues/1508>`_)
* Modify excluding callback duration from topic statistics (`#1492 <https://github.com/ros2/rclcpp/issues/1492>`_)
* Make the test of graph users more robust. (`#1504 <https://github.com/ros2/rclcpp/issues/1504>`_)
* Make sure to wait for graph change events in test_node_graph. (`#1503 <https://github.com/ros2/rclcpp/issues/1503>`_)
* add timeout to SyncParametersClient methods (`#1493 <https://github.com/ros2/rclcpp/issues/1493>`_)
* Fix wrong test expectations (`#1497 <https://github.com/ros2/rclcpp/issues/1497>`_)
* Update create_publisher/subscription documentation, clarifying when a parameters interface is required (`#1494 <https://github.com/ros2/rclcpp/issues/1494>`_)
* Fix string literal warnings (`#1442 <https://github.com/ros2/rclcpp/issues/1442>`_)
* support describe_parameters methods to parameter client. (`#1453 <https://github.com/ros2/rclcpp/issues/1453>`_)
* Contributors: Audrow Nash, Chris Lalancette, Ivan Santiago Paunovic, Nikolai Morin, hsgwa, tomoya
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>`_)

View File

@@ -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

View File

@@ -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 2** 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,43 +163,43 @@ 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 2**, 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 2**, 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 2**, 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 2**, 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`

View File

@@ -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 2** 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.

View File

@@ -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

View File

@@ -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_;
};

View File

@@ -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,15 +25,65 @@
#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()
* which returns a shared_ptr to a NodeTopicsInterface.
*
* In case `options.qos_overriding_options` is enabling qos parameter overrides,
* NodeT must also have a method called get_node_parameters_interface()
* which returns a shared_ptr to a NodeParametersInterface.
*/
template<
typename MessageT,
@@ -49,21 +100,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

View File

@@ -41,12 +41,115 @@
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()
* which returns a shared_ptr to a NodeTopicsInterface, or be a
* NodeTopicsInterface pointer itself.
*
* In case `options.qos_overriding_options` is enabling qos parameter overrides,
* NodeT must also have a method called get_node_parameters_interface()
* which returns a shared_ptr to a NodeParametersInterface.
*
* \tparam MessageT
* \tparam CallbackT
* \tparam AllocatorT
@@ -78,7 +181,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 +193,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

View 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_

View File

@@ -137,6 +137,9 @@ public:
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

View File

@@ -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

View File

@@ -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"
@@ -447,7 +448,7 @@ protected:
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(
WeakCallbackGroupsToNodesMap weak_groups_to_nodes,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
rclcpp::CallbackGroup::SharedPtr group);
/// Return true if the node has been added to this executor.
@@ -459,7 +460,7 @@ protected:
bool
has_node(
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
WeakCallbackGroupsToNodesMap weak_groups_to_nodes) const;
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) const;
RCLCPP_PUBLIC
rclcpp::CallbackGroup::SharedPtr
@@ -475,7 +476,7 @@ protected:
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
bool notify = true);
bool notify = true) RCPPUTILS_TSA_REQUIRES(mutex_);
/// Remove a callback group from the executor.
/**
@@ -486,7 +487,7 @@ protected:
remove_callback_group_from_map(
rclcpp::CallbackGroup::SharedPtr group_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
bool notify = true);
bool notify = true) RCPPUTILS_TSA_REQUIRES(mutex_);
RCLCPP_PUBLIC
bool
@@ -496,7 +497,7 @@ protected:
bool
get_next_ready_executable_from_map(
AnyExecutable & any_executable,
WeakCallbackGroupsToNodesMap weak_groups_to_nodes);
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
RCLCPP_PUBLIC
bool
@@ -517,7 +518,7 @@ protected:
*/
RCLCPP_PUBLIC
virtual void
add_callback_groups_from_nodes_associated_to_executor();
add_callback_groups_from_nodes_associated_to_executor() RCPPUTILS_TSA_REQUIRES(mutex_);
/// Spinning state, used to prevent multi threaded calls to spin and to cancel blocking spins.
std::atomic_bool spinning;
@@ -525,14 +526,17 @@ 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();
// Mutex to protect the subsequent memory_strategy_.
std::mutex memory_strategy_mutex_;
mutable std::mutex mutex_;
/// The memory strategy: an interface for handling user-defined memory allocation strategies.
memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;
memory_strategy::MemoryStrategy::SharedPtr
memory_strategy_ RCPPUTILS_TSA_PT_GUARDED_BY(mutex_);
/// The context associated with this executor.
std::shared_ptr<rclcpp::Context> context_;
@@ -549,19 +553,24 @@ protected:
WeakNodesToGuardConditionsMap;
/// maps nodes to guard conditions
WeakNodesToGuardConditionsMap weak_nodes_to_guard_conditions_;
WeakNodesToGuardConditionsMap
weak_nodes_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// maps callback groups associated to nodes
WeakCallbackGroupsToNodesMap weak_groups_associated_with_executor_to_nodes_;
WeakCallbackGroupsToNodesMap
weak_groups_associated_with_executor_to_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// maps callback groups to nodes associated with executor
WeakCallbackGroupsToNodesMap weak_groups_to_nodes_associated_with_executor_;
WeakCallbackGroupsToNodesMap
weak_groups_to_nodes_associated_with_executor_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// maps all callback groups to nodes
WeakCallbackGroupsToNodesMap weak_groups_to_nodes_;
WeakCallbackGroupsToNodesMap
weak_groups_to_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// nodes that are associated with the executor
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>
weak_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
};
namespace executor

View File

@@ -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
/**

View File

@@ -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_;

View File

@@ -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;

View File

@@ -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();
@@ -161,19 +161,22 @@ protected:
run_loop();
RCLCPP_PUBLIC
void init_wait_set();
void
init_wait_set();
RCLCPP_PUBLIC
void cleanup_wait_set();
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_;
@@ -185,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();
};

View File

@@ -22,6 +22,7 @@
#include "rcl/node.h"
#include "rcutils/logging.h"
#include "rcpputils/filesystem_helper.hpp"
/**
* \def RCLCPP_LOGGING_ENABLED
@@ -75,6 +76,18 @@ RCLCPP_PUBLIC
Logger
get_node_logger(const rcl_node_t * node);
/// Get the current logging directory.
/**
* For more details of how the logging directory is determined,
* see \ref rcl_logging_get_logging_directory.
*
* \returns the logging directory being used.
* \throws rclcpp::exceptions::RCLError if an unexpected error occurs.
*/
RCLCPP_PUBLIC
rcpputils::fs::path
get_logging_directory();
class Logger
{
public:

View File

@@ -15,6 +15,8 @@
#ifndef RCLCPP__PARAMETER_CLIENT_HPP_
#define RCLCPP__PARAMETER_CLIENT_HPP_
#include <functional>
#include <future>
#include <memory>
#include <string>
#include <utility>
@@ -120,6 +122,14 @@ public:
void(std::shared_future<std::vector<rclcpp::Parameter>>)
> callback = nullptr);
RCLCPP_PUBLIC
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>>
describe_parameters(
const std::vector<std::string> & names,
std::function<
void(std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>>)
> callback = nullptr);
RCLCPP_PUBLIC
std::shared_future<std::vector<rclcpp::ParameterType>>
get_parameter_types(
@@ -332,9 +342,17 @@ public:
qos_profile);
}
RCLCPP_PUBLIC
template<typename RepT = int64_t, typename RatioT = std::milli>
std::vector<rclcpp::Parameter>
get_parameters(const std::vector<std::string> & parameter_names);
get_parameters(
const std::vector<std::string> & parameter_names,
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
{
return get_parameters(
parameter_names,
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
);
}
RCLCPP_PUBLIC
bool
@@ -378,23 +396,67 @@ public:
);
}
RCLCPP_PUBLIC
template<typename RepT = int64_t, typename RatioT = std::milli>
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(
const std::vector<std::string> & parameter_names,
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
{
return describe_parameters(
parameter_names,
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
);
}
template<typename RepT = int64_t, typename RatioT = std::milli>
std::vector<rclcpp::ParameterType>
get_parameter_types(const std::vector<std::string> & parameter_names);
get_parameter_types(
const std::vector<std::string> & parameter_names,
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
{
return get_parameter_types(
parameter_names,
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
);
}
RCLCPP_PUBLIC
template<typename RepT = int64_t, typename RatioT = std::milli>
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(const std::vector<rclcpp::Parameter> & parameters);
set_parameters(
const std::vector<rclcpp::Parameter> & parameters,
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
{
return set_parameters(
parameters,
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
);
}
RCLCPP_PUBLIC
template<typename RepT = int64_t, typename RatioT = std::milli>
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(const std::vector<rclcpp::Parameter> & parameters);
set_parameters_atomically(
const std::vector<rclcpp::Parameter> & parameters,
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
{
return set_parameters_atomically(
parameters,
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
);
}
RCLCPP_PUBLIC
template<typename RepT = int64_t, typename RatioT = std::milli>
rcl_interfaces::msg::ListParametersResult
list_parameters(
const std::vector<std::string> & parameter_prefixes,
uint64_t depth);
uint64_t depth,
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
{
return list_parameters(
parameter_prefixes,
depth,
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
);
}
template<typename CallbackT>
typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
@@ -437,6 +499,44 @@ public:
return async_parameters_client_->wait_for_service(timeout);
}
protected:
RCLCPP_PUBLIC
std::vector<rclcpp::Parameter>
get_parameters(
const std::vector<std::string> & parameter_names,
std::chrono::nanoseconds timeout);
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(
const std::vector<std::string> & parameter_names,
std::chrono::nanoseconds timeout);
RCLCPP_PUBLIC
std::vector<rclcpp::ParameterType>
get_parameter_types(
const std::vector<std::string> & parameter_names,
std::chrono::nanoseconds timeout);
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(
const std::vector<rclcpp::Parameter> & parameters,
std::chrono::nanoseconds timeout);
RCLCPP_PUBLIC
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(
const std::vector<rclcpp::Parameter> & parameters,
std::chrono::nanoseconds timeout);
RCLCPP_PUBLIC
rcl_interfaces::msg::ListParametersResult
list_parameters(
const std::vector<std::string> & parameter_prefixes,
uint64_t depth,
std::chrono::nanoseconds timeout);
private:
rclcpp::Executor::SharedPtr executor_;
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface_;

View File

@@ -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.

View File

@@ -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
{
@@ -58,10 +90,32 @@ struct RCLCPP_PUBLIC KeepLast : public rclcpp::QoSInitialization
};
/// Encapsulation of Quality of Service settings.
/**
* Quality of Service settings control the behavior of publishers, subscriptions,
* and other entities, and includes things like how data is sent or resent,
* how data is buffered on the publishing and subscribing side, and other things.
* See:
* <a href="https://index.ros.org/doc/ros2/Concepts/About-Quality-of-Service-Settings">
* https://index.ros.org/doc/ros2/Concepts/About-Quality-of-Service-Settings
* </a>
*/
class RCLCPP_PUBLIC QoS
{
public:
/// Constructor which allows you to construct a QoS by giving the only required settings.
/// Create a QoS by specifying only the history policy and history depth.
/**
* When using the default initial profile, the defaults will include:
*
* - \link rclcpp::ReliabilityPolicy::Reliable ReliabilityPolicy::Reliable\endlink
* - \link rclcpp::DurabilityPolicy::Volatile DurabilityPolicy::Volatile\endlink
*
* See rmw_qos_profile_default for a full list of default settings.
* If some other rmw_qos_profile_t is passed to initial_profile, then the defaults will derive from
* that profile instead.
*
* \param[in] qos_initialization Specifies history policy and history depth.
* \param[in] initial_profile The rmw_qos_profile_t instance on which to base the default settings.
*/
explicit
QoS(
const QoSInitialization & qos_initialization,
@@ -69,7 +123,11 @@ public:
/// Conversion constructor to ease construction in the common case of just specifying depth.
/**
* Convenience constructor, equivalent to QoS(KeepLast(history_depth)).
* This is a convenience constructor that calls QoS(KeepLast(history_depth)).
*
* \param[in] history_depth How many messages can be queued when publishing
* with a Publisher, or how many messages can be queued before being replaced
* by a Subscription.
*/
// cppcheck-suppress noExplicitConstructor
QoS(size_t history_depth); // NOLINT(runtime/explicit): conversion constructor
@@ -82,6 +140,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 +160,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 +176,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 +211,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 +227,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_;
};

View File

@@ -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:

View 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_

View File

@@ -277,11 +277,18 @@ public:
return;
}
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
std::chrono::time_point<std::chrono::system_clock> now;
if (subscription_topic_statistics_) {
// get current time before executing callback to
// exclude callback duration from topic statistics result.
now = std::chrono::system_clock::now();
}
any_callback_.dispatch(typed_message, message_info);
if (subscription_topic_statistics_) {
const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(
std::chrono::system_clock::now());
const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
const auto time = rclcpp::Time(nanos.time_since_epoch().count());
subscription_topic_statistics_->handle_message(*typed_message, time);
}

View File

@@ -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.

View File

@@ -32,6 +32,20 @@ namespace rclcpp
{
class Clock;
/**
* Time source that will drive the attached clocks.
*
* If the attached node `use_sim_time` parameter is `true`, the attached clocks will
* be updated based on messages received.
*
* The subscription to the clock topic created by the time source can have it's qos reconfigured
* using parameter overrides, particularly the following ones are accepted:
*
* - qos_overrides./clock.depth
* - qos_overrides./clock.durability
* - qos_overrides./clock.history
* - qos_overrides./clock.reliability
*/
class TimeSource
{
public:

View File

@@ -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.
/**

View File

@@ -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.1.0</version>
<version>6.3.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>

View File

@@ -151,10 +151,9 @@ def get_rclcpp_suffix_from_features(features):
@[ end if]@
logger.get_name(), \
@[ if 'stream' not in feature_combination]@
rclcpp::get_c_string(RCLCPP_FIRST_ARG(__VA_ARGS__, "")), \
RCLCPP_ALL_BUT_FIRST_ARGS(__VA_ARGS__,"")); \
__VA_ARGS__); \
@[ else]@
"%s", rclcpp::get_c_string(ss.str())); \
"%s", ss.str().c_str()); \
@[ end if]@
} while (0)

View File

@@ -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()
{

View File

@@ -246,6 +246,27 @@ 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)
{

View File

@@ -25,6 +25,7 @@
#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"
@@ -42,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,
@@ -129,14 +137,14 @@ 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>
Executor::get_all_callback_groups()
{
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
std::lock_guard<std::mutex> guard{mutex_};
for (const auto & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
groups.push_back(group_node_ptr.first);
}
@@ -150,6 +158,7 @@ std::vector<rclcpp::CallbackGroup::WeakPtr>
Executor::get_manually_added_callback_groups()
{
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
std::lock_guard<std::mutex> guard{mutex_};
for (auto const & group_node_ptr : weak_groups_associated_with_executor_to_nodes_) {
groups.push_back(group_node_ptr.first);
}
@@ -160,6 +169,7 @@ std::vector<rclcpp::CallbackGroup::WeakPtr>
Executor::get_automatically_added_callback_groups_from_nodes()
{
std::vector<rclcpp::CallbackGroup::WeakPtr> groups;
std::lock_guard<std::mutex> guard{mutex_};
for (auto const & group_node_ptr : weak_groups_to_nodes_associated_with_executor_) {
groups.push_back(group_node_ptr.first);
}
@@ -226,7 +236,6 @@ Executor::add_callback_group_to_map(
}
}
// Add the node's notify condition to the guard condition handles
std::unique_lock<std::mutex> lock(memory_strategy_mutex_);
memory_strategy_->add_guard_condition(node_ptr->get_notify_guard_condition());
}
}
@@ -237,6 +246,7 @@ Executor::add_callback_group(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
bool notify)
{
std::lock_guard<std::mutex> guard{mutex_};
this->add_callback_group_to_map(
group_ptr,
node_ptr,
@@ -252,6 +262,7 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
if (has_executor.exchange(true)) {
throw std::runtime_error("Node has already been added to an executor.");
}
std::lock_guard<std::mutex> guard{mutex_};
for (auto & weak_group : node_ptr->get_callback_groups()) {
auto group_ptr = weak_group.lock();
if (group_ptr != nullptr && !group_ptr->get_associated_with_executor_atomic().load() &&
@@ -300,7 +311,6 @@ Executor::remove_callback_group_from_map(
throw_from_rcl_error(ret, "Failed to trigger guard condition on callback group remove");
}
}
std::unique_lock<std::mutex> lock(memory_strategy_mutex_);
memory_strategy_->remove_guard_condition(node_ptr->get_notify_guard_condition());
}
}
@@ -310,6 +320,7 @@ Executor::remove_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
bool notify)
{
std::lock_guard<std::mutex> guard{mutex_};
this->remove_callback_group_from_map(
group_ptr,
weak_groups_associated_with_executor_to_nodes_,
@@ -329,6 +340,7 @@ Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node
throw std::runtime_error("Node needs to be associated with an executor.");
}
std::lock_guard<std::mutex> guard{mutex_};
bool found_node = false;
auto node_it = weak_nodes_.begin();
while (node_it != weak_nodes_.end()) {
@@ -482,6 +494,7 @@ Executor::set_memory_strategy(rclcpp::memory_strategy::MemoryStrategy::SharedPtr
if (memory_strategy == nullptr) {
throw std::runtime_error("Received NULL memory strategy in executor.");
}
std::lock_guard<std::mutex> guard{mutex_};
memory_strategy_ = memory_strategy;
}
@@ -504,7 +517,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);
@@ -655,7 +668,7 @@ void
Executor::wait_for_work(std::chrono::nanoseconds timeout)
{
{
std::unique_lock<std::mutex> lock(memory_strategy_mutex_);
std::lock_guard<std::mutex> guard(mutex_);
// Check weak_nodes_ to find any callback group that is not owned
// by an executor and add it to the list of callbackgroups for
@@ -676,8 +689,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(
@@ -731,12 +747,14 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
// check the null handles in the wait set and remove them from the handles in memory strategy
// for callback-based entities
std::lock_guard<std::mutex> guard(mutex_);
memory_strategy_->remove_null_handles(&wait_set_);
}
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
Executor::get_node_by_group(
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap weak_groups_to_nodes,
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
weak_groups_to_nodes,
rclcpp::CallbackGroup::SharedPtr group)
{
if (!group) {
@@ -754,6 +772,7 @@ Executor::get_node_by_group(
rclcpp::CallbackGroup::SharedPtr
Executor::get_group_by_timer(rclcpp::TimerBase::SharedPtr timer)
{
std::lock_guard<std::mutex> guard{mutex_};
for (const auto & pair : weak_groups_associated_with_executor_to_nodes_) {
auto group = pair.first.lock();
if (!group) {
@@ -794,9 +813,11 @@ Executor::get_next_ready_executable(AnyExecutable & any_executable)
bool
Executor::get_next_ready_executable_from_map(
AnyExecutable & any_executable,
rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap weak_groups_to_nodes)
const rclcpp::memory_strategy::MemoryStrategy::WeakCallbackGroupsToNodesMap &
weak_groups_to_nodes)
{
bool success = false;
std::lock_guard<std::mutex> guard{mutex_};
// Check the timers to see if there are any that are ready
memory_strategy_->get_next_timer(any_executable, weak_groups_to_nodes);
if (any_executable.timer) {
@@ -827,6 +848,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;
}
}
@@ -882,7 +904,8 @@ Executor::get_next_executable(AnyExecutable & any_executable, std::chrono::nanos
bool
Executor::has_node(
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
rclcpp::memory_strategy::MemoryStrategy::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(),

View File

@@ -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)
@@ -315,8 +323,7 @@ StaticExecutorEntitiesCollector::add_callback_group(
throw std::runtime_error("Callback group was already added to executor.");
}
if (is_new_node) {
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr node_weak_ptr(node_ptr);
weak_nodes_to_guard_conditions_[node_weak_ptr] = node_ptr->get_notify_guard_condition();
weak_nodes_to_guard_conditions_[node_ptr] = node_ptr->get_notify_guard_condition();
return true;
}
return false;

View File

@@ -175,8 +175,10 @@ 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();
auto waitable = entities_collector_->get_waitable(i);
if (waitable->is_ready(&wait_set_)) {
auto data = waitable->take_data();
waitable->execute(data);
}
}
}

View File

@@ -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()
@@ -64,10 +62,6 @@ GraphListener::~GraphListener()
void GraphListener::init_wait_set()
{
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
@@ -76,7 +70,7 @@ void GraphListener::init_wait_set()
0, // number_of_clients
0, // number_of_services
0, // number_of_events
parent_context->get_rcl_context().get(),
rcl_parent_context_.get(),
rcl_get_default_allocator());
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to initialize wait set");
@@ -90,14 +84,13 @@ GraphListener::start_if_not_started()
if (is_shutdown_.load()) {
throw GraphListenerShutdownError();
}
if (!is_started_) {
// Initialize the wait set before starting.
init_wait_set();
// Register an on_shutdown hook to shutdown the graph listener.
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) {
@@ -105,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;
@@ -149,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
@@ -176,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) {
@@ -211,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];
@@ -224,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();
}
@@ -365,7 +343,7 @@ GraphListener::cleanup_wait_set()
}
void
GraphListener::__shutdown(bool should_throw)
GraphListener::__shutdown()
{
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (!is_shutdown_.exchange(true)) {
@@ -377,28 +355,6 @@ 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_) {
cleanup_wait_set();
}
@@ -408,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"),

View File

@@ -14,6 +14,8 @@
#include <string>
#include "rcl_logging_interface/rcl_logging_interface.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/logger.hpp"
#include "rclcpp/logging.hpp"
@@ -38,12 +40,28 @@ get_node_logger(const rcl_node_t * node)
const char * logger_name = rcl_node_get_logger_name(node);
if (nullptr == logger_name) {
auto logger = rclcpp::get_logger("rclcpp");
RCLCPP_ERROR(logger, "failed to get logger name from node at address %p", node);
RCLCPP_ERROR(
logger, "failed to get logger name from node at address %p",
static_cast<void *>(const_cast<rcl_node_t *>(node)));
return logger;
}
return rclcpp::get_logger(logger_name);
}
rcpputils::fs::path
get_logging_directory()
{
char * log_dir = NULL;
auto allocator = rcutils_get_default_allocator();
rcl_logging_ret_t ret = rcl_logging_get_logging_directory(allocator, &log_dir);
if (RCL_LOGGING_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
std::string path{log_dir};
allocator.deallocate(log_dir, allocator.state);
return path;
}
void
Logger::set_level(Level level)
{

View File

@@ -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.

View File

@@ -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",

View File

@@ -16,6 +16,8 @@
#include <string>
#include "tracetools/tracetools.h"
using rclcpp::node_interfaces::NodeTimers;
NodeTimers::NodeTimers(rclcpp::node_interfaces::NodeBaseInterface * node_base)
@@ -44,4 +46,8 @@ NodeTimers::add_timer(
std::string("Failed to notify wait set on timer creation: ") +
rmw_get_error_string().str);
}
TRACEPOINT(
rclcpp_timer_link_node,
static_cast<const void *>(timer->get_timer_handle().get()),
static_cast<const void *>(node_base_->get_rcl_node_handle()));
}

View File

@@ -144,6 +144,35 @@ AsyncParametersClient::get_parameters(
return future_result;
}
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>>
AsyncParametersClient::describe_parameters(
const std::vector<std::string> & names,
std::function<
void(std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>>)
> callback)
{
auto promise_result =
std::make_shared<std::promise<std::vector<rcl_interfaces::msg::ParameterDescriptor>>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::DescribeParameters::Request>();
request->names = names;
describe_parameters_client_->async_send_request(
request,
[promise_result, future_result, callback](
rclcpp::Client<rcl_interfaces::srv::DescribeParameters>::SharedFuture cb_f)
{
promise_result->set_value(cb_f.get()->descriptors);
if (callback != nullptr) {
callback(future_result);
}
}
);
return future_result;
}
std::shared_future<std::vector<rclcpp::ParameterType>>
AsyncParametersClient::get_parameter_types(
const std::vector<std::string> & names,
@@ -313,14 +342,16 @@ AsyncParametersClient::wait_for_service_nanoseconds(std::chrono::nanoseconds tim
}
std::vector<rclcpp::Parameter>
SyncParametersClient::get_parameters(const std::vector<std::string> & parameter_names)
SyncParametersClient::get_parameters(
const std::vector<std::string> & parameter_names,
std::chrono::nanoseconds timeout)
{
auto f = async_parameters_client_->get_parameters(parameter_names);
using rclcpp::executors::spin_node_until_future_complete;
if (
spin_node_until_future_complete(
*executor_, node_base_interface_,
f) == rclcpp::FutureReturnCode::SUCCESS)
*executor_, node_base_interface_, f,
timeout) == rclcpp::FutureReturnCode::SUCCESS)
{
return f.get();
}
@@ -337,16 +368,34 @@ SyncParametersClient::has_parameter(const std::string & parameter_name)
return vars.names.size() > 0;
}
std::vector<rcl_interfaces::msg::ParameterDescriptor>
SyncParametersClient::describe_parameters(
const std::vector<std::string> & parameter_names,
std::chrono::nanoseconds timeout)
{
auto f = async_parameters_client_->describe_parameters(parameter_names);
using rclcpp::executors::spin_node_until_future_complete;
rclcpp::FutureReturnCode future =
spin_node_until_future_complete(*executor_, node_base_interface_, f, timeout);
if (future == rclcpp::FutureReturnCode::SUCCESS) {
return f.get();
}
return std::vector<rcl_interfaces::msg::ParameterDescriptor>();
}
std::vector<rclcpp::ParameterType>
SyncParametersClient::get_parameter_types(const std::vector<std::string> & parameter_names)
SyncParametersClient::get_parameter_types(
const std::vector<std::string> & parameter_names,
std::chrono::nanoseconds timeout)
{
auto f = async_parameters_client_->get_parameter_types(parameter_names);
using rclcpp::executors::spin_node_until_future_complete;
if (
spin_node_until_future_complete(
*executor_, node_base_interface_,
f) == rclcpp::FutureReturnCode::SUCCESS)
*executor_, node_base_interface_, f,
timeout) == rclcpp::FutureReturnCode::SUCCESS)
{
return f.get();
}
@@ -355,15 +404,16 @@ SyncParametersClient::get_parameter_types(const std::vector<std::string> & param
std::vector<rcl_interfaces::msg::SetParametersResult>
SyncParametersClient::set_parameters(
const std::vector<rclcpp::Parameter> & parameters)
const std::vector<rclcpp::Parameter> & parameters,
std::chrono::nanoseconds timeout)
{
auto f = async_parameters_client_->set_parameters(parameters);
using rclcpp::executors::spin_node_until_future_complete;
if (
spin_node_until_future_complete(
*executor_, node_base_interface_,
f) == rclcpp::FutureReturnCode::SUCCESS)
*executor_, node_base_interface_, f,
timeout) == rclcpp::FutureReturnCode::SUCCESS)
{
return f.get();
}
@@ -372,15 +422,16 @@ SyncParametersClient::set_parameters(
rcl_interfaces::msg::SetParametersResult
SyncParametersClient::set_parameters_atomically(
const std::vector<rclcpp::Parameter> & parameters)
const std::vector<rclcpp::Parameter> & parameters,
std::chrono::nanoseconds timeout)
{
auto f = async_parameters_client_->set_parameters_atomically(parameters);
using rclcpp::executors::spin_node_until_future_complete;
if (
spin_node_until_future_complete(
*executor_, node_base_interface_,
f) == rclcpp::FutureReturnCode::SUCCESS)
*executor_, node_base_interface_, f,
timeout) == rclcpp::FutureReturnCode::SUCCESS)
{
return f.get();
}
@@ -391,15 +442,16 @@ SyncParametersClient::set_parameters_atomically(
rcl_interfaces::msg::ListParametersResult
SyncParametersClient::list_parameters(
const std::vector<std::string> & parameter_prefixes,
uint64_t depth)
uint64_t depth,
std::chrono::nanoseconds timeout)
{
auto f = async_parameters_client_->list_parameters(parameter_prefixes, depth);
using rclcpp::executors::spin_node_until_future_complete;
if (
spin_node_until_future_complete(
*executor_, node_base_interface_,
f) == rclcpp::FutureReturnCode::SUCCESS)
*executor_, node_base_interface_, f,
timeout) == rclcpp::FutureReturnCode::SUCCESS)
{
return f.get();
}

View File

@@ -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.

View 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

View File

@@ -243,7 +243,7 @@ SignalHandler::deferred_signal_handler()
get_logger(),
"deferred_signal_handler(): "
"shutting down rclcpp::Context @ %p, because it had shutdown_on_sigint == true",
context_ptr.get());
static_cast<void *>(context_ptr.get()));
context_ptr->shutdown("signal handler");
}
}

View File

@@ -233,11 +233,22 @@ void TimeSource::create_clock_sub()
return;
}
rclcpp::SubscriptionOptions options;
options.qos_overriding_options = rclcpp::QosOverridingOptions(
{
rclcpp::QosPolicyKind::Depth,
rclcpp::QosPolicyKind::Durability,
rclcpp::QosPolicyKind::History,
rclcpp::QosPolicyKind::Reliability,
});
clock_subscription_ = rclcpp::create_subscription<rosgraph_msgs::msg::Clock>(
node_parameters_,
node_topics_,
"/clock",
rclcpp::QoS(KeepLast(1)).best_effort(),
std::bind(&TimeSource::clock_cb, this, std::placeholders::_1)
std::bind(&TimeSource::clock_cb, this, std::placeholders::_1),
options
);
}

View File

@@ -1,3 +1,4 @@
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
@@ -31,6 +32,11 @@ 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})

View File

@@ -384,6 +384,7 @@ BENCHMARK_F(
reset_heap_counters();
for (auto _ : st) {
entities_collector_->execute();
std::shared_ptr<void> data = entities_collector_->take_data();
entities_collector_->execute(data);
}
}

View 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;
}
}
}

View File

@@ -369,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

View File

@@ -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>>(

View File

@@ -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));

View File

@@ -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);
}

View File

@@ -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()));
}

View File

@@ -14,9 +14,11 @@
#include <gtest/gtest.h>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "rcl/graph.h"
#include "rcl/node_options.h"
@@ -66,6 +68,43 @@ protected:
const rclcpp::node_interfaces::NodeGraph * node_graph() const {return node_graph_;}
size_t get_num_graph_things(std::function<size_t()> predicate)
{
constexpr std::chrono::milliseconds timeout(100);
size_t tries = 0;
size_t num_things = 0;
while (tries++ < 5) {
num_things = predicate();
if (num_things >= 1) {
break;
}
auto event = node()->get_graph_event();
EXPECT_NO_THROW(node()->wait_for_graph_change(event, timeout));
}
return num_things;
}
size_t get_num_topics()
{
return get_num_graph_things(
[this]() -> size_t {
auto topic_names_and_types = node_graph()->get_topic_names_and_types();
return topic_names_and_types.size();
});
}
size_t get_num_services()
{
return get_num_graph_things(
[this]() -> size_t {
auto service_names_and_types = node_graph()->get_service_names_and_types();
return service_names_and_types.size();
});
}
private:
std::shared_ptr<rclcpp::Node> node_;
rclcpp::node_interfaces::NodeGraph * node_graph_;
@@ -73,11 +112,9 @@ private:
TEST_F(TestNodeGraph, construct_from_node)
{
auto topic_names_and_types = node_graph()->get_topic_names_and_types(false);
EXPECT_LT(0u, topic_names_and_types.size());
EXPECT_LT(0u, get_num_topics());
auto service_names_and_types = node_graph()->get_service_names_and_types();
EXPECT_LT(0u, service_names_and_types.size());
EXPECT_LT(0u, get_num_services());
auto names = node_graph()->get_node_names();
EXPECT_EQ(1u, names.size());
@@ -91,13 +128,12 @@ TEST_F(TestNodeGraph, construct_from_node)
// get_graph_event is non-const
EXPECT_NE(nullptr, node()->get_node_graph_interface()->get_graph_event());
EXPECT_EQ(1u, node_graph()->count_graph_users());
EXPECT_LE(1u, node_graph()->count_graph_users());
}
TEST_F(TestNodeGraph, get_topic_names_and_types)
{
auto topic_names_and_types = node_graph()->get_topic_names_and_types();
EXPECT_LT(0u, topic_names_and_types.size());
ASSERT_LT(0u, get_num_topics());
}
TEST_F(TestNodeGraph, get_topic_names_and_types_rcl_error)
@@ -126,8 +162,7 @@ TEST_F(TestNodeGraph, get_topic_names_and_types_rcl_names_and_types_fini_error)
TEST_F(TestNodeGraph, get_service_names_and_types)
{
auto service_names_and_types = node_graph()->get_service_names_and_types();
EXPECT_LT(0u, service_names_and_types.size());
ASSERT_LT(0u, get_num_services());
}
TEST_F(TestNodeGraph, get_service_names_and_types_rcl_error)
@@ -309,8 +344,13 @@ TEST_F(TestNodeGraph, get_info_by_topic)
EXPECT_EQ(0u, node_graph()->get_publishers_info_by_topic("topic", true).size());
auto publishers = node_graph()->get_publishers_info_by_topic("topic", false);
ASSERT_EQ(1u, publishers.size());
std::vector<rclcpp::TopicEndpointInfo> publishers;
size_t num_publishers = get_num_graph_things(
[this, &publishers]() {
publishers = node_graph()->get_publishers_info_by_topic("topic", false);
return publishers.size();
});
ASSERT_EQ(1u, num_publishers);
auto publisher_endpoint_info = publishers[0];
const auto const_publisher_endpoint_info = publisher_endpoint_info;

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -148,6 +148,34 @@ TEST_F(TestDuration, from_seconds) {
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(0ns), rclcpp::Duration(0.0s));
EXPECT_EQ(rclcpp::Duration(0ns), rclcpp::Duration(0s));

View File

@@ -21,6 +21,7 @@
#include "rclcpp/executor.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
#include "../mocking_utils/patch.hpp"
@@ -54,6 +55,7 @@ public:
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr local_get_node_by_group(
rclcpp::CallbackGroup::SharedPtr group)
{
std::lock_guard<std::mutex> guard_{mutex_}; // only to make the TSA happy
return get_node_by_group(weak_groups_to_nodes_, group);
}
@@ -87,19 +89,47 @@ MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcutils_allocator_t, !=)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcutils_allocator_t, <)
MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcutils_allocator_t, >)
TEST_F(TestExecutor, add_remove_node_thread_safe) {
using namespace std::chrono_literals;
// Create an Executor
rclcpp::executors::SingleThreadedExecutor executor;
auto future = std::async(std::launch::async, [&executor] {executor.spin();});
// Add and remove nodes repeatedly
// Test that this does not cause a segfault
size_t num_nodes = 100;
for (size_t i = 0; i < num_nodes; ++i) {
std::ostringstream name;
name << "node_" << i;
auto node = std::make_shared<rclcpp::Node>(name.str());
executor.add_node(node);
// Sleeping here helps exaggerate the issue
std::this_thread::sleep_for(std::chrono::milliseconds(1));
executor.remove_node(node);
}
std::future_status future_status = std::future_status::timeout;
do {
executor.cancel();
future_status = future.wait_for(1s);
} while (future_status == std::future_status::timeout);
EXPECT_EQ(future_status, std::future_status::ready);
future.get();
}
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(
new DummyExecutor(),
std::runtime_error(
"Failed to create interrupt guard condition in Executor constructor: error not set"));
EXPECT_THROW(
static_cast<void>(std::make_unique<DummyExecutor>()),
rclcpp::exceptions::RCLError);
}
TEST_F(TestExecutor, constructor_bad_wait_set_init) {
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_init, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
new DummyExecutor(),
static_cast<void>(std::make_unique<DummyExecutor>()),
std::runtime_error("Failed to create wait set in Executor constructor: error not set"));
}

View File

@@ -143,7 +143,9 @@ 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) {
@@ -170,26 +172,6 @@ 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_init_wait_set();
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 =

View File

@@ -17,6 +17,8 @@
#include <memory>
#include <string>
#include "rcutils/env.h"
#include "rclcpp/logger.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/node.hpp"
@@ -80,78 +82,99 @@ TEST(TestLogger, set_level) {
rcutils_logging_set_output_handler(rcutils_logging_console_output_handler);
// default
RCLCPP_DEBUG(logger, std::string("message %s"), "debug");
RCLCPP_DEBUG(logger, "message %s", "debug");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_INFO(logger, std::string("message %s"), "info");
RCLCPP_INFO(logger, "message %s", "info");
EXPECT_TRUE(g_last_log_event.console_output_handler_called);
EXPECT_EQ("message info", g_last_log_event.message);
// unset
g_last_log_event.console_output_handler_called = false;
logger.set_level(rclcpp::Logger::Level::Unset);
RCLCPP_DEBUG(logger, std::string("message %s"), "debug");
RCLCPP_DEBUG(logger, "message");
RCLCPP_DEBUG(logger, "message %s", "debug");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_INFO(logger, std::string("message %s"), "info");
RCLCPP_INFO(logger, "message");
RCLCPP_INFO(logger, "message %s", "info");
EXPECT_TRUE(g_last_log_event.console_output_handler_called);
EXPECT_EQ("message info", g_last_log_event.message);
// debug
g_last_log_event.console_output_handler_called = false;
logger.set_level(rclcpp::Logger::Level::Debug);
RCLCPP_DEBUG(logger, std::string("message %s"), "debug");
RCLCPP_DEBUG(logger, "message %s", "debug");
EXPECT_TRUE(g_last_log_event.console_output_handler_called);
EXPECT_EQ("message debug", g_last_log_event.message);
RCLCPP_INFO(logger, std::string("message %s"), "info");
RCLCPP_INFO(logger, "message %s", "info");
EXPECT_EQ("message info", g_last_log_event.message);
// info
g_last_log_event.console_output_handler_called = false;
logger.set_level(rclcpp::Logger::Level::Info);
RCLCPP_DEBUG(logger, std::string("message %s"), "debug");
RCLCPP_DEBUG(logger, "message %s", "debug");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_INFO(logger, std::string("message %s"), "info");
RCLCPP_INFO(logger, "message %s", "info");
EXPECT_TRUE(g_last_log_event.console_output_handler_called);
EXPECT_EQ("message info", g_last_log_event.message);
// warn
g_last_log_event.console_output_handler_called = false;
logger.set_level(rclcpp::Logger::Level::Warn);
RCLCPP_DEBUG(logger, std::string("message %s"), "debug");
RCLCPP_DEBUG(logger, "message %s", "debug");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_INFO(logger, std::string("message %s"), "info");
RCLCPP_INFO(logger, "message %s", "info");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_WARN(logger, std::string("message %s"), "warn");
RCLCPP_WARN(logger, "message %s", "warn");
EXPECT_TRUE(g_last_log_event.console_output_handler_called);
EXPECT_EQ("message warn", g_last_log_event.message);
// error
g_last_log_event.console_output_handler_called = false;
logger.set_level(rclcpp::Logger::Level::Error);
RCLCPP_DEBUG(logger, std::string("message %s"), "debug");
RCLCPP_DEBUG(logger, "message %s", "debug");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_INFO(logger, std::string("message %s"), "info");
RCLCPP_INFO(logger, "message %s", "info");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_WARN(logger, std::string("message %s"), "warn");
RCLCPP_WARN(logger, "message %s", "warn");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_ERROR(logger, std::string("message %s"), "error");
RCLCPP_ERROR(logger, "message %s", "error");
EXPECT_TRUE(g_last_log_event.console_output_handler_called);
EXPECT_EQ("message error", g_last_log_event.message);
// fatal
g_last_log_event.console_output_handler_called = false;
logger.set_level(rclcpp::Logger::Level::Fatal);
RCLCPP_DEBUG(logger, std::string("message %s"), "debug");
RCLCPP_DEBUG(logger, "message %s", "debug");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_INFO(logger, std::string("message %s"), "info");
RCLCPP_INFO(logger, "message %s", "info");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_WARN(logger, std::string("message %s"), "warn");
RCLCPP_WARN(logger, "message %s", "warn");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_ERROR(logger, std::string("message %s"), "error");
RCLCPP_ERROR(logger, "message %s", "error");
EXPECT_FALSE(g_last_log_event.console_output_handler_called);
RCLCPP_FATAL(logger, std::string("message %s"), "fatal");
RCLCPP_FATAL(logger, "message %s", "fatal");
EXPECT_TRUE(g_last_log_event.console_output_handler_called);
EXPECT_EQ("message fatal", g_last_log_event.message);
rcutils_logging_set_output_handler(previous_output_handler);
EXPECT_EQ(RCUTILS_RET_OK, rcutils_logging_shutdown());
}
TEST(TestLogger, get_logging_directory) {
ASSERT_EQ(true, rcutils_set_env("HOME", "/fake_home_dir"));
ASSERT_EQ(true, rcutils_set_env("USERPROFILE", nullptr));
ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", nullptr));
ASSERT_EQ(true, rcutils_set_env("ROS_HOME", nullptr));
auto path = rclcpp::get_logging_directory();
auto expected_path = rcpputils::fs::path{"/fake_home_dir"} / ".ros" / "log";
// TODO(ivanpauno): Add operator== to rcpputils::fs::path
auto it = path.cbegin();
auto eit = expected_path.cbegin();
for (; it != path.cend() && eit != expected_path.cend(); ++it, ++eit) {
EXPECT_EQ(*eit, *it);
}
EXPECT_EQ(it, path.cend());
EXPECT_EQ(eit, expected_path.cend());
}

View File

@@ -110,26 +110,6 @@ TEST_F(TestLoggingMacros, test_logging_named) {
EXPECT_EQ("message 3", g_last_log_event.message);
}
TEST_F(TestLoggingMacros, test_logging_string) {
for (std::string i : {"one", "two", "three"}) {
RCLCPP_DEBUG(g_logger, "message " + i);
}
EXPECT_EQ(3u, g_log_calls);
EXPECT_EQ("message three", g_last_log_event.message);
RCLCPP_DEBUG(g_logger, "message " "four");
EXPECT_EQ("message four", g_last_log_event.message);
RCLCPP_DEBUG(g_logger, std::string("message " "five"));
EXPECT_EQ("message five", g_last_log_event.message);
RCLCPP_DEBUG(g_logger, std::string("message %s"), "six");
EXPECT_EQ("message six", g_last_log_event.message);
RCLCPP_DEBUG(g_logger, "message seven");
EXPECT_EQ("message seven", g_last_log_event.message);
}
TEST_F(TestLoggingMacros, test_logging_stream) {
for (std::string i : {"one", "two", "three"}) {
RCLCPP_DEBUG_STREAM(g_logger, "message " << i);

View File

@@ -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

View File

@@ -43,6 +43,11 @@ protected:
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp() override
{
test_resources_path /= "test_node";

View File

@@ -28,6 +28,8 @@
#include "rcl_interfaces/msg/parameter_event.hpp"
using namespace std::chrono_literals;
class TestParameterClient : public ::testing::Test
{
public:
@@ -45,14 +47,20 @@ protected:
void SetUp()
{
node = std::make_shared<rclcpp::Node>("test_parameter_client", "/ns");
node_with_option =
std::make_shared<rclcpp::Node>(
"test_parameter_client_allow_undeclare", "/ns",
rclcpp::NodeOptions().allow_undeclared_parameters(true));
}
void TearDown()
{
node.reset();
node_with_option.reset();
}
rclcpp::Node::SharedPtr node;
rclcpp::Node::SharedPtr node_with_option;
};
/*
@@ -287,7 +295,337 @@ TEST_F(TestParameterClient, sync_parameter_get_parameter_types) {
auto synchronous_client = std::make_shared<rclcpp::SyncParametersClient>(node);
std::vector<std::string> names{"foo"};
std::vector<rclcpp::ParameterType> parameter_types =
synchronous_client->get_parameter_types(names);
synchronous_client->get_parameter_types(names, 10s);
ASSERT_EQ(1u, parameter_types.size());
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_types[0]);
}
/*
Coverage for async describe_parameters
*/
TEST_F(TestParameterClient, async_parameter_describe_parameters) {
node->declare_parameter("foo", 4);
node->declare_parameter("bar", "this is bar");
auto asynchronous_client = std::make_shared<rclcpp::AsyncParametersClient>(node);
{
bool callback_called = false;
auto callback = [&callback_called](
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> result)
{
// We expect the result to be empty since we tried to get a parameter that didn't exist.
if (result.valid() && result.get().size() == 0) {
callback_called = true;
}
};
std::vector<std::string> names{"none"};
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> future =
asynchronous_client->describe_parameters(names, callback);
auto return_code = rclcpp::spin_until_future_complete(
node, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
ASSERT_TRUE(callback_called);
}
{
bool callback_called = false;
auto callback = [&callback_called](
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> result)
{
if (result.valid() && result.get().size() == 1) {
callback_called = true;
}
};
std::vector<std::string> names{"foo"};
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> future =
asynchronous_client->describe_parameters(names, callback);
auto return_code = rclcpp::spin_until_future_complete(
node, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs = future.get();
ASSERT_EQ(1u, parameter_descs.size());
ASSERT_EQ("foo", parameter_descs[0].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_descs[0].type);
ASSERT_EQ("", parameter_descs[0].description);
ASSERT_EQ("", parameter_descs[0].additional_constraints);
ASSERT_FALSE(parameter_descs[0].read_only);
ASSERT_TRUE(callback_called);
}
{
bool callback_called = false;
auto callback = [&callback_called](
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> result)
{
// We expect the result to be empty since we tried to get a parameter that didn't exist.
if (result.valid() && result.get().size() == 0) {
callback_called = true;
}
};
std::vector<std::string> names{"foo", "baz"};
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> future =
asynchronous_client->describe_parameters(names, callback);
auto return_code = rclcpp::spin_until_future_complete(
node, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
ASSERT_TRUE(callback_called);
}
{
bool callback_called = false;
auto callback = [&callback_called](
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> result)
{
// We expect the result to be empty since we tried to get a parameter that didn't exist.
if (result.valid() && result.get().size() == 0) {
callback_called = true;
}
};
std::vector<std::string> names{"baz", "foo"};
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> future =
asynchronous_client->describe_parameters(names, callback);
auto return_code = rclcpp::spin_until_future_complete(
node, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
ASSERT_TRUE(callback_called);
}
{
bool callback_called = false;
auto callback = [&callback_called](
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> result)
{
if (result.valid() && result.get().size() == 2) {
callback_called = true;
}
};
std::vector<std::string> names{"foo", "bar"};
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> future =
asynchronous_client->describe_parameters(names, callback);
auto return_code = rclcpp::spin_until_future_complete(
node, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs = future.get();
ASSERT_EQ(2u, parameter_descs.size());
ASSERT_EQ("foo", parameter_descs[0].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_descs[0].type);
ASSERT_EQ("", parameter_descs[0].description);
ASSERT_EQ("", parameter_descs[0].additional_constraints);
ASSERT_FALSE(parameter_descs[0].read_only);
ASSERT_EQ("bar", parameter_descs[1].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_STRING, parameter_descs[1].type);
ASSERT_EQ("", parameter_descs[1].description);
ASSERT_EQ("", parameter_descs[1].additional_constraints);
ASSERT_FALSE(parameter_descs[1].read_only);
ASSERT_TRUE(callback_called);
}
}
/*
Coverage for sync describe_parameters
*/
TEST_F(TestParameterClient, sync_parameter_describe_parameters) {
node->declare_parameter("foo", 4);
node->declare_parameter("bar", "this is bar");
auto synchronous_client = std::make_shared<rclcpp::SyncParametersClient>(node);
{
std::vector<std::string> names{"none"};
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs =
synchronous_client->describe_parameters(names, 10s);
ASSERT_EQ(0u, parameter_descs.size());
}
{
std::vector<std::string> names{"foo"};
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs =
synchronous_client->describe_parameters(names, 10s);
ASSERT_EQ(1u, parameter_descs.size());
ASSERT_EQ("foo", parameter_descs[0].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_descs[0].type);
ASSERT_EQ("", parameter_descs[0].description);
ASSERT_EQ("", parameter_descs[0].additional_constraints);
ASSERT_FALSE(parameter_descs[0].read_only);
}
{
std::vector<std::string> names{"foo", "baz"};
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs =
synchronous_client->describe_parameters(names, 10s);
ASSERT_EQ(0u, parameter_descs.size());
}
{
std::vector<std::string> names{"baz", "foo"};
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs =
synchronous_client->describe_parameters(names, 10s);
ASSERT_EQ(0u, parameter_descs.size());
}
{
std::vector<std::string> names{"foo", "bar"};
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs =
synchronous_client->describe_parameters(names, 10s);
ASSERT_EQ(2u, parameter_descs.size());
ASSERT_EQ("foo", parameter_descs[0].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_descs[0].type);
ASSERT_EQ("", parameter_descs[0].description);
ASSERT_EQ("", parameter_descs[0].additional_constraints);
ASSERT_FALSE(parameter_descs[0].read_only);
ASSERT_EQ("bar", parameter_descs[1].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_STRING, parameter_descs[1].type);
ASSERT_EQ("", parameter_descs[1].description);
ASSERT_EQ("", parameter_descs[1].additional_constraints);
ASSERT_FALSE(parameter_descs[1].read_only);
}
}
/*
Coverage for async describe_parameters with allow_undeclared_ enabled
*/
TEST_F(TestParameterClient, async_parameter_describe_parameters_allow_undeclared) {
node_with_option->declare_parameter("foo", 4);
node_with_option->declare_parameter("bar", "this is bar");
auto asynchronous_client =
std::make_shared<rclcpp::AsyncParametersClient>(node_with_option);
{
bool callback_called = false;
auto callback = [&callback_called](
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> result)
{
// We expect the result to be defaut since we tried to get a parameter that didn't exist.
if (result.valid() && result.get().size() == 1) {
callback_called = true;
}
};
std::vector<std::string> names{"none"};
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> future =
asynchronous_client->describe_parameters(names, callback);
auto return_code = rclcpp::spin_until_future_complete(
node_with_option, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs = future.get();
ASSERT_EQ(1u, parameter_descs.size());
ASSERT_EQ("none", parameter_descs[0].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_NOT_SET, parameter_descs[0].type);
ASSERT_EQ("", parameter_descs[0].description);
ASSERT_EQ("", parameter_descs[0].additional_constraints);
ASSERT_FALSE(parameter_descs[0].read_only);
ASSERT_TRUE(callback_called);
}
{
bool callback_called = false;
auto callback = [&callback_called](
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> result)
{
if (result.valid() && result.get().size() == 2) {
callback_called = true;
}
};
std::vector<std::string> names{"foo", "baz"};
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> future =
asynchronous_client->describe_parameters(names, callback);
auto return_code = rclcpp::spin_until_future_complete(
node_with_option, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs = future.get();
ASSERT_EQ(2u, parameter_descs.size());
ASSERT_EQ("foo", parameter_descs[0].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_descs[0].type);
ASSERT_EQ("", parameter_descs[0].description);
ASSERT_EQ("", parameter_descs[0].additional_constraints);
ASSERT_FALSE(parameter_descs[0].read_only);
ASSERT_EQ("baz", parameter_descs[1].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_NOT_SET, parameter_descs[1].type);
ASSERT_EQ("", parameter_descs[1].description);
ASSERT_EQ("", parameter_descs[1].additional_constraints);
ASSERT_FALSE(parameter_descs[1].read_only);
ASSERT_TRUE(callback_called);
}
{
bool callback_called = false;
auto callback = [&callback_called](
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> result)
{
if (result.valid() && result.get().size() == 2) {
callback_called = true;
}
};
std::vector<std::string> names{"baz", "foo"};
std::shared_future<std::vector<rcl_interfaces::msg::ParameterDescriptor>> future =
asynchronous_client->describe_parameters(names, callback);
auto return_code = rclcpp::spin_until_future_complete(
node_with_option, future, std::chrono::milliseconds(100));
ASSERT_EQ(rclcpp::FutureReturnCode::SUCCESS, return_code);
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs = future.get();
ASSERT_EQ(2u, parameter_descs.size());
ASSERT_EQ("baz", parameter_descs[0].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_NOT_SET, parameter_descs[0].type);
ASSERT_EQ("", parameter_descs[0].description);
ASSERT_EQ("", parameter_descs[0].additional_constraints);
ASSERT_FALSE(parameter_descs[0].read_only);
ASSERT_EQ("foo", parameter_descs[1].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_descs[1].type);
ASSERT_EQ("", parameter_descs[1].description);
ASSERT_EQ("", parameter_descs[1].additional_constraints);
ASSERT_FALSE(parameter_descs[1].read_only);
ASSERT_TRUE(callback_called);
}
}
/*
Coverage for sync describe_parameters with allow_undeclared_ enabled
*/
TEST_F(TestParameterClient, sync_parameter_describe_parameters_allow_undeclared) {
node_with_option->declare_parameter("foo", 4);
node_with_option->declare_parameter("bar", "this is bar");
auto synchronous_client =
std::make_shared<rclcpp::SyncParametersClient>(node_with_option);
{
std::vector<std::string> names{"none"};
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs =
synchronous_client->describe_parameters(names, 10s);
ASSERT_EQ(1u, parameter_descs.size());
ASSERT_EQ("none", parameter_descs[0].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_NOT_SET, parameter_descs[0].type);
ASSERT_EQ("", parameter_descs[0].description);
ASSERT_EQ("", parameter_descs[0].additional_constraints);
ASSERT_FALSE(parameter_descs[0].read_only);
}
{
std::vector<std::string> names{"foo", "baz"};
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs =
synchronous_client->describe_parameters(names, 10s);
ASSERT_EQ(2u, parameter_descs.size());
ASSERT_EQ("foo", parameter_descs[0].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_descs[0].type);
ASSERT_EQ("", parameter_descs[0].description);
ASSERT_EQ("", parameter_descs[0].additional_constraints);
ASSERT_FALSE(parameter_descs[0].read_only);
ASSERT_EQ("baz", parameter_descs[1].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_NOT_SET, parameter_descs[1].type);
ASSERT_EQ("", parameter_descs[1].description);
ASSERT_EQ("", parameter_descs[1].additional_constraints);
ASSERT_FALSE(parameter_descs[1].read_only);
}
{
std::vector<std::string> names{"baz", "foo"};
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs =
synchronous_client->describe_parameters(names, 10s);
ASSERT_EQ(2u, parameter_descs.size());
ASSERT_EQ("baz", parameter_descs[0].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_NOT_SET, parameter_descs[0].type);
ASSERT_EQ("", parameter_descs[0].description);
ASSERT_EQ("", parameter_descs[0].additional_constraints);
ASSERT_FALSE(parameter_descs[0].read_only);
ASSERT_EQ("foo", parameter_descs[1].name);
ASSERT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_descs[1].type);
ASSERT_EQ("", parameter_descs[1].description);
ASSERT_EQ("", parameter_descs[1].additional_constraints);
ASSERT_FALSE(parameter_descs[1].read_only);
}
}

View File

@@ -22,6 +22,8 @@
#include "rclcpp/rclcpp.hpp"
#include "../../src/rclcpp/parameter_service_names.hpp"
using namespace std::chrono_literals;
// This tests the ParameterService as it is included in an rclcpp::Node. Creating a separate
// ParameterService would interfere with the built-in one
class TestParameterService : public ::testing::Test
@@ -59,12 +61,12 @@ TEST_F(TestParameterService, get_parameter_types) {
node->declare_parameter("parameter1", rclcpp::ParameterValue(42));
const std::vector<std::string> declared_parameters = {"parameter1"};
const auto parameter_types = client->get_parameter_types(declared_parameters);
const auto parameter_types = client->get_parameter_types(declared_parameters, 10s);
ASSERT_EQ(1u, parameter_types.size());
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_types[0]);
const std::vector<std::string> undeclared_parameters = {"parameter2"};
const auto undeclared_parameter_types = client->get_parameter_types(undeclared_parameters);
const auto undeclared_parameter_types = client->get_parameter_types(undeclared_parameters, 10s);
EXPECT_EQ(0u, undeclared_parameter_types.size());
}
@@ -75,7 +77,7 @@ TEST_F(TestParameterService, set_parameters) {
const std::vector<rclcpp::Parameter> parameters = {
rclcpp::Parameter("parameter1", 0),
};
client->set_parameters(parameters);
client->set_parameters(parameters, 10s);
EXPECT_EQ(0, client->get_parameter("parameter1", 100));
}
@@ -86,54 +88,34 @@ TEST_F(TestParameterService, set_parameters_atomically) {
const std::vector<rclcpp::Parameter> parameters = {
rclcpp::Parameter("parameter1", 0),
};
client->set_parameters_atomically(parameters);
client->set_parameters_atomically(parameters, 10s);
EXPECT_EQ(0, client->get_parameter("parameter1", 100));
}
TEST_F(TestParameterService, list_parameters) {
const size_t number_parameters_in_basic_node = client->list_parameters({}, 1).names.size();
const size_t number_parameters_in_basic_node = client->list_parameters({}, 1, 10s).names.size();
node->declare_parameter("parameter1", rclcpp::ParameterValue(42));
const auto list_result = client->list_parameters({}, 1);
const auto list_result = client->list_parameters({}, 1, 10s);
EXPECT_EQ(1u + number_parameters_in_basic_node, list_result.names.size());
}
TEST_F(TestParameterService, describe_parameters) {
// There is no current API in ParameterClient for calling the describe_parameters service
// Update this test when https://github.com/ros2/rclcpp/issues/1354 is resolved
const std::string describe_parameters_service_name =
node->get_fully_qualified_name() + std::string("/") +
rclcpp::parameter_service_names::describe_parameters;
using ServiceT = rcl_interfaces::srv::DescribeParameters;
auto client =
node->create_client<ServiceT>(describe_parameters_service_name);
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(1)));
node->declare_parameter("parameter1", rclcpp::ParameterValue(42));
{
auto request = std::make_shared<rcl_interfaces::srv::DescribeParameters::Request>();
request->names.push_back("parameter1");
auto future = client->async_send_request(request);
rclcpp::spin_until_future_complete(node, future, std::chrono::seconds(1));
auto response = future.get();
ASSERT_NE(nullptr, response);
EXPECT_EQ(1u, response->descriptors.size());
auto descriptor = response->descriptors[0];
EXPECT_EQ("parameter1", descriptor.name);
EXPECT_EQ(rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER, descriptor.type);
const std::vector<std::string> names{"parameter1"};
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs =
client->describe_parameters(names, 10s);
ASSERT_EQ(1u, parameter_descs.size());
EXPECT_EQ("parameter1", parameter_descs[0].name);
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, parameter_descs[0].type);
}
{
auto request = std::make_shared<rcl_interfaces::srv::DescribeParameters::Request>();
request->names.push_back("undeclared_parameter");
auto future = client->async_send_request(request);
rclcpp::spin_until_future_complete(node, future, std::chrono::seconds(1));
auto response = future.get();
ASSERT_NE(nullptr, response);
EXPECT_EQ(0u, response->descriptors.size());
{
const std::vector<std::string> names{"undeclared_parameter"};
std::vector<rcl_interfaces::msg::ParameterDescriptor> parameter_descs =
client->describe_parameters(names, 10s);
EXPECT_EQ(0u, parameter_descs.size());
}
}

View File

@@ -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;
}
}
/*
@@ -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();
handler->execute(data);
}
}

View File

@@ -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)

View File

@@ -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);
}
}

View 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);
}

View 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);
}

View File

@@ -131,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>();

View File

@@ -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;}

View File

@@ -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;}

View File

@@ -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;}

View File

@@ -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;}

View File

@@ -3,6 +3,30 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6.3.0 (2021-01-25)
------------------
* Fix action server deadlock (`#1285 <https://github.com/ros2/rclcpp/issues/1285>`_) (`#1313 <https://github.com/ros2/rclcpp/issues/1313>`_)
* Contributors: Daisuke Sato
6.2.0 (2021-01-08)
------------------
* Goal response callback compatibility shim with deprecation of old signature (`#1495 <https://github.com/ros2/rclcpp/issues/1495>`_)
* [rclcpp_action] Add warnings (`#1405 <https://github.com/ros2/rclcpp/issues/1405>`_)
* Contributors: Audrow Nash, Ivan Santiago Paunovic
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>`_)

View File

@@ -13,7 +13,10 @@ if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic -Wnon-virtual-dtor -Woverloaded-virtual)
add_compile_options(
-Wall -Wextra -Wpedantic -Wnon-virtual-dtor -Woverloaded-virtual
-Wformat=2 -Wconversion -Wshadow -Wsign-conversion -Wcast-qual
)
endif()
set(${PROJECT_NAME}_SRCS

View File

@@ -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 2** 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 2**, 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 2**, 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 2**, 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]

View File

@@ -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 2** 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.

View File

@@ -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
// -----------------
@@ -263,13 +268,107 @@ public:
using Feedback = typename ActionT::Feedback;
using GoalHandle = ClientGoalHandle<ActionT>;
using WrappedResult = typename GoalHandle::WrappedResult;
using GoalResponseCallback = std::function<void (typename GoalHandle::SharedPtr)>;
using FeedbackCallback = typename GoalHandle::FeedbackCallback;
using ResultCallback = typename GoalHandle::ResultCallback;
using CancelRequest = typename ActionT::Impl::CancelGoalService::Request;
using CancelResponse = typename ActionT::Impl::CancelGoalService::Response;
using CancelCallback = std::function<void (typename CancelResponse::SharedPtr)>;
/// Compatibility wrapper for `goal_response_callback`.
class GoalResponseCallback
{
public:
using NewSignature = std::function<void (typename GoalHandle::SharedPtr)>;
using OldSignature = std::function<void (std::shared_future<typename GoalHandle::SharedPtr>)>;
GoalResponseCallback() = default;
GoalResponseCallback(std::nullptr_t) {} // NOLINT, intentionally implicit.
// implicit constructor
[[deprecated(
"Use new goal response callback signature "
"`std::function<void (Client<ActionT>::GoalHandle::SharedPtr)>` "
"instead of the old "
"`std::function<void (std::shared_future<Client<ActionT>::GoalHandle::SharedPtr>)>`.\n"
"e.g.:\n"
"```cpp\n"
"Client<ActionT>::SendGoalOptions options;\n"
"options.goal_response_callback = [](Client<ActionT>::GoalHandle::SharedPtr goal) {\n"
" // do something with `goal` here\n"
"};")]]
GoalResponseCallback(OldSignature old_callback) // NOLINT, intentionally implicit.
: old_callback_(std::move(old_callback)) {}
GoalResponseCallback(NewSignature new_callback) // NOLINT, intentionally implicit.
: new_callback_(std::move(new_callback)) {}
GoalResponseCallback &
operator=(OldSignature old_callback) {old_callback_ = std::move(old_callback); return *this;}
GoalResponseCallback &
operator=(NewSignature new_callback) {new_callback_ = std::move(new_callback); return *this;}
void
operator()(typename GoalHandle::SharedPtr goal_handle) const
{
if (new_callback_) {
new_callback_(std::move(goal_handle));
return;
}
if (old_callback_) {
throw std::runtime_error{
"Cannot call GoalResponseCallback(GoalHandle::SharedPtr) "
"if using the old goal response callback signature."};
}
throw std::bad_function_call{};
}
[[deprecated(
"Calling "
"`void goal_response_callback("
" std::shared_future<Client<ActionT>::GoalHandle::SharedPtr> goal_handle_shared_future)`"
" is deprecated.")]]
void
operator()(std::shared_future<typename GoalHandle::SharedPtr> goal_handle_future) const
{
if (old_callback_) {
old_callback_(std::move(goal_handle_future));
return;
}
if (new_callback_) {
new_callback_(std::move(goal_handle_future).get_future().share());
return;
}
throw std::bad_function_call{};
}
explicit operator bool() const noexcept {
return new_callback_ || old_callback_;
}
private:
friend class Client;
void
operator()(
typename GoalHandle::SharedPtr goal_handle,
std::shared_future<typename GoalHandle::SharedPtr> goal_handle_future) const
{
if (new_callback_) {
new_callback_(std::move(goal_handle));
return;
}
if (old_callback_) {
old_callback_(std::move(goal_handle_future));
return;
}
throw std::bad_function_call{};
}
NewSignature new_callback_;
OldSignature old_callback_;
};
/// Options for sending a goal.
/**
* This struct is used to pass parameters to the function `async_send_goal`.
@@ -358,7 +457,7 @@ public:
if (!goal_response->accepted) {
promise->set_value(nullptr);
if (options.goal_response_callback) {
options.goal_response_callback(nullptr);
options.goal_response_callback(nullptr, future);
}
return;
}
@@ -374,7 +473,7 @@ public:
}
promise->set_value(goal_handle);
if (options.goal_response_callback) {
options.goal_response_callback(goal_handle);
options.goal_response_callback(goal_handle, future);
}
if (options.result_callback) {

View File

@@ -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
@@ -377,31 +381,31 @@ protected:
std::weak_ptr<Server<ActionT>> weak_this = this->shared_from_this();
std::function<void(const GoalUUID &, std::shared_ptr<void>)> on_terminal_state =
[weak_this](const GoalUUID & uuid, std::shared_ptr<void> result_message)
[weak_this](const GoalUUID & goal_uuid, std::shared_ptr<void> result_message)
{
std::shared_ptr<Server<ActionT>> shared_this = weak_this.lock();
if (!shared_this) {
return;
}
// Send result message to anyone that asked
shared_this->publish_result(uuid, result_message);
shared_this->publish_result(goal_uuid, result_message);
// Publish a status message any time a goal handle changes state
shared_this->publish_status();
// notify base so it can recalculate the expired goal timer
shared_this->notify_goal_terminal_state();
// Delete data now (ServerBase and rcl_action_server_t keep data until goal handle expires)
std::lock_guard<std::mutex> lock(shared_this->goal_handles_mutex_);
shared_this->goal_handles_.erase(uuid);
shared_this->goal_handles_.erase(goal_uuid);
};
std::function<void(const GoalUUID &)> on_executing =
[weak_this](const GoalUUID & uuid)
[weak_this](const GoalUUID & goal_uuid)
{
std::shared_ptr<Server<ActionT>> shared_this = weak_this.lock();
if (!shared_this) {
return;
}
(void)uuid;
(void)goal_uuid;
// Publish a status message any time a goal handle changes state
shared_this->publish_status();
};

View File

@@ -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.1.0</version>
<version>6.3.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>

View File

@@ -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");

View File

@@ -24,7 +24,9 @@
#include <memory>
#include <mutex>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <vector>
using rclcpp_action::ServerBase;
@@ -43,7 +45,7 @@ public:
{
}
// Lock everything except user callbacks
// Lock for action_server_
std::recursive_mutex action_server_reentrant_mutex_;
std::shared_ptr<rcl_action_server_t> action_server_;
@@ -56,10 +58,13 @@ public:
size_t num_services_ = 0;
size_t num_guard_conditions_ = 0;
bool goal_request_ready_ = false;
bool cancel_request_ready_ = false;
bool result_request_ready_ = false;
bool goal_expired_ = false;
std::atomic<bool> goal_request_ready_{false};
std::atomic<bool> cancel_request_ready_{false};
std::atomic<bool> result_request_ready_{false};
std::atomic<bool> goal_expired_{false};
// Lock for unordered_maps
std::recursive_mutex unordered_map_mutex_;
// Results to be kept until the goal expires after reaching a terminal state
std::unordered_map<GoalUUID, std::shared_ptr<void>> goal_results_;
@@ -169,35 +174,109 @@ 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_->action_server_reentrant_mutex_);
rcl_ret_t ret = rcl_action_server_wait_set_get_entities_ready(
wait_set,
pimpl_->action_server_.get(),
&pimpl_->goal_request_ready_,
&pimpl_->cancel_request_ready_,
&pimpl_->result_request_ready_,
&pimpl_->goal_expired_);
bool goal_request_ready;
bool cancel_request_ready;
bool result_request_ready;
bool goal_expired;
rcl_ret_t ret;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_server_wait_set_get_entities_ready(
wait_set,
pimpl_->action_server_.get(),
&goal_request_ready,
&cancel_request_ready,
&result_request_ready,
&goal_expired);
}
pimpl_->goal_request_ready_ = goal_request_ready;
pimpl_->cancel_request_ready_ = cancel_request_ready;
pimpl_->result_request_ready_ = result_request_ready;
pimpl_->goal_expired_ = goal_expired;
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
return pimpl_->goal_request_ready_ ||
pimpl_->cancel_request_ready_ ||
pimpl_->result_request_ready_ ||
pimpl_->goal_expired_;
return pimpl_->goal_request_ready_.load() ||
pimpl_->cancel_request_ready_.load() ||
pimpl_->result_request_ready_.load() ||
pimpl_->goal_expired_.load();
}
std::shared_ptr<void>
ServerBase::take_data()
{
if (pimpl_->goal_request_ready_.load()) {
rcl_ret_t ret;
rcl_action_goal_info_t goal_info = rcl_action_get_zero_initialized_goal_info();
rmw_request_id_t request_header;
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
std::shared_ptr<void> message = create_goal_request();
ret = rcl_action_take_goal_request(
pimpl_->action_server_.get(),
&request_header,
message.get());
return std::static_pointer_cast<void>(
std::make_shared
<std::tuple<rcl_ret_t, rcl_action_goal_info_t, rmw_request_id_t, std::shared_ptr<void>>>(
ret,
goal_info,
request_header, message));
} else if (pimpl_->cancel_request_ready_.load()) {
rcl_ret_t ret;
rmw_request_id_t request_header;
// Initialize cancel request
auto request = std::make_shared<action_msgs::srv::CancelGoal::Request>();
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_take_cancel_request(
pimpl_->action_server_.get(),
&request_header,
request.get());
return std::static_pointer_cast<void>(
std::make_shared
<std::tuple<rcl_ret_t, std::shared_ptr<action_msgs::srv::CancelGoal::Request>,
rmw_request_id_t>>(ret, request, request_header));
} else if (pimpl_->result_request_ready_.load()) {
rcl_ret_t ret;
// Get the result request message
rmw_request_id_t request_header;
std::shared_ptr<void> result_request = create_result_request();
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_take_result_request(
pimpl_->action_server_.get(), &request_header, result_request.get());
return std::static_pointer_cast<void>(
std::make_shared<std::tuple<rcl_ret_t, std::shared_ptr<void>, rmw_request_id_t>>(
ret, result_request, request_header));
} else if (pimpl_->goal_expired_.load()) {
return nullptr;
} else {
throw std::runtime_error("Taking data from action server but nothing is ready");
}
}
void
ServerBase::execute()
ServerBase::execute(std::shared_ptr<void> & data)
{
if (pimpl_->goal_request_ready_) {
execute_goal_request_received();
} else if (pimpl_->cancel_request_ready_) {
execute_cancel_request_received();
} else if (pimpl_->result_request_ready_) {
execute_result_request_received();
} else if (pimpl_->goal_expired_) {
if (!data && !pimpl_->goal_expired_.load()) {
throw std::runtime_error("'data' is empty");
}
if (pimpl_->goal_request_ready_.load()) {
execute_goal_request_received(data);
} else if (pimpl_->cancel_request_ready_.load()) {
execute_cancel_request_received(data);
} else if (pimpl_->result_request_ready_.load()) {
execute_result_request_received(data);
} else if (pimpl_->goal_expired_.load()) {
execute_check_expired_goals();
} else {
throw std::runtime_error("Executing action server but nothing is ready");
@@ -205,22 +284,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_->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());
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
@@ -229,6 +297,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);
bool expected = true;
if (!pimpl_->goal_request_ready_.compare_exchange_strong(expected, false)) {
return;
}
GoalUUID uuid = get_goal_id_from_goal_request(message.get());
convert(uuid, &goal_info);
@@ -236,10 +312,13 @@ ServerBase::execute_goal_request_received()
// Call user's callback, getting the user's response and a ros message to send back
auto response_pair = call_handle_goal_callback(uuid, message);
ret = rcl_action_send_goal_response(
pimpl_->action_server_.get(),
&request_header,
response_pair.second.get());
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_send_goal_response(
pimpl_->action_server_.get(),
&request_header,
response_pair.second.get());
}
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
@@ -264,7 +343,10 @@ ServerBase::execute_goal_request_received()
}
};
rcl_action_goal_handle_t * rcl_handle;
rcl_handle = rcl_action_accept_new_goal(pimpl_->action_server_.get(), &goal_info);
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
rcl_handle = rcl_action_accept_new_goal(pimpl_->action_server_.get(), &goal_info);
}
if (!rcl_handle) {
throw std::runtime_error("Failed to accept new goal\n");
}
@@ -273,7 +355,10 @@ ServerBase::execute_goal_request_received()
// Copy out goal handle since action server storage disappears when it is fini'd
*handle = *rcl_handle;
pimpl_->goal_handles_[uuid] = handle;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
pimpl_->goal_handles_[uuid] = handle;
}
if (GoalResponse::ACCEPT_AND_EXECUTE == status) {
// Change status to executing
@@ -288,25 +373,16 @@ 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_->action_server_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);
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
@@ -315,6 +391,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();
@@ -325,10 +403,14 @@ ServerBase::execute_cancel_request_received()
// Get a list of goal info that should be attempted to be cancelled
rcl_action_cancel_response_t cancel_response = rcl_action_get_zero_initialized_cancel_response();
ret = rcl_action_process_cancel_request(
pimpl_->action_server_.get(),
&cancel_request,
&cancel_response);
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_process_cancel_request(
pimpl_->action_server_.get(),
&cancel_request,
&cancel_response);
}
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
@@ -371,26 +453,24 @@ ServerBase::execute_cancel_request_received()
publish_status();
}
ret = rcl_action_send_cancel_response(
pimpl_->action_server_.get(), &request_header, response.get());
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_send_cancel_response(
pimpl_->action_server_.get(), &request_header, response.get());
}
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_->action_server_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);
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
@@ -399,7 +479,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
@@ -407,7 +490,10 @@ ServerBase::execute_result_request_received()
rcl_action_goal_info_t goal_info;
convert(uuid, &goal_info);
bool goal_exists;
goal_exists = rcl_action_server_goal_exists(pimpl_->action_server_.get(), &goal_info);
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
goal_exists = rcl_action_server_goal_exists(pimpl_->action_server_.get(), &goal_info);
}
if (!goal_exists) {
// Goal does not exists
result_response = create_result_response(action_msgs::msg::GoalStatus::STATUS_UNKNOWN);
@@ -421,15 +507,18 @@ ServerBase::execute_result_request_received()
if (result_response) {
// Send the result now
ret = rcl_action_send_result_response(
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
rcl_ret_t rcl_ret = rcl_action_send_result_response(
pimpl_->action_server_.get(), &request_header, result_response.get());
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
if (RCL_RET_OK != rcl_ret) {
rclcpp::exceptions::throw_from_rcl_error(rcl_ret);
}
} else {
// Store the request so it can be responded to later
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
pimpl_->result_requests_[uuid].push_back(request_header);
}
data.reset();
}
void
@@ -441,9 +530,11 @@ 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_->action_server_reentrant_mutex_);
rcl_ret_t ret;
ret = rcl_action_expire_goals(pimpl_->action_server_.get(), expired_goals, 1, &num_expired);
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
ret = rcl_action_expire_goals(pimpl_->action_server_.get(), expired_goals, 1, &num_expired);
}
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
} else if (num_expired) {
@@ -451,6 +542,7 @@ ServerBase::execute_check_expired_goals()
GoalUUID uuid;
convert(expired_goals[0], &uuid);
RCLCPP_DEBUG(pimpl_->logger_, "Expired goal %s", to_string(uuid).c_str());
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
pimpl_->goal_results_.erase(uuid);
pimpl_->result_requests_.erase(uuid);
pimpl_->goal_handles_.erase(uuid);
@@ -523,20 +615,26 @@ 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_->action_server_reentrant_mutex_);
bool goal_exists;
goal_exists = rcl_action_server_goal_exists(pimpl_->action_server_.get(), &goal_info);
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
goal_exists = rcl_action_server_goal_exists(pimpl_->action_server_.get(), &goal_info);
}
if (!goal_exists) {
throw std::runtime_error("Asked to publish result for goal that does not exist");
}
pimpl_->goal_results_[uuid] = result_msg;
{
std::lock_guard<std::recursive_mutex> lock(pimpl_->unordered_map_mutex_);
pimpl_->goal_results_[uuid] = result_msg;
}
// if there are clients who already asked for the result, send it to them
auto iter = pimpl_->result_requests_.find(uuid);
if (iter != pimpl_->result_requests_.end()) {
for (auto & request_header : iter->second) {
std::lock_guard<std::recursive_mutex> lock(pimpl_->action_server_reentrant_mutex_);
rcl_ret_t ret = rcl_action_send_result_response(
pimpl_->action_server_.get(), &request_header, result_msg.get());
if (RCL_RET_OK != ret) {

View File

@@ -81,7 +81,7 @@ public:
// Should be checked by the server above
assert(goal->order > 0);
result->sequence.resize(goal->order);
result->sequence.resize(static_cast<size_t>(goal->order));
result->sequence[0] = 0;
if (goal->order == 1) {
current_goal_handle->succeed(result);
@@ -92,7 +92,7 @@ public:
current_goal_handle->succeed(result);
return;
}
for (int i = 2; i < goal->order; ++i) {
for (size_t i = 2; i < static_cast<size_t>(goal->order); ++i) {
result->sequence[i] =
result->sequence[i - 1] + result->sequence[i - 2];
}
@@ -310,8 +310,8 @@ BENCHMARK_F(ActionClientPerformanceTest, async_cancel_goal)(benchmark::State & s
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) {
using CancelActionResponse = test_msgs::action::Fibonacci::Impl::CancelGoalService::Response;
if (CancelActionResponse::ERROR_NONE != cancel_response->return_code) {
state.SkipWithError("Cancel request did not succeed");
break;
}
@@ -345,8 +345,8 @@ BENCHMARK_F(ActionClientPerformanceTest, async_cancel_all_goals)(benchmark::Stat
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) {
using CancelActionResponse = test_msgs::action::Fibonacci::Impl::CancelGoalService::Response;
if (CancelActionResponse::ERROR_NONE != cancel_response->return_code) {
state.SkipWithError("Cancel request did not succeed");
break;
}

View File

@@ -187,8 +187,8 @@ BENCHMARK_F(ActionServerPerformanceTest, action_server_cancel_goal)(benchmark::S
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) {
using CancelActionResponse = test_msgs::action::Fibonacci::Impl::CancelGoalService::Response;
if (CancelActionResponse::ERROR_NONE != cancel_response->return_code) {
state.SkipWithError("Cancel request did not succeed");
break;
}
@@ -247,12 +247,12 @@ BENCHMARK_F(ActionServerPerformanceTest, action_server_set_success)(benchmark::S
// 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>();
auto action_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);
action_result->sequence.push_back(i);
}
return result;
return action_result;
} ();
reset_heap_counters();
@@ -291,12 +291,12 @@ BENCHMARK_F(ActionServerPerformanceTest, action_server_abort)(benchmark::State &
// Capturing with & because MSVC and Clang disagree about how to capture goal_order
const auto result = [&]() {
auto result = std::make_shared<Fibonacci::Result>();
auto action_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);
action_result->sequence.push_back(i);
}
return result;
return action_result;
} ();
reset_heap_counters();

View File

@@ -132,7 +132,7 @@ protected:
feedback_message.feedback.sequence.push_back(1);
feedback_publisher->publish(feedback_message);
client_executor.spin_once();
for (int i = 1; i < goal_request->goal.order; ++i) {
for (size_t i = 1; i < static_cast<size_t>(goal_request->goal.order); ++i) {
feedback_message.feedback.sequence.push_back(
feedback_message.feedback.sequence[i] +
feedback_message.feedback.sequence[i - 1]);
@@ -536,6 +536,64 @@ TEST_F(TestClientAgainstServer, async_send_goal_with_goal_response_callback_wait
}
}
TEST_F(TestClientAgainstServer, async_send_goal_with_deprecated_goal_response_callback)
{
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
bool goal_response_received = false;
auto send_goal_ops = rclcpp_action::Client<ActionType>::SendGoalOptions();
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
send_goal_ops.goal_response_callback =
[&goal_response_received]
(std::shared_future<typename ActionGoalHandle::SharedPtr> goal_future)
{
if (goal_future.get()) {
goal_response_received = true;
}
};
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else
# pragma warning(pop)
#endif
{
ActionGoal bad_goal;
bad_goal.order = -1;
auto future_goal_handle = action_client->async_send_goal(bad_goal, send_goal_ops);
dual_spin_until_future_complete(future_goal_handle);
auto goal_handle = future_goal_handle.get();
EXPECT_FALSE(goal_response_received);
EXPECT_EQ(nullptr, goal_handle);
}
{
ActionGoal goal;
goal.order = 4;
auto future_goal_handle = action_client->async_send_goal(goal, send_goal_ops);
dual_spin_until_future_complete(future_goal_handle);
auto goal_handle = future_goal_handle.get();
EXPECT_TRUE(goal_response_received);
EXPECT_EQ(rclcpp_action::GoalStatus::STATUS_ACCEPTED, goal_handle->get_status());
EXPECT_FALSE(goal_handle->is_feedback_aware());
EXPECT_FALSE(goal_handle->is_result_aware());
auto future_result = action_client->async_get_result(goal_handle);
EXPECT_TRUE(goal_handle->is_result_aware());
dual_spin_until_future_complete(future_result);
auto wrapped_result = future_result.get();
ASSERT_EQ(5u, wrapped_result.result->sequence.size());
EXPECT_EQ(3, wrapped_result.result->sequence.back());
}
}
TEST_F(TestClientAgainstServer, async_send_goal_with_feedback_callback_wait_for_result)
{
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);

View File

@@ -1221,3 +1221,92 @@ TEST_F(TestCancelRequestServer, publish_status_send_cancel_response_errors)
EXPECT_THROW(SendClientCancelRequest(), std::runtime_error);
}
class TestDeadlockServer : public TestServer
{
public:
void SetUp()
{
node_ = std::make_shared<rclcpp::Node>("goal_request", "/rclcpp_action/goal_request");
uuid1_ = {{1, 2, 3, 4, 5, 6, 70, 80, 9, 1, 11, 120, 13, 140, 15, 160}};
uuid2_ = {{2, 2, 3, 4, 5, 6, 70, 80, 9, 1, 11, 120, 13, 140, 15, 160}};
action_server_ = rclcpp_action::create_server<Fibonacci>(
node_, "fibonacci",
[this](const GoalUUID &, std::shared_ptr<const Fibonacci::Goal>) {
// instead of making a deadlock, check if it can acquire the lock in a second
std::unique_lock<std::recursive_timed_mutex> lock(server_mutex_, std::defer_lock);
this->TryLockFor(lock, std::chrono::milliseconds(1000));
return rclcpp_action::GoalResponse::ACCEPT_AND_EXECUTE;
},
[this](std::shared_ptr<GoalHandle>) {
// instead of making a deadlock, check if it can acquire the lock in a second
std::unique_lock<std::recursive_timed_mutex> lock(server_mutex_, std::defer_lock);
this->TryLockFor(lock, std::chrono::milliseconds(1000));
return rclcpp_action::CancelResponse::ACCEPT;
},
[this](std::shared_ptr<GoalHandle> handle) {
// instead of making a deadlock, check if it can acquire the lock in a second
std::unique_lock<std::recursive_timed_mutex> lock(server_mutex_, std::defer_lock);
this->TryLockFor(lock, std::chrono::milliseconds(1000));
goal_handle_ = handle;
});
}
void GoalSucceeded()
{
std::lock_guard<std::recursive_timed_mutex> lock(server_mutex_);
rclcpp::sleep_for(std::chrono::milliseconds(100));
auto result = std::make_shared<Fibonacci::Result>();
result->sequence = {5, 8, 13, 21};
goal_handle_->succeed(result);
}
void GoalCanceled()
{
std::lock_guard<std::recursive_timed_mutex> lock(server_mutex_);
rclcpp::sleep_for(std::chrono::milliseconds(100));
auto result = std::make_shared<Fibonacci::Result>();
goal_handle_->canceled(result);
}
void TryLockFor(
std::unique_lock<std::recursive_timed_mutex> & lock,
std::chrono::milliseconds timeout
)
{
ASSERT_TRUE(lock.try_lock_for(timeout));
}
protected:
std::recursive_timed_mutex server_mutex_;
GoalUUID uuid1_, uuid2_;
std::shared_ptr<rclcpp::Node> node_;
std::shared_ptr<rclcpp_action::Server<Fibonacci>> action_server_;
using GoalHandle = rclcpp_action::ServerGoalHandle<Fibonacci>;
std::shared_ptr<GoalHandle> goal_handle_;
};
TEST_F(TestDeadlockServer, deadlock_while_succeed)
{
send_goal_request(node_, uuid1_);
// this will lock wrapper's mutex and intentionally wait 100ms for calling succeed
// to try to acquire the lock of rclcpp_action mutex
std::thread t(&TestDeadlockServer::GoalSucceeded, this);
// after the wrapper's mutex is locked and before succeed is called
rclcpp::sleep_for(std::chrono::milliseconds(50));
// call next goal request to intentionally reproduce deadlock
// this first locks rclcpp_action mutex and then call callback to lock wrapper's mutex
send_goal_request(node_, uuid2_);
t.join();
}
TEST_F(TestDeadlockServer, deadlock_while_canceled)
{
send_goal_request(node_, uuid1_);
send_cancel_request(node_, uuid1_);
std::thread t(&TestDeadlockServer::GoalCanceled, this);
rclcpp::sleep_for(std::chrono::milliseconds(50));
send_goal_request(node_, uuid2_); // deadlock here
t.join();
}

View File

@@ -25,12 +25,12 @@ TEST(TestActionTypes, goal_uuid_to_string) {
EXPECT_STREQ("0123456789abcdef", rclcpp_action::to_string(goal_id).c_str());
for (uint8_t i = 0; i < UUID_SIZE; ++i) {
goal_id[i] = 16u + i;
goal_id[i] = static_cast<uint8_t>(16u + i);
}
EXPECT_STREQ("101112131415161718191a1b1c1d1e1f", rclcpp_action::to_string(goal_id).c_str());
for (uint8_t i = 0; i < UUID_SIZE; ++i) {
goal_id[i] = std::numeric_limits<uint8_t>::max() - i;
goal_id[i] = static_cast<uint8_t>(std::numeric_limits<uint8_t>::max() - i);
}
EXPECT_STREQ("fffefdfcfbfaf9f8f7f6f5f4f3f2f1f0", rclcpp_action::to_string(goal_id).c_str());
}

View File

@@ -2,6 +2,26 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6.3.0 (2021-01-25)
------------------
6.2.0 (2021-01-08)
------------------
* Use std compliant non-method std::filesystem::exists function (`#1502 <https://github.com/ros2/rclcpp/issues/1502>`_)
* Fix string literal warnings (`#1442 <https://github.com/ros2/rclcpp/issues/1442>`_)
* Contributors: Audrow Nash, Josh Langsfeld
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>`_)

View File

@@ -7,7 +7,10 @@ if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
add_compile_options(-Wall -Wextra -Wpedantic -Wnon-virtual-dtor -Woverloaded-virtual)
add_compile_options(
-Wall -Wextra -Wpedantic -Wnon-virtual-dtor -Woverloaded-virtual
-Wformat=2 -Wconversion -Wshadow -Wsign-conversion -Wold-style-cast -Wcast-qual
)
endif()
find_package(ament_cmake_ros REQUIRED)
@@ -65,6 +68,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 +116,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(

View File

@@ -2,7 +2,7 @@ This document is a declaration of software quality for the `rclcpp_components` p
# rclcpp_components Quality Declaration
The package `rclcpp_components` claims to be in the **Quality Level 2** category.
The package `rclcpp_components` claims to be in the **Quality Level 1** category.
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Quality Categories in REP-2004](https://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_components` 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_components/) 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_components/) 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_components`.
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_components/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_components/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_components_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_components_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_components` 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_components` are located in the [test/benchmark directory](https://github.com/ros2/rclcpp/tree/master/rclcpp_components/test/benchmark).
Package and system level performance benchmarks that cover features of `rclcpp_components` 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]
@@ -157,25 +165,25 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
The `class_loader` package provides a ROS-independent package for loading plugins during runtime
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros/class_loader/blob/ros2/QUALITY_DECLARATION.md).
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros/class_loader/blob/ros2/QUALITY_DECLARATION.md).
#### `composition_interfaces`
The `composition_interfaces` package contains message and service definitions for managing composable nodes in a container process.
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/composition_interfaces/QUALITY_DECLARATION.md).
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/composition_interfaces/QUALITY_DECLARATION.md).
#### `rclcpp`
The `rclcpp` package provides the ROS client library in C++.
It is **Quality Level 2**, 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).
#### `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).
### Direct Runtime non-ROS Dependency [5.iii]

View File

@@ -6,4 +6,4 @@ Visit the [rclcpp_components API documentation](http://docs.ros2.org/latest/api/
## Quality Declaration
This package claims to be in the **Quality Level 2** 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.

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp_components</name>
<version>5.1.0</version>
<version>6.3.0</version>
<description>Package containing tools for dynamically loadable components</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
<maintainer email="mabel@openrobotics.org">Mabel Zhang</maintainer>
@@ -23,6 +23,7 @@
<exec_depend>composition_interfaces</exec_depend>
<exec_depend>rclcpp</exec_depend>
<test_depend>ament_cmake_google_benchmark</test_depend>
<test_depend>ament_cmake_gtest</test_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>

View File

@@ -223,7 +223,7 @@ ComponentManager::OnLoadNode(
response->error_message = "Failed to find class with the requested plugin name.";
response->success = false;
} catch (const ComponentManagerException & ex) {
RCLCPP_ERROR(get_logger(), ex.what());
RCLCPP_ERROR(get_logger(), "%s", ex.what());
response->error_message = ex.what();
response->success = false;
}
@@ -244,7 +244,7 @@ ComponentManager::OnUnloadNode(
std::stringstream ss;
ss << "No node found with unique_id: " << request->unique_id;
response->error_message = ss.str();
RCLCPP_WARN(get_logger(), ss.str());
RCLCPP_WARN(get_logger(), "%s", ss.str().c_str());
} else {
if (auto exec = executor_.lock()) {
exec->remove_node(wrapper->second.get_node_base_interface());

View File

@@ -0,0 +1,121 @@
// 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 "benchmark/benchmark.h"
#include <rcutils/logging.h>
#include <memory>
#include <string>
#include <vector>
#include "rclcpp_components/component_manager.hpp"
class ComponentTest : public benchmark::Fixture
{
public:
ComponentTest()
: component_manager_name("my_manager")
{
}
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
#endif
void SetUp(benchmark::State &) override
{
rcutils_logging_set_default_logger_level(RCUTILS_LOG_SEVERITY_WARN);
context = std::make_shared<rclcpp::Context>();
context->init(0, nullptr, rclcpp::InitOptions().auto_initialize_logging(false));
rclcpp::ExecutorOptions exec_options;
exec_options.context = context;
executor = std::make_shared<rclcpp::executors::SingleThreadedExecutor>(exec_options);
manager = std::make_shared<rclcpp_components::ComponentManager>(
executor, component_manager_name, rclcpp::NodeOptions().context(context));
executor->add_node(manager);
}
void TearDown(benchmark::State &) override
{
context->shutdown("Test is complete");
manager.reset();
executor.reset();
context.reset();
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
const std::string component_manager_name;
protected:
rclcpp::Context::SharedPtr context;
rclcpp::executors::SingleThreadedExecutor::SharedPtr executor;
std::shared_ptr<rclcpp_components::ComponentManager> manager;
};
BENCHMARK_F(ComponentTest, get_component_resources)(benchmark::State & state)
{
for (auto _ : state) {
std::vector<rclcpp_components::ComponentManager::ComponentResource> resources =
manager->get_component_resources("rclcpp_components");
if (resources.size() != 3) {
state.SkipWithError("Wrong number of components found");
break;
}
}
}
BENCHMARK_F(ComponentTest, create_component_factory)(benchmark::State & state)
{
const std::vector<rclcpp_components::ComponentManager::ComponentResource> resources =
manager->get_component_resources("rclcpp_components");
if (resources.size() != 3) {
state.SkipWithError("Wrong number of components found");
return;
}
for (auto _ : state) {
manager->create_component_factory(resources[0]).reset();
}
}
BENCHMARK_F(ComponentTest, create_node_instance)(benchmark::State & state)
{
const std::vector<rclcpp_components::ComponentManager::ComponentResource> resources =
manager->get_component_resources("rclcpp_components");
if (resources.size() != 3) {
state.SkipWithError("Wrong number of components found");
return;
}
// Choosing resource 0 - the other two test components were shown empirically to yield
// the same performance charactarisitics, so they shouldn't need their own benchmarks.
const std::shared_ptr<rclcpp_components::NodeFactory> factory =
manager->create_component_factory(resources[0]);
const rclcpp::NodeOptions options = rclcpp::NodeOptions().context(context);
for (auto _ : state) {
rclcpp_components::NodeInstanceWrapper node = factory->create_node_instance(options);
benchmark::DoNotOptimize(node);
benchmark::ClobberMemory();
}
}

View File

@@ -51,9 +51,10 @@ TEST_F(TestComponentManager, get_component_resources_valid)
EXPECT_EQ("test_rclcpp_components::TestComponentBar", resources[1].first);
EXPECT_EQ("test_rclcpp_components::TestComponentNoNode", resources[2].first);
EXPECT_TRUE(rcpputils::fs::path(resources[0].second).exists());
EXPECT_TRUE(rcpputils::fs::path(resources[1].second).exists());
EXPECT_TRUE(rcpputils::fs::path(resources[2].second).exists());
namespace fs = rcpputils::fs;
EXPECT_TRUE(fs::exists(fs::path(resources[0].second)));
EXPECT_TRUE(fs::exists(fs::path(resources[1].second)));
EXPECT_TRUE(fs::exists(fs::path(resources[2].second)));
}
TEST_F(TestComponentManager, create_component_factory_valid)
@@ -85,8 +86,8 @@ TEST_F(TestComponentManager, create_component_factory_invalid)
rclcpp_components::ComponentManagerException);
// Test valid library with invalid class
auto resources = manager->get_component_resources("rclcpp_components");
auto factory = manager->create_component_factory({"foo_class", resources[0].second});
auto component_resources = manager->get_component_resources("rclcpp_components");
auto factory = manager->create_component_factory({"foo_class", component_resources[0].second});
EXPECT_EQ(nullptr, factory);
// Test improperly formed resources file

View File

@@ -45,10 +45,10 @@ TEST_F(TestComponentManager, components_api)
exec->add_node(manager);
exec->add_node(node);
auto client = node->create_client<composition_interfaces::srv::LoadNode>(
auto composition_client = node->create_client<composition_interfaces::srv::LoadNode>(
"/ComponentManager/_container/load_node");
if (!client->wait_for_service(20s)) {
if (!composition_client->wait_for_service(20s)) {
ASSERT_TRUE(false) << "service not available after waiting";
}
@@ -57,7 +57,7 @@ TEST_F(TestComponentManager, components_api)
request->package_name = "rclcpp_components";
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
auto result = client->async_send_request(request);
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
@@ -71,7 +71,7 @@ TEST_F(TestComponentManager, components_api)
request->package_name = "rclcpp_components";
request->plugin_name = "test_rclcpp_components::TestComponentBar";
auto result = client->async_send_request(request);
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
@@ -87,7 +87,7 @@ TEST_F(TestComponentManager, components_api)
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
request->node_name = "test_component_baz";
auto result = client->async_send_request(request);
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
@@ -104,7 +104,7 @@ TEST_F(TestComponentManager, components_api)
request->node_namespace = "/ns";
request->node_name = "test_component_bing";
auto result = client->async_send_request(request);
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
@@ -119,7 +119,7 @@ TEST_F(TestComponentManager, components_api)
request->package_name = "rclcpp_components";
request->plugin_name = "test_rclcpp_components::TestComponent";
auto result = client->async_send_request(request);
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, false);
@@ -134,7 +134,7 @@ TEST_F(TestComponentManager, components_api)
request->package_name = "rclcpp_components_foo";
request->plugin_name = "test_rclcpp_components::TestComponentFoo";
auto result = client->async_send_request(request);
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, false);
@@ -151,7 +151,7 @@ TEST_F(TestComponentManager, components_api)
request->node_name = "test_component_remap";
request->remap_rules.push_back("alice:=bob");
auto result = client->async_send_request(request);
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
@@ -170,7 +170,7 @@ TEST_F(TestComponentManager, components_api)
rclcpp::ParameterValue(true));
request->extra_arguments.push_back(use_intraprocess_comms.to_parameter_msg());
auto result = client->async_send_request(request);
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, true);
@@ -191,7 +191,7 @@ TEST_F(TestComponentManager, components_api)
rclcpp::ParameterValue("hello"));
request->extra_arguments.push_back(use_intraprocess_comms.to_parameter_msg());
auto result = client->async_send_request(request);
auto result = composition_client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(result.get()->success, false);
@@ -226,23 +226,23 @@ TEST_F(TestComponentManager, components_api)
auto result = client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
auto node_names = result.get()->full_node_names;
auto unique_ids = result.get()->unique_ids;
auto result_node_names = result.get()->full_node_names;
auto result_unique_ids = result.get()->unique_ids;
EXPECT_EQ(node_names.size(), 6u);
EXPECT_EQ(node_names[0], "/test_component_foo");
EXPECT_EQ(node_names[1], "/test_component_bar");
EXPECT_EQ(node_names[2], "/test_component_baz");
EXPECT_EQ(node_names[3], "/ns/test_component_bing");
EXPECT_EQ(node_names[4], "/test_component_remap");
EXPECT_EQ(node_names[5], "/test_component_intra_process");
EXPECT_EQ(unique_ids.size(), 6u);
EXPECT_EQ(unique_ids[0], 1u);
EXPECT_EQ(unique_ids[1], 2u);
EXPECT_EQ(unique_ids[2], 3u);
EXPECT_EQ(unique_ids[3], 4u);
EXPECT_EQ(unique_ids[4], 5u);
EXPECT_EQ(unique_ids[5], 6u);
EXPECT_EQ(result_node_names.size(), 6u);
EXPECT_EQ(result_node_names[0], "/test_component_foo");
EXPECT_EQ(result_node_names[1], "/test_component_bar");
EXPECT_EQ(result_node_names[2], "/test_component_baz");
EXPECT_EQ(result_node_names[3], "/ns/test_component_bing");
EXPECT_EQ(result_node_names[4], "/test_component_remap");
EXPECT_EQ(result_node_names[5], "/test_component_intra_process");
EXPECT_EQ(result_unique_ids.size(), 6u);
EXPECT_EQ(result_unique_ids[0], 1u);
EXPECT_EQ(result_unique_ids[1], 2u);
EXPECT_EQ(result_unique_ids[2], 3u);
EXPECT_EQ(result_unique_ids[3], 4u);
EXPECT_EQ(result_unique_ids[4], 5u);
EXPECT_EQ(result_unique_ids[5], 6u);
}
}
@@ -290,21 +290,21 @@ TEST_F(TestComponentManager, components_api)
auto result = client->async_send_request(request);
auto ret = exec->spin_until_future_complete(result, 5s); // Wait for the result.
EXPECT_EQ(ret, rclcpp::FutureReturnCode::SUCCESS);
auto node_names = result.get()->full_node_names;
auto unique_ids = result.get()->unique_ids;
auto result_node_names = result.get()->full_node_names;
auto result_unique_ids = result.get()->unique_ids;
EXPECT_EQ(node_names.size(), 5u);
EXPECT_EQ(node_names[0], "/test_component_bar");
EXPECT_EQ(node_names[1], "/test_component_baz");
EXPECT_EQ(node_names[2], "/ns/test_component_bing");
EXPECT_EQ(node_names[3], "/test_component_remap");
EXPECT_EQ(node_names[4], "/test_component_intra_process");
EXPECT_EQ(unique_ids.size(), 5u);
EXPECT_EQ(unique_ids[0], 2u);
EXPECT_EQ(unique_ids[1], 3u);
EXPECT_EQ(unique_ids[2], 4u);
EXPECT_EQ(unique_ids[3], 5u);
EXPECT_EQ(unique_ids[4], 6u);
EXPECT_EQ(result_node_names.size(), 5u);
EXPECT_EQ(result_node_names[0], "/test_component_bar");
EXPECT_EQ(result_node_names[1], "/test_component_baz");
EXPECT_EQ(result_node_names[2], "/ns/test_component_bing");
EXPECT_EQ(result_node_names[3], "/test_component_remap");
EXPECT_EQ(result_node_names[4], "/test_component_intra_process");
EXPECT_EQ(result_unique_ids.size(), 5u);
EXPECT_EQ(result_unique_ids[0], 2u);
EXPECT_EQ(result_unique_ids[1], 3u);
EXPECT_EQ(result_unique_ids[2], 4u);
EXPECT_EQ(result_unique_ids[3], 5u);
EXPECT_EQ(result_unique_ids[4], 6u);
}
}
}

View File

@@ -3,6 +3,26 @@ Changelog for package rclcpp_lifecycle
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
6.3.0 (2021-01-25)
------------------
6.2.0 (2021-01-08)
------------------
6.1.0 (2020-12-10)
------------------
* add LifecycleNode::get_transition_graph to match services. (`#1472 <https://github.com/ros2/rclcpp/issues/1472>`_)
* Update QDs to QL 1 (`#1477 <https://github.com/ros2/rclcpp/issues/1477>`_)
* Benchmark lifecycle features (`#1462 <https://github.com/ros2/rclcpp/issues/1462>`_)
* Contributors: Stephen Brawner, brawner, tomoya
6.0.0 (2020-11-18)
------------------
* Reserve vector capacities and use emplace_back for constructing vectors (`#1464 <https://github.com/ros2/rclcpp/issues/1464>`_)
* [rclcpp_lifecycle] Change uint8_t iterator variables to size_t (`#1461 <https://github.com/ros2/rclcpp/issues/1461>`_)
* Bump rclcpp packages to Quality Level 2 (`#1445 <https://github.com/ros2/rclcpp/issues/1445>`_)
* Contributors: Louise Poubel, brawner
5.1.0 (2020-11-02)
------------------
* Increase test timeouts of slow running tests with rmw_connext_cpp (`#1400 <https://github.com/ros2/rclcpp/issues/1400>`_)

View File

@@ -51,6 +51,35 @@ if(BUILD_TESTING)
set(ament_cmake_cppcheck_ADDITIONAL_INCLUDE_DIRS ${rclcpp_INCLUDE_DIRS})
ament_lint_auto_find_test_dependencies()
find_package(performance_test_fixture REQUIRED)
add_performance_test(
benchmark_lifecycle_client
test/benchmark/benchmark_lifecycle_client.cpp)
if(TARGET benchmark_lifecycle_client)
target_link_libraries(benchmark_lifecycle_client ${PROJECT_NAME})
ament_target_dependencies(benchmark_lifecycle_client rclcpp)
endif()
add_performance_test(
benchmark_lifecycle_node
test/benchmark/benchmark_lifecycle_node.cpp)
if(TARGET benchmark_lifecycle_node)
target_link_libraries(benchmark_lifecycle_node ${PROJECT_NAME})
ament_target_dependencies(benchmark_lifecycle_node rclcpp)
endif()
add_performance_test(
benchmark_state
test/benchmark/benchmark_state.cpp)
if(TARGET benchmark_state)
target_link_libraries(benchmark_state ${PROJECT_NAME})
endif()
add_performance_test(
benchmark_transition
test/benchmark/benchmark_transition.cpp)
if(TARGET benchmark_transition)
target_link_libraries(benchmark_transition ${PROJECT_NAME})
endif()
ament_add_gtest(test_lifecycle_node test/test_lifecycle_node.cpp TIMEOUT 120)
if(TARGET test_lifecycle_node)
ament_target_dependencies(test_lifecycle_node

View File

@@ -2,7 +2,7 @@ This document is a declaration of software quality for the `rclcpp_lifecycle` pa
# rclcpp_lifecycle Quality Declaration
The package `rclcpp_lifecycle` claims to be in the **Quality Level 2** category.
The package `rclcpp_lifecycle` claims to be in the **Quality Level 1** category when 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_lifecycle` 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_lifecycle/) 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_lifecycle/) 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_lifecycle`.
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_lifecycle/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_lifecycle/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_lifecycle_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_lifecycle_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_lifecycle` 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_lifecycle` are located in the [test/benchmark directory](https://github.com/ros2/rclcpp/tree/master/rclcpp_lifecycle/test/benchmark).
Package and system level performance benchmarks that cover features of `rclcpp_lifecycle` 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,31 +159,31 @@ It also has several test dependencies, which do not affect the resulting quality
The `lifecycle_msgs` package contains message and service definitions for managing lifecycle nodes.
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/lifecycle_msgs/QUALITY_DECLARATION.md).
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcl_interfaces/blob/master/lifecycle_msgs/QUALITY_DECLARATION.md).
#### `rclcpp`
The `rclcpp` package provides the ROS client library in C++.
It is **Quality Level 2**, 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_lifecycle`
The `rcl_lifecycle` package provides functionality for ROS 2 lifecycle nodes in C.
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros2/rcl/blob/master/rcl_lifecycle/QUALITY_DECLARATION.md).
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rcl/blob/master/rcl_lifecycle/QUALITY_DECLARATION.md).
#### `rosidl_typesupport_cpp`
The `rosidl_typesupport_cpp` package generates the type support for C++ messages.
It is **Quality Level 2**, see its [Quality Declaration document](https://github.com/ros2/rosidl_typesupport/blob/master/rosidl_typesupport_cpp/QUALITY_DECLARATION.md).
It is **Quality Level 1**, see its [Quality Declaration document](https://github.com/ros2/rosidl_typesupport/blob/master/rosidl_typesupport_cpp/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).
### Direct Runtime non-ROS Dependency [5.iii]

Some files were not shown because too many files have changed in this diff Show More