Compare commits
94 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
85ba923a5e | ||
|
|
a803ae61c1 | ||
|
|
f50ec36539 | ||
|
|
27edfe16d2 | ||
|
|
f79f57feb2 | ||
|
|
ed3f93724b | ||
|
|
6b1dffee35 | ||
|
|
b90980abb3 | ||
|
|
1332331751 | ||
|
|
525a418188 | ||
|
|
c3c5b693b7 | ||
|
|
d65ea282ac | ||
|
|
2f854aa9c3 | ||
|
|
d5fdfd5f27 | ||
|
|
0601de2ac8 | ||
|
|
de7f83cac4 | ||
|
|
2096d63a1b | ||
|
|
4cbc807f46 | ||
|
|
2445c37af3 | ||
|
|
a73ce84e32 | ||
|
|
ebdaf1f8ef | ||
|
|
37f0960640 | ||
|
|
b6a0b004a7 | ||
|
|
bb3233b029 | ||
|
|
c4ca0dfb2d | ||
|
|
83e0ec56b7 | ||
|
|
7aab9b6f99 | ||
|
|
7860a8c490 | ||
|
|
4d8a695c79 | ||
|
|
f4b1b7694f | ||
|
|
d126fe2b34 | ||
|
|
a8b90f5221 | ||
|
|
2b8ab746ff | ||
|
|
8a145880bd | ||
|
|
dd807bf4b5 | ||
|
|
8266f85bc8 | ||
|
|
b15203914b | ||
|
|
86142c3aac | ||
|
|
379430b2ce | ||
|
|
2412329935 | ||
|
|
fe6aca3faf | ||
|
|
f3d66b892e | ||
|
|
3ca1e69c20 | ||
|
|
405687cc5e | ||
|
|
52d2bbfb8a | ||
|
|
b6acdc9ab6 | ||
|
|
a982829f69 | ||
|
|
1aa7c7a969 | ||
|
|
04502128a4 | ||
|
|
6d586d17aa | ||
|
|
9c7e591e62 | ||
|
|
c31daa608d | ||
|
|
4c239d30a9 | ||
|
|
d24a31738c | ||
|
|
7dadf343b7 | ||
|
|
1044236ade | ||
|
|
abc76111dc | ||
|
|
2eaa2eb355 | ||
|
|
9dd2c91d7d | ||
|
|
7c896265a6 | ||
|
|
985e320de3 | ||
|
|
eac2e8a7dd | ||
|
|
cff2711aba | ||
|
|
608e2f2e56 | ||
|
|
bd695f4a3b | ||
|
|
74162b19bf | ||
|
|
e217532817 | ||
|
|
a7b8ada6a7 | ||
|
|
df09c6e77d | ||
|
|
06ae92ae96 | ||
|
|
99b3803ce2 | ||
|
|
bbe2b99893 | ||
|
|
09e5dd70f4 | ||
|
|
5551facd9b | ||
|
|
38ed9ed172 | ||
|
|
fad351ff85 | ||
|
|
08c6cd9f9a | ||
|
|
830a1f0212 | ||
|
|
1b4485443d | ||
|
|
d73fc2a86a | ||
|
|
e083bf9b7d | ||
|
|
c3bc232eae | ||
|
|
7f5b44ebe2 | ||
|
|
8ab10aabc0 | ||
|
|
119a7bcbac | ||
|
|
0beba97156 | ||
|
|
c88a3602ab | ||
|
|
258b0a06bb | ||
|
|
bf6a6733a1 | ||
|
|
4f17dee383 | ||
|
|
2c23e3a73a | ||
|
|
8934b5a0a9 | ||
|
|
3407564a15 | ||
|
|
8b3be2ad7e |
13
.github/workflows/mirror-rolling-to-master.yaml
vendored
13
.github/workflows/mirror-rolling-to-master.yaml
vendored
@@ -1,13 +0,0 @@
|
||||
name: Mirror rolling to master
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ rolling ]
|
||||
|
||||
jobs:
|
||||
mirror-to-master:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: zofrex/mirror-branch@v1
|
||||
with:
|
||||
target-branch: master
|
||||
@@ -1,2 +0,0 @@
|
||||
# This file was generated by https://github.com/audrow/update-ros2-repos
|
||||
* @ivanpauno @hidmic @wjwwood
|
||||
@@ -2,49 +2,213 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
28.3.3 (2024-07-29)
|
||||
-------------------
|
||||
* Only compile the tests once. (`#2590 <https://github.com/ros2/rclcpp/issues/2590>`_)
|
||||
* Contributors: Chris Lalancette
|
||||
28.1.15 (2025-12-23)
|
||||
--------------------
|
||||
* Unified Node Interfaces: Add const version of get_node_x_interface() (`#3006 <https://github.com/ros2/rclcpp/issues/3006>`_) (`#3009 <https://github.com/ros2/rclcpp/issues/3009>`_)
|
||||
* remove I/O from signal handler. (`#3000 <https://github.com/ros2/rclcpp/issues/3000>`_) (`#3004 <https://github.com/ros2/rclcpp/issues/3004>`_)
|
||||
* correct test function descriptions (`#2970 <https://github.com/ros2/rclcpp/issues/2970>`_) (`#2993 <https://github.com/ros2/rclcpp/issues/2993>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
28.3.2 (2024-07-24)
|
||||
28.1.14 (2025-11-18)
|
||||
--------------------
|
||||
* Fix REP url locations (`#2987 <https://github.com/ros2/rclcpp/issues/2987>`_) (`#2990 <https://github.com/ros2/rclcpp/issues/2990>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
28.1.13 (2025-10-21)
|
||||
--------------------
|
||||
|
||||
28.1.12 (2025-09-11)
|
||||
--------------------
|
||||
* Removed warning test_qos (`#2859 <https://github.com/ros2/rclcpp/issues/2859>`_) (`#2945 <https://github.com/ros2/rclcpp/issues/2945>`_)
|
||||
* Allow for implicitly convertable loggers as well (`#2922 <https://github.com/ros2/rclcpp/issues/2922>`_) (`#2936 <https://github.com/ros2/rclcpp/issues/2936>`_) (`#2937 <https://github.com/ros2/rclcpp/issues/2937>`_)
|
||||
* Fix: improve exception context for parameter_value_from (`#2917 <https://github.com/ros2/rclcpp/issues/2917>`_) (`#2920 <https://github.com/ros2/rclcpp/issues/2920>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
28.1.11 (2025-08-06)
|
||||
--------------------
|
||||
* Fix `start_type_description_service` param handling (`#2897 <https://github.com/ros2/rclcpp/issues/2897>`_) (`#2909 <https://github.com/ros2/rclcpp/issues/2909>`_)
|
||||
* Fix `start_type_description_service` param handling
|
||||
* Add test
|
||||
* Demonstrate different exceptions depending on node options
|
||||
* Same exact exception and `what()` message in both cases
|
||||
* Uncrustify
|
||||
---------
|
||||
(cherry picked from commit 4fb558ae7b2ce7ce9b546e103beaac4f99991e5c)
|
||||
Co-authored-by: Patrick Roncagliolo <ronca.pat@gmail.com>
|
||||
* Add qos parameter for wait_for_message function (`#2903 <https://github.com/ros2/rclcpp/issues/2903>`_) (`#2906 <https://github.com/ros2/rclcpp/issues/2906>`_)
|
||||
(cherry picked from commit 2fcef70ea78c2c3a45391e59aebb265c05113050)
|
||||
Co-authored-by: Sriharsha Ghanta <ghanta_sriharsha@mymail.sutd.edu.sg>
|
||||
Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
|
||||
* [jazzy] Expose `typesupport_helpers` API needed for the Rosbag2 (backport `#2858 <https://github.com/ros2/rclcpp/issues/2858>`_) (`#2902 <https://github.com/ros2/rclcpp/issues/2902>`_)
|
||||
* Expose `typesupport_helpers` API needed for the Rosbag2 (`#2858 <https://github.com/ros2/rclcpp/issues/2858>`_)
|
||||
* Expose extract_type_identifier and get_typesupport_library_path API
|
||||
- Rationale: We need to use this API in the Rosbag2
|
||||
- Reference PR https://github.com/ros2/rosbag2/pull/2017 in the Rosbag2
|
||||
* Use C++ style in doxygen documentation
|
||||
---------
|
||||
(cherry picked from commit 448287b1090567181c809e59a3c72eed5ef4c69c)
|
||||
# Conflicts:
|
||||
# rclcpp/include/rclcpp/typesupport_helpers.hpp
|
||||
* Address merge conflicts
|
||||
---------
|
||||
Co-authored-by: Michael Orlov <morlovmr@gmail.com>
|
||||
* Fujitatomoya/test append parameter override (`#2896 <https://github.com/ros2/rclcpp/issues/2896>`_) (`#2900 <https://github.com/ros2/rclcpp/issues/2900>`_)
|
||||
(cherry picked from commit 84c6fb1cfc945521680a0e1fccf5b32237acbcc3)
|
||||
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
|
||||
* Add overload of `append_parameter_override` (`#2891 <https://github.com/ros2/rclcpp/issues/2891>`_) (`#2895 <https://github.com/ros2/rclcpp/issues/2895>`_)
|
||||
(cherry picked from commit fa0cf2da31dfae7c83f185e3bf18cb8fd55f0f57)
|
||||
Co-authored-by: Patrick Roncagliolo <ronca.pat@gmail.com>
|
||||
* Event exec timer fix for https://github.com/ros2/rclcpp/issues/2889 (`#2890 <https://github.com/ros2/rclcpp/issues/2890>`_)
|
||||
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
|
||||
Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
|
||||
* Shutdown deadlock fix jazzy (`#2887 <https://github.com/ros2/rclcpp/issues/2887>`_)
|
||||
* fix: Don't deadlock if removing shutdown callbacks in a shutdown callback
|
||||
* refactor: Made fix API compatible
|
||||
---------
|
||||
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
|
||||
* fix test_publisher_with_system_default_qos. (`#2881 <https://github.com/ros2/rclcpp/issues/2881>`_) (`#2883 <https://github.com/ros2/rclcpp/issues/2883>`_)
|
||||
(cherry picked from commit e6577c6792f76a74e303cc0c061e89abeb8cb1a6)
|
||||
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
|
||||
* Contributors: Janosch Machowinski, mergify[bot]
|
||||
|
||||
28.1.10 (2025-06-23)
|
||||
--------------------
|
||||
* Fix for memory leaks in rclcpp::SerializedMessage (`#2861 <https://github.com/ros2/rclcpp/issues/2861>`_) (`#2864 <https://github.com/ros2/rclcpp/issues/2864>`_)
|
||||
* Added missing chrono includes (`#2854 <https://github.com/ros2/rclcpp/issues/2854>`_) (`#2856 <https://github.com/ros2/rclcpp/issues/2856>`_)
|
||||
* get_all_data_impl() does not handle null pointers properly, causing segmentation fault (backport `#2840 <https://github.com/ros2/rclcpp/issues/2840>`_) (`#2851 <https://github.com/ros2/rclcpp/issues/2851>`_)
|
||||
* QoSInitialization::from_rmw does not validate invalid history policy values, leading to silent failures (`#2841 <https://github.com/ros2/rclcpp/issues/2841>`_) (`#2845 <https://github.com/ros2/rclcpp/issues/2845>`_)
|
||||
* throws std::invalid_argument if ParameterEvent is NULL. (`#2814 <https://github.com/ros2/rclcpp/issues/2814>`_) (`#2825 <https://github.com/ros2/rclcpp/issues/2825>`_)
|
||||
* Merge pull request `#2821 <https://github.com/ros2/rclcpp/issues/2821>`_ from ros2/mergify/bp/jazzy/pr-2819
|
||||
* Fix race condition (`#2819 <https://github.com/ros2/rclcpp/issues/2819>`_)
|
||||
* Contributors: Michael Orlov, Pedro de Azeredo, mergify[bot]
|
||||
|
||||
28.1.9 (2025-04-23)
|
||||
-------------------
|
||||
* Updated rcpputils path API (`#2579 <https://github.com/ros2/rclcpp/issues/2579>`_)
|
||||
* Make the subscriber_triggered_to_receive_message test more reliable. (`#2584 <https://github.com/ros2/rclcpp/issues/2584>`_)
|
||||
* Make the subscriber_triggered_to_receive_message test more reliable.
|
||||
In the current code, inside of the timer we create the subscription
|
||||
and the publisher, publish immediately, and expect the subscription
|
||||
to get it immediately. But it may be the case that discovery
|
||||
hasn't even happened between the publisher and the subscription
|
||||
by the time the publish call happens.
|
||||
To make this more reliable, create the subscription and publish *before*
|
||||
we ever create and spin on the timer. This at least gives 100
|
||||
milliseconds for discovery to happen. That may not be quite enough
|
||||
to make this reliable on all platforms, but in my local testing this
|
||||
helps a lot. Prior to this change I can make this fail one out of 10
|
||||
times, and after the change I've run 100 times with no failures.
|
||||
* Have the EventsExecutor use more common code (`#2570 <https://github.com/ros2/rclcpp/issues/2570>`_)
|
||||
* move notify waitable setup to its own function
|
||||
* move mutex lock to retrieve_entity utility
|
||||
* use entities_need_rebuild\_ atomic bool in events-executors
|
||||
* remove duplicated set_on_ready_callback for notify_waitable
|
||||
* use mutex from base class rather than a new recursive mutex
|
||||
* use current_collection\_ member in events-executor
|
||||
* delay adding notify waitable to collection
|
||||
* postpone clearing the current collection
|
||||
* commonize notify waitable and collection
|
||||
* commonize add/remove node/cbg methods
|
||||
* fix linter errors
|
||||
* remove redundant typesupport check in serialization module (`#2808 <https://github.com/ros2/rclcpp/issues/2808>`_) (`#2815 <https://github.com/ros2/rclcpp/issues/2815>`_)
|
||||
(cherry picked from commit f78ed952b27acc63ef8022d78cb816c309a9ca3d)
|
||||
Co-authored-by: Tanishq Chaudhary <tanishqchaudhary101010@gmail.com>
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
28.1.8 (2025-04-02)
|
||||
-------------------
|
||||
|
||||
28.1.7 (2025-03-26)
|
||||
-------------------
|
||||
* fix(ClockConditionalVariable): Fixed potential crash on shutdown (`#2762 <https://github.com/ros2/rclcpp/issues/2762>`_)
|
||||
* doc: Added warning to not instantiate Clock directly with RCL_ROS_TIME (`#2769 <https://github.com/ros2/rclcpp/issues/2769>`_)
|
||||
* Backports: `#2768 <https://github.com/ros2/rclcpp/issues/2768>`_
|
||||
* Use rmw_event_type_is_supported in test_qos_event (`#2766 <https://github.com/ros2/rclcpp/issues/2766>`_)
|
||||
* Backports: `#2761 <https://github.com/ros2/rclcpp/issues/2761>`_
|
||||
* fix: Fixed expiring of goals if events executor is used (`#2674 <https://github.com/ros2/rclcpp/issues/2674>`_)
|
||||
* Executor strong reference fix (`#2754 <https://github.com/ros2/rclcpp/issues/2754>`_)
|
||||
* Backports: `#2745 <https://github.com/ros2/rclcpp/issues/2745>`_
|
||||
* Double gc executor fix (`#2753 <https://github.com/ros2/rclcpp/issues/2753>`_)
|
||||
* Fix typo in doc section for get_service_typesupport_handle (`#2752 <https://github.com/ros2/rclcpp/issues/2752>`_)
|
||||
* Backports: `#2751 <https://github.com/ros2/rclcpp/issues/2751>`_
|
||||
* Test case and fix for for https://github.com/ros2/rclcpp/issues/2652 (`#2740 <https://github.com/ros2/rclcpp/issues/2740>`_)
|
||||
* Backports: `#2713 <https://github.com/ros2/rclcpp/issues/2713>`_
|
||||
* fix(timer): Delete node, after executor thread terminated (`#2738 <https://github.com/ros2/rclcpp/issues/2738>`_)
|
||||
* Backports: `#2737 <https://github.com/ros2/rclcpp/issues/2737>`_
|
||||
* fix(Executor): Fixed entities not beeing executed after just beeing added (`#2729 <https://github.com/ros2/rclcpp/issues/2729>`_)
|
||||
* Backports: `#2737 <https://github.com/ros2/rclcpp/issues/2724>`_
|
||||
* Fix transient local IPC publish (`#2722 <https://github.com/ros2/rclcpp/issues/2722>`_)
|
||||
* Backports: `#2708 <https://github.com/ros2/rclcpp/issues/2708>`_
|
||||
* Contributors: Janosch Machowinski, Jeffery Hsu, Tomoya Fujita, Francisco Martín Rico
|
||||
|
||||
28.1.6 (2024-12-18)
|
||||
-------------------
|
||||
* apply actual QoS from rmw to the IPC publisher. (`#2707 <https://github.com/ros2/rclcpp/issues/2707>`_) (`#2712 <https://github.com/ros2/rclcpp/issues/2712>`_)
|
||||
* apply actual QoS from rmw to the IPC publisher.
|
||||
* address uncrustify warning.
|
||||
---------
|
||||
* Removed deprecated methods and classes (`#2575 <https://github.com/ros2/rclcpp/issues/2575>`_)
|
||||
* Release ownership of entities after spinning cancelled (`#2556 <https://github.com/ros2/rclcpp/issues/2556>`_)
|
||||
* Release ownership of entities after spinning cancelled
|
||||
* Move release action to every exit point in different spin functions
|
||||
* Move wait_result\_.reset() before setting spinning to false
|
||||
* Update test code
|
||||
* Move test code to test_executors.cpp
|
||||
(cherry picked from commit 016cfeac99e4b67f58abdf247e57f05b85c09ec4)
|
||||
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
|
||||
* Adding in topic name to logging on IPC issues (`#2706 <https://github.com/ros2/rclcpp/issues/2706>`_) (`#2710 <https://github.com/ros2/rclcpp/issues/2710>`_)
|
||||
* Adding in topic name to logging on IPC issues
|
||||
* Update test matching output logging
|
||||
* adding in single quotes
|
||||
---------
|
||||
* Split test_executors.cpp even further. (`#2572 <https://github.com/ros2/rclcpp/issues/2572>`_)
|
||||
(cherry picked from commit a13e16e2cbaeacb14ff31272d01cbb21bd8ac037)
|
||||
Co-authored-by: Steve Macenski <stevenmacenski@gmail.com>
|
||||
* enable testRaceConditionAddNode for rmw_connextdds. (`#2698 <https://github.com/ros2/rclcpp/issues/2698>`_)
|
||||
* Re-enable executor test on rmw_connextdds. (`#2693 <https://github.com/ros2/rclcpp/issues/2693>`_) (`#2695 <https://github.com/ros2/rclcpp/issues/2695>`_)
|
||||
It supports the events executor now, so re-enable the test.
|
||||
(cherry picked from commit d7245365ed867db9b309ed3efbfb0391bda09bd5)
|
||||
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
|
||||
* Fix warnings on Windows. (backport `#2692 <https://github.com/ros2/rclcpp/issues/2692>`_) (`#2694 <https://github.com/ros2/rclcpp/issues/2694>`_)
|
||||
* Fix warnings on Windows. (`#2692 <https://github.com/ros2/rclcpp/issues/2692>`_)
|
||||
For reasons I admit I do not understand, the deprecation
|
||||
warnings for StaticSingleThreadedExecutor on Windows
|
||||
happen when we construct a shared_ptr for it in the tests.
|
||||
If we construct a regular object, then it is fine. Luckily
|
||||
this test does not require a shared_ptr, so just make it
|
||||
a regular object here, which rixes the warning.
|
||||
While we are in here, make all of the tests camel case to
|
||||
be consistent.
|
||||
(cherry picked from commit 3310f9eaed967e0c18d17bb2f82d2def838bb7a5)
|
||||
# Conflicts:
|
||||
# rclcpp/test/rclcpp/executors/test_executors.cpp
|
||||
* resolve backport conflict.
|
||||
---------
|
||||
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
|
||||
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
|
||||
* Omnibus fixes for running tests with Connext. (backport `#2684 <https://github.com/ros2/rclcpp/issues/2684>`_) (`#2690 <https://github.com/ros2/rclcpp/issues/2690>`_)
|
||||
* Omnibus fixes for running tests with Connext. (`#2684 <https://github.com/ros2/rclcpp/issues/2684>`_)
|
||||
* Omnibus fixes for running tests with Connext.
|
||||
When running the tests with RTI Connext as the default
|
||||
RMW, some of the tests are failing. There are three
|
||||
different failures fixed here:
|
||||
1. Setting the liveliness duration to a value smaller than
|
||||
a microsecond causes Connext to throw an error. Set it to
|
||||
a millisecond.
|
||||
2. Using the SystemDefaultsQoS sets the QoS to KEEP_LAST 1.
|
||||
Connext is somewhat slow in this regard, so it can be the case
|
||||
that we are overwriting a previous service introspection event
|
||||
with the next one. Switch to the ServicesDefaultQoS in the test,
|
||||
which ensures we will not lose events.
|
||||
3. Connext is slow to match publishers and subscriptions. Thus,
|
||||
when creating a subscription "on-the-fly", we should wait for the
|
||||
publisher to match it before expecting the subscription to actually
|
||||
receive data from it.
|
||||
With these fixes in place, the test_client_common, test_generic_service,
|
||||
test_service_introspection, and test_executors tests all pass for
|
||||
me with rmw_connextdds.
|
||||
* Fixes for executors.
|
||||
* One more fix for services.
|
||||
* More fixes for service_introspection.
|
||||
* More fixes for introspection.
|
||||
---------
|
||||
(cherry picked from commit 9984197c292d6c5ae0e7661aaea245ffb0fea057)
|
||||
# Conflicts:
|
||||
# rclcpp/test/rclcpp/executors/test_executors.cpp
|
||||
# rclcpp/test/rclcpp/test_generic_service.cpp
|
||||
* address backport merge conflicts.
|
||||
---------
|
||||
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
|
||||
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
|
||||
* fix(Executor): Fix segfault if callback group is deleted during rmw_wait (`#2682 <https://github.com/ros2/rclcpp/issues/2682>`_)
|
||||
* Fix NodeOptions assignment operator (`#2656 <https://github.com/ros2/rclcpp/issues/2656>`_) (`#2660 <https://github.com/ros2/rclcpp/issues/2660>`_)
|
||||
* Fix NodeOptions assignment operator
|
||||
Also copy the enable_logger_service\_ member during the assignment operation
|
||||
* Add more checks for NodeOptions copy test
|
||||
* Set non default values by avoiding the copy-assignement
|
||||
Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com>
|
||||
(cherry picked from commit 9b654942f99f17850e0e95480958abdbb508bc00)
|
||||
Co-authored-by: Romain DESILLE <r.desille@gmail.com>
|
||||
* set QoS History KEEP_ALL explicitly for statistics publisher. (`#2650 <https://github.com/ros2/rclcpp/issues/2650>`_) (`#2657 <https://github.com/ros2/rclcpp/issues/2657>`_)
|
||||
* set QoS History KEEP_ALL explicitly for statistics publisher.
|
||||
* test_subscription_options adjustment.
|
||||
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
|
||||
* Contributors: Cristóbal Arroyo, Tomoya Fujita, jmachowinski, mergify[bot]
|
||||
|
||||
28.1.5 (2024-09-19)
|
||||
-------------------
|
||||
* backport fix events-executor warm-up bug and add unit-tests (`#2591 <https://github.com/ros2/rclcpp/issues/2591>`_) (`#2628 <https://github.com/ros2/rclcpp/issues/2628>`_)
|
||||
* Contributors: Alberto Soragna
|
||||
|
||||
28.1.4 (2024-09-06)
|
||||
-------------------
|
||||
* Split test_executors.cpp even further. (`#2572 <https://github.com/ros2/rclcpp/issues/2572>`_) (`#2619 <https://github.com/ros2/rclcpp/issues/2619>`_)
|
||||
That's because it is too large for Windows Debug to compile,
|
||||
so split into smaller bits.
|
||||
Even with this split, the file is too big; that's likely
|
||||
@@ -52,32 +216,70 @@ Changelog for package rclcpp
|
||||
symbols per test case. To deal with this, without further
|
||||
breaking up the file, also add in the /bigobj flag when
|
||||
compiling on Windows Debug.
|
||||
* avoid adding notify waitable twice to events-executor collection (`#2564 <https://github.com/ros2/rclcpp/issues/2564>`_)
|
||||
* avoid adding notify waitable twice to events-executor entities collection
|
||||
* remove redundant mutex lock
|
||||
(cherry picked from commit c743c173e68d92af872cf163e10721a8dbe51dd0)
|
||||
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
|
||||
* Correct node name in service test code (`#2615 <https://github.com/ros2/rclcpp/issues/2615>`_) (`#2616 <https://github.com/ros2/rclcpp/issues/2616>`_)
|
||||
(cherry picked from commit e846f56224a39b93f1c609e7ee03fff0662b7453)
|
||||
Co-authored-by: Barry Xu <barry.xu@sony.com>
|
||||
* Release ownership of entities after spinning cancelled (backport `#2556 <https://github.com/ros2/rclcpp/issues/2556>`_) (`#2580 <https://github.com/ros2/rclcpp/issues/2580>`_)
|
||||
* Release ownership of entities after spinning cancelled (`#2556 <https://github.com/ros2/rclcpp/issues/2556>`_)
|
||||
* Release ownership of entities after spinning cancelled
|
||||
* Move release action to every exit point in different spin functions
|
||||
* Move wait_result\_.reset() before setting spinning to false
|
||||
* Update test code
|
||||
* Move test code to test_executors.cpp
|
||||
---------
|
||||
* Contributors: Alberto Soragna, Alejandro Hernández Cordero, Barry Xu, Chris Lalancette
|
||||
(cherry picked from commit 069a0018935b33a14632a1cdf4074984a1cf80fe)
|
||||
# Conflicts:
|
||||
# rclcpp/test/rclcpp/executors/test_executors.cpp
|
||||
* Fix backport issue (`#2581 <https://github.com/ros2/rclcpp/issues/2581>`_)
|
||||
---------
|
||||
Co-authored-by: Barry Xu <barry.xu@sony.com>
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
28.3.1 (2024-06-25)
|
||||
28.1.3 (2024-06-27)
|
||||
-------------------
|
||||
* Remove unnecessary msg includes in tests (`#2566 <https://github.com/ros2/rclcpp/issues/2566>`_)
|
||||
* Fix copy-paste errors in function docs (`#2565 <https://github.com/ros2/rclcpp/issues/2565>`_)
|
||||
* Fix typo in function doc (`#2563 <https://github.com/ros2/rclcpp/issues/2563>`_)
|
||||
* Contributors: Christophe Bedard
|
||||
* Add test creating two content filter topics with the same topic name (`#2546 <https://github.com/ros2/rclcpp/issues/2546>`_) (`#2549 <https://github.com/ros2/rclcpp/issues/2549>`_) (`#2552 <https://github.com/ros2/rclcpp/issues/2552>`_)
|
||||
Co-authored-by: Mario Domínguez López <116071334+Mario-DL@users.noreply.github.com>
|
||||
(cherry picked from commit 7c096888caf92aa7557e1d3efc5448b56d8ce81c)
|
||||
Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
28.3.0 (2024-06-17)
|
||||
28.1.2 (2024-05-13)
|
||||
-------------------
|
||||
* Add test creating two content filter topics with the same topic name (`#2546 <https://github.com/ros2/rclcpp/issues/2546>`_) (`#2549 <https://github.com/ros2/rclcpp/issues/2549>`_)
|
||||
* add impl pointer for ExecutorOptions (`#2523 <https://github.com/ros2/rclcpp/issues/2523>`_)
|
||||
* Fixup Executor::spin_all() regression fix (`#2517 <https://github.com/ros2/rclcpp/issues/2517>`_)
|
||||
* Add 'mimick' label to tests which use Mimick (`#2516 <https://github.com/ros2/rclcpp/issues/2516>`_)
|
||||
* Contributors: Alejandro Hernández Cordero, Scott K Logan, William Woodall
|
||||
* add impl pointer for ExecutorOptions (`#2523 <https://github.com/ros2/rclcpp/issues/2523>`_) (`#2525 <https://github.com/ros2/rclcpp/issues/2525>`_)
|
||||
* add impl pointer for ExecutorOptions
|
||||
(cherry picked from commit 343b29b617b163ad72b9fe3f6441dd4ed3d3af09)
|
||||
Co-authored-by: William Woodall <william@osrfoundation.org>
|
||||
* Fixup Executor::spin_all() regression fix (`#2517 <https://github.com/ros2/rclcpp/issues/2517>`_) (`#2521 <https://github.com/ros2/rclcpp/issues/2521>`_)
|
||||
* test(Executors): Added tests for busy waiting
|
||||
Checks if executors are busy waiting while they should block
|
||||
in spin_some or spin_all.
|
||||
* fix: Reworked spinAll test
|
||||
This test was strange. It looked like, it assumed that spin_all did
|
||||
not return instantly. Also it was racy, as the thread could terminate
|
||||
instantly.
|
||||
* fix(Executor): Fixed spin_all not returning instantly is no work was available
|
||||
* Update rclcpp/test/rclcpp/executors/test_executors.cpp
|
||||
* test(executors): Added test for busy waiting while calling spin
|
||||
* fix(executor): Reset wait_result on every call to spin_some_impl
|
||||
Before, the method would not recollect available work in case of
|
||||
spin_some, spin_all. This would lead to the method behaving differently
|
||||
than to what the documentation states.
|
||||
* restore previous test logic for now
|
||||
* refactor spin_some_impl's logic and improve busy wait tests
|
||||
* added some more comments about the implementation
|
||||
---------
|
||||
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
|
||||
Co-authored-by: jmachowinski <jmachowinski@users.noreply.github.com>
|
||||
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
|
||||
Co-authored-by: William Woodall <william@osrfoundation.org>
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
28.2.0 (2024-04-26)
|
||||
28.1.1 (2024-04-24)
|
||||
-------------------
|
||||
* Check for negative time in rclcpp::Time(int64_t nanoseconds, ...) constructor (`#2510 <https://github.com/ros2/rclcpp/issues/2510>`_)
|
||||
* Revise the description of service configure_introspection() (`#2511 <https://github.com/ros2/rclcpp/issues/2511>`_)
|
||||
* Contributors: Barry Xu, Sharmin Ramli
|
||||
* Revise the description of service configure_introspection() (`#2511 <https://github.com/ros2/rclcpp/issues/2511>`_) (`#2513 <https://github.com/ros2/rclcpp/issues/2513>`_)
|
||||
* Contributors: mergify[bot]
|
||||
|
||||
28.1.0 (2024-04-16)
|
||||
-------------------
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
This document is a declaration of software quality for the `rclcpp` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
|
||||
This document is a declaration of software quality for the `rclcpp` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/).
|
||||
|
||||
# rclcpp Quality Declaration
|
||||
|
||||
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 Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html).
|
||||
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://reps.openrobotics.org/rep-2004/).
|
||||
|
||||
## Version Policy [1]
|
||||
|
||||
@@ -55,7 +55,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d
|
||||
|
||||
### Continuous Integration [2.iv]
|
||||
|
||||
All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers)
|
||||
All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers)
|
||||
|
||||
Currently nightly results can be seen here:
|
||||
|
||||
@@ -213,7 +213,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
|
||||
|
||||
## Platform Support [6]
|
||||
|
||||
`rclcpp` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them.
|
||||
`rclcpp` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them.
|
||||
|
||||
Currently nightly build status can be seen here:
|
||||
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp/)
|
||||
@@ -225,4 +225,4 @@ Currently nightly build status can be seen here:
|
||||
|
||||
### Vulnerability Disclosure Policy [7.i]
|
||||
|
||||
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html).
|
||||
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/).
|
||||
|
||||
@@ -59,6 +59,46 @@ class CallbackGroup
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(CallbackGroup)
|
||||
|
||||
/// Constructor for CallbackGroup.
|
||||
/**
|
||||
* Callback Groups have a type, either 'Mutually Exclusive' or 'Reentrant'
|
||||
* and when creating one the type must be specified.
|
||||
*
|
||||
* Callbacks in Reentrant Callback Groups must be able to:
|
||||
* - run at the same time as themselves (reentrant)
|
||||
* - run at the same time as other callbacks in their group
|
||||
* - run at the same time as other callbacks in other groups
|
||||
*
|
||||
* Callbacks in Mutually Exclusive Callback Groups:
|
||||
* - will not be run multiple times simultaneously (non-reentrant)
|
||||
* - will not be run at the same time as other callbacks in their group
|
||||
* - but must run at the same time as callbacks in other groups
|
||||
*
|
||||
* Additionally, callback groups have a property which determines whether or
|
||||
* not they are added to an executor with their associated node automatically.
|
||||
* When creating a callback group the automatically_add_to_executor_with_node
|
||||
* argument determines this behavior, and if true it will cause the newly
|
||||
* created callback group to be added to an executor with the node when the
|
||||
* Executor::add_node method is used.
|
||||
* If false, this callback group will not be added automatically and would
|
||||
* have to be added to an executor manually using the
|
||||
* Executor::add_callback_group method.
|
||||
*
|
||||
* Whether the node was added to the executor before creating the callback
|
||||
* group, or after, is irrelevant; the callback group will be automatically
|
||||
* added to the executor in either case.
|
||||
*
|
||||
* \param[in] group_type The type of the callback group.
|
||||
* \param[in] automatically_add_to_executor_with_node A boolean that
|
||||
* determines whether a callback group is automatically added to an executor
|
||||
* with the node with which it is associated.
|
||||
*/
|
||||
[[deprecated("Use CallbackGroup constructor with context function argument")]]
|
||||
RCLCPP_PUBLIC
|
||||
explicit CallbackGroup(
|
||||
CallbackGroupType group_type,
|
||||
bool automatically_add_to_executor_with_node = true);
|
||||
|
||||
/// Constructor for CallbackGroup.
|
||||
/**
|
||||
* Callback Groups have a type, either 'Mutually Exclusive' or 'Reentrant'
|
||||
|
||||
@@ -70,6 +70,14 @@ struct FutureAndRequestId
|
||||
/// Allow implicit conversions to `std::future` by reference.
|
||||
operator FutureT &() {return this->future;}
|
||||
|
||||
/// Deprecated, use the `future` member variable instead.
|
||||
/**
|
||||
* Allow implicit conversions to `std::future` by value.
|
||||
* \deprecated
|
||||
*/
|
||||
[[deprecated("FutureAndRequestId: use .future instead of an implicit conversion")]]
|
||||
operator FutureT() {return this->future;}
|
||||
|
||||
// delegate future like methods in the std::future impl_
|
||||
|
||||
/// See std::future::get().
|
||||
@@ -428,6 +436,15 @@ public:
|
||||
{
|
||||
using detail::FutureAndRequestId<std::future<SharedResponse>>::FutureAndRequestId;
|
||||
|
||||
/// Deprecated, use `.future.share()` instead.
|
||||
/**
|
||||
* Allow implicit conversions to `std::shared_future` by value.
|
||||
* \deprecated
|
||||
*/
|
||||
[[deprecated(
|
||||
"FutureAndRequestId: use .future.share() instead of an implicit conversion")]]
|
||||
operator SharedFuture() {return this->future.share();}
|
||||
|
||||
// delegate future like methods in the std::future impl_
|
||||
|
||||
/// See std::future::share().
|
||||
@@ -473,7 +490,7 @@ public:
|
||||
* \param[in] node_base NodeBaseInterface pointer that is used in part of the setup.
|
||||
* \param[in] node_graph The node graph interface of the corresponding node.
|
||||
* \param[in] service_name Name of the topic to publish to.
|
||||
* \param[in] client_options options for the client.
|
||||
* \param[in] client_options options for the subscription.
|
||||
*/
|
||||
Client(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
|
||||
@@ -60,6 +60,13 @@ public:
|
||||
/**
|
||||
* Initializes the clock instance with the given clock_type.
|
||||
*
|
||||
* WARNING Don't instantiate a clock using RCL_ROS_TIME directly,
|
||||
* unless you really know what you are doing. By default no TimeSource
|
||||
* is attached to a new clock. This will lead to the unexpected behavior,
|
||||
* that your RCL_ROS_TIME will run always on system time. If you want
|
||||
* a RCL_ROS_TIME use Node::get_clock(), or make sure to attach a
|
||||
* TimeSource yourself.
|
||||
*
|
||||
* \param clock_type type of the clock.
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
@@ -217,13 +224,13 @@ public:
|
||||
std::mutex &
|
||||
get_clock_mutex() noexcept;
|
||||
|
||||
/// Add a callback to invoke if the jump threshold is exceeded.
|
||||
// Add a callback to invoke if the jump threshold is exceeded.
|
||||
/**
|
||||
* These callback functions must remain valid as long as the
|
||||
* returned shared pointer is valid.
|
||||
*
|
||||
* Function will register callbacks to the callback queue. On time jump all
|
||||
* callbacks will be executed whose threshold is greater than the time jump;
|
||||
* callbacks will be executed whose threshold is greater then the time jump;
|
||||
* The logic will first call selected pre_callbacks and then all selected
|
||||
* post_callbacks.
|
||||
*
|
||||
@@ -232,7 +239,7 @@ public:
|
||||
* \param pre_callback Must be non-throwing
|
||||
* \param post_callback Must be non-throwing.
|
||||
* \param threshold Callbacks will be triggered if the time jump is greater
|
||||
* than the threshold.
|
||||
* then the threshold.
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
* \throws std::bad_alloc if the allocation of the JumpHandler fails.
|
||||
* \warning the instance of the clock must remain valid as long as any created
|
||||
@@ -260,6 +267,117 @@ private:
|
||||
std::shared_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
/**
|
||||
* A synchronization primitive, equal to std::conditional_variable,
|
||||
* that works with the rclcpp::Clock.
|
||||
*
|
||||
* For more information on the API see https://en.cppreference.com/w/cpp/thread/condition_variable.
|
||||
*
|
||||
* Note, this class does not handle shutdowns, if you want to
|
||||
* haven them handles as well, use ClockConditionalVariable.
|
||||
*/
|
||||
class ClockWaiter
|
||||
{
|
||||
private:
|
||||
class ClockWaiterImpl;
|
||||
std::unique_ptr<ClockWaiterImpl> impl_;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ClockWaiter)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit ClockWaiter(const rclcpp::Clock::SharedPtr & clock);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
~ClockWaiter();
|
||||
|
||||
/**
|
||||
* Calling this function will block the current thread, until abs_time is reached,
|
||||
* or pred returns true.
|
||||
* @param lock A locked lock. The lock must be locked at call time, or this method will throw.
|
||||
* The lock will be atomically released and this thread will blocked.
|
||||
* @param abs_time The time until which this thread shall be blocked.
|
||||
* @param pred may be called in cased of spurious wakeups, but must be called every time
|
||||
* notify_one() was called. During the call to pred, the given lock will be locked.
|
||||
* This method will return, if pred returns true.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
wait_until(
|
||||
std::unique_lock<std::mutex> & lock,
|
||||
const rclcpp::Time & abs_time, const std::function<bool ()> & pred);
|
||||
|
||||
/**
|
||||
* Notify the blocked thread, that it should reevaluate the wakeup condition.
|
||||
* The given pred function in wait_until will be reevaluated and wait_until
|
||||
* will return if it evaluates to true.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
notify_one();
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A synchronization primitive, similar to std::conditional_variable,
|
||||
* that works with the rclcpp::Clock.
|
||||
*
|
||||
* For more information on the API see https://en.cppreference.com/w/cpp/thread/condition_variable.
|
||||
*
|
||||
* This primitive will wake up if the context was shut down.
|
||||
*/
|
||||
class ClockConditionalVariable
|
||||
{
|
||||
class Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ClockConditionalVariable)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
ClockConditionalVariable(
|
||||
const rclcpp::Clock::SharedPtr & clock,
|
||||
rclcpp::Context::SharedPtr context = rclcpp::contexts::get_global_default_context());
|
||||
RCLCPP_PUBLIC
|
||||
~ClockConditionalVariable();
|
||||
|
||||
/**
|
||||
* Calling this function will block the current thread, until abs_time is reached,
|
||||
* or pred returns true.
|
||||
* @param lock A locked lock. The lock must be locked at call time, or this method will throw.
|
||||
* The lock will be atomically released and this thread will blocked.
|
||||
* The given lock must be created using the mutex returned my mutex().
|
||||
* @param abs_time The time until which this thread shall be blocked.
|
||||
* @param pred may be called in cased of spurious wakeups, but must be called every time
|
||||
* notify_one() was called. During the call to pred, the given lock will be locked.
|
||||
* This method will return, if pred returns true.
|
||||
*
|
||||
* @return true if until was reached.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
wait_until(
|
||||
std::unique_lock<std::mutex> & lock, rclcpp::Time until,
|
||||
const std::function<bool ()> & pred);
|
||||
|
||||
/**
|
||||
* Notify the blocked thread, that is should reevaluate the wakeup condition.
|
||||
* E.g. the given pred function in wait_until shall be reevaluated.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
notify_one();
|
||||
|
||||
/**
|
||||
* Returns the internal mutex. In order to be race free with the context shutdown,
|
||||
* this mutex must be used for the wait_until call.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::mutex &
|
||||
mutex();
|
||||
};
|
||||
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CLOCK_HPP_
|
||||
|
||||
@@ -47,9 +47,28 @@ create_client(
|
||||
const std::string & service_name,
|
||||
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr)
|
||||
{
|
||||
return create_client<ServiceT>(
|
||||
node_base, node_graph, node_services,
|
||||
service_name,
|
||||
qos.get_rmw_qos_profile(),
|
||||
group);
|
||||
}
|
||||
|
||||
/// Create a service client with a given type.
|
||||
/// \internal
|
||||
template<typename ServiceT>
|
||||
typename rclcpp::Client<ServiceT>::SharedPtr
|
||||
create_client(
|
||||
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
|
||||
std::shared_ptr<node_interfaces::NodeGraphInterface> node_graph,
|
||||
std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
|
||||
const std::string & service_name,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
rcl_client_options_t options = rcl_client_get_default_options();
|
||||
options.qos = qos.get_rmw_qos_profile();
|
||||
options.qos = qos_profile;
|
||||
|
||||
auto cli = rclcpp::Client<ServiceT>::make_shared(
|
||||
node_base.get(),
|
||||
@@ -61,6 +80,7 @@ create_client(
|
||||
node_services->add_client(cli_base_ptr, group);
|
||||
return cli;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CREATE_CLIENT_HPP_
|
||||
|
||||
@@ -233,6 +233,8 @@ protected:
|
||||
size_t wait_set_event_index_;
|
||||
};
|
||||
|
||||
using QOSEventHandlerBase [[deprecated("Use rclcpp::EventHandlerBase")]] = EventHandlerBase;
|
||||
|
||||
template<typename EventCallbackT, typename ParentHandleT>
|
||||
class EventHandler : public EventHandlerBase
|
||||
{
|
||||
@@ -309,6 +311,11 @@ private:
|
||||
ParentHandleT parent_handle_;
|
||||
EventCallbackT event_callback_;
|
||||
};
|
||||
|
||||
template<typename EventCallbackT, typename ParentHandleT>
|
||||
using QOSEventHandler [[deprecated("Use rclcpp::EventHandler")]] = EventHandler<EventCallbackT,
|
||||
ParentHandleT>;
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EVENT_HANDLER_HPP_
|
||||
|
||||
@@ -541,9 +541,8 @@ protected:
|
||||
*
|
||||
* \param[in] notify if true will execute a trigger that will wake up a waiting executor
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
handle_updated_entities(bool notify);
|
||||
void
|
||||
trigger_entity_recollect(bool notify);
|
||||
|
||||
/// Spinning state, used to prevent multi threaded calls to spin and to cancel blocking spins.
|
||||
std::atomic_bool spinning;
|
||||
|
||||
@@ -122,14 +122,6 @@ public:
|
||||
void
|
||||
clear_on_ready_callback() override;
|
||||
|
||||
/// Set a new callback to be called whenever this waitable is executed.
|
||||
/**
|
||||
* \param[in] on_execute_callback The new callback
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_execute_callback(std::function<void(void)> on_execute_callback);
|
||||
|
||||
/// Remove a guard condition from being waited on.
|
||||
/**
|
||||
* \param[in] weak_guard_condition The guard condition to remove.
|
||||
@@ -150,10 +142,7 @@ private:
|
||||
/// Callback to run when waitable executes
|
||||
std::function<void(void)> execute_callback_;
|
||||
|
||||
/// Mutex to procetect the guard conditions
|
||||
std::mutex guard_condition_mutex_;
|
||||
/// Mutex to protect the execute callback
|
||||
std::mutex execute_mutex_;
|
||||
|
||||
std::function<void(size_t)> on_ready_callback_;
|
||||
|
||||
|
||||
@@ -255,9 +255,13 @@ private:
|
||||
std::vector<BufferT> result_vtr;
|
||||
result_vtr.reserve(size_);
|
||||
for (size_t id = 0; id < size_; ++id) {
|
||||
result_vtr.emplace_back(
|
||||
new typename is_std_unique_ptr<T>::Ptr_type(
|
||||
*(ring_buffer_[(read_index_ + id) % capacity_])));
|
||||
const auto & elem(ring_buffer_[(read_index_ + id) % capacity_]);
|
||||
if (elem != nullptr) {
|
||||
result_vtr.emplace_back(new typename is_std_unique_ptr<T>::Ptr_type(
|
||||
*elem));
|
||||
} else {
|
||||
result_vtr.emplace_back(nullptr);
|
||||
}
|
||||
}
|
||||
return result_vtr;
|
||||
}
|
||||
|
||||
@@ -59,6 +59,8 @@ namespace executors
|
||||
*/
|
||||
class EventsExecutor : public rclcpp::Executor
|
||||
{
|
||||
friend class EventsExecutorEntitiesCollector;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(EventsExecutor)
|
||||
|
||||
@@ -70,7 +72,7 @@ public:
|
||||
* \param[in] options Options used to configure the executor.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
EventsExecutor(
|
||||
explicit EventsExecutor(
|
||||
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue = std::make_unique<
|
||||
rclcpp::experimental::executors::SimpleEventsQueue>(),
|
||||
bool execute_timers_separate_thread = false,
|
||||
@@ -126,6 +128,87 @@ public:
|
||||
void
|
||||
spin_all(std::chrono::nanoseconds max_duration) override;
|
||||
|
||||
/// Add a node to the executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::add_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
bool notify = true) override;
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
/**
|
||||
* \sa rclcpp::EventsExecutor::add_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
|
||||
|
||||
/// Remove a node from the executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::remove_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
bool notify = true) override;
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
/**
|
||||
* \sa rclcpp::Executor::remove_node
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
|
||||
|
||||
/// Add a callback group to an executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::add_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
bool notify = true) override;
|
||||
|
||||
/// Remove callback group from the executor
|
||||
/**
|
||||
* \sa rclcpp::Executor::remove_callback_group
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
bool notify = true) override;
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::get_all_callback_groups()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_all_callback_groups() override;
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::get_manually_added_callback_groups()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_manually_added_callback_groups() override;
|
||||
|
||||
/// Get callback groups that belong to executor.
|
||||
/**
|
||||
* \sa rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
get_automatically_added_callback_groups_from_nodes() override;
|
||||
|
||||
protected:
|
||||
/// Internal implementation of spin_once
|
||||
RCLCPP_PUBLIC
|
||||
@@ -137,11 +220,6 @@ protected:
|
||||
void
|
||||
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
|
||||
|
||||
/// Collect entities from callback groups and refresh the current collection with them
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
handle_updated_entities(bool notify) override;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(EventsExecutor)
|
||||
|
||||
@@ -149,9 +227,9 @@ private:
|
||||
void
|
||||
execute_event(const ExecutorEvent & event);
|
||||
|
||||
/// Rebuilds the executor's notify waitable, as we can't use the one built in the base class
|
||||
/// Collect entities from callback groups and refresh the current collection with them
|
||||
void
|
||||
setup_notify_waitable();
|
||||
refresh_current_collection_from_callback_groups();
|
||||
|
||||
/// Refresh the current collection using the provided new_collection
|
||||
void
|
||||
@@ -175,11 +253,6 @@ private:
|
||||
typename CollectionType::EntitySharedPtr
|
||||
retrieve_entity(typename CollectionType::Key entity_id, CollectionType & collection)
|
||||
{
|
||||
// Note: we lock the mutex because we assume that you are trying to get an element from the
|
||||
// current collection... If there will be a use-case to retrieve elements also from other
|
||||
// collections, we can move the mutex back to the calling codes.
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
|
||||
// Check if the entity_id is in the collection
|
||||
auto it = collection.find(entity_id);
|
||||
if (it == collection.end()) {
|
||||
@@ -200,6 +273,16 @@ private:
|
||||
/// Queue where entities can push events
|
||||
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue_;
|
||||
|
||||
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollector> entities_collector_;
|
||||
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> notify_waitable_;
|
||||
|
||||
/// Mutex to protect the current_entities_collection_
|
||||
std::recursive_mutex collection_mutex_;
|
||||
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollection> current_entities_collection_;
|
||||
|
||||
/// Flag used to reduce the number of unnecessary waitable events
|
||||
std::atomic<bool> notify_waitable_event_pushed_ {false};
|
||||
|
||||
/// Timers manager used to track and/or execute associated timers
|
||||
std::shared_ptr<rclcpp::experimental::TimersManager> timers_manager_;
|
||||
};
|
||||
|
||||
@@ -82,6 +82,36 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
/// Constructor of the LoanedMessage class.
|
||||
/**
|
||||
* The constructor of this class allocates memory for a given message type
|
||||
* and associates this with a given publisher.
|
||||
*
|
||||
* Given the publisher instance, a case differentiation is being performaned
|
||||
* which decides whether the underlying middleware is able to allocate the appropriate
|
||||
* memory for this message type or not.
|
||||
* In the case that the middleware can not loan messages, the passed in allocator instance
|
||||
* is being used to allocate the message within the scope of this class.
|
||||
* Otherwise, the allocator is being ignored and the allocation is solely performaned
|
||||
* in the underlying middleware with its appropriate allocation strategy.
|
||||
* The need for this arises as the user code can be written explicitly targeting a middleware
|
||||
* capable of loaning messages.
|
||||
* However, this user code is ought to be usable even when dynamically linked against
|
||||
* a middleware which doesn't support message loaning in which case the allocator will be used.
|
||||
*
|
||||
* \param[in] pub rclcpp::Publisher instance to which the memory belongs
|
||||
* \param[in] allocator Allocator instance in case middleware can not allocate messages
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
[[
|
||||
deprecated("used the LoanedMessage constructor that does not use a shared_ptr to the allocator")
|
||||
]]
|
||||
LoanedMessage(
|
||||
const rclcpp::PublisherBase * pub,
|
||||
std::shared_ptr<std::allocator<MessageT>> allocator)
|
||||
: LoanedMessage(*pub, *allocator)
|
||||
{}
|
||||
|
||||
/// Move semantic for RVO
|
||||
LoanedMessage(LoanedMessage<MessageT> && other)
|
||||
: pub_(std::move(other.pub_)),
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#ifndef RCLCPP__LOGGER_HPP_
|
||||
#define RCLCPP__LOGGER_HPP_
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -78,14 +77,6 @@ RCLCPP_PUBLIC
|
||||
Logger
|
||||
get_node_logger(const rcl_node_t * node);
|
||||
|
||||
// TODO(ahcorde): Remove deprecated class on the next release (in Rolling after Kilted).
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
/// Get the current logging directory.
|
||||
/**
|
||||
* For more details of how the logging directory is determined,
|
||||
@@ -94,30 +85,10 @@ get_node_logger(const rcl_node_t * node);
|
||||
* \returns the logging directory being used.
|
||||
* \throws rclcpp::exceptions::RCLError if an unexpected error occurs.
|
||||
*/
|
||||
[[deprecated("use rclcpp::get_log_directory instead of rclcpp::get_logging_directory")]]
|
||||
RCLCPP_PUBLIC
|
||||
rcpputils::fs::path
|
||||
get_logging_directory();
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
/// Get the current logging directory.
|
||||
/**
|
||||
* For more details of how the logging directory is determined,
|
||||
* see rcl_logging_get_logging_directory().
|
||||
*
|
||||
* \returns the logging directory being used.
|
||||
* \throws rclcpp::exceptions::RCLError if an unexpected error occurs.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::filesystem::path
|
||||
get_log_directory();
|
||||
|
||||
class Logger
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -257,6 +257,22 @@ public:
|
||||
CallbackT callback,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Create and return a Client.
|
||||
/**
|
||||
* \param[in] service_name The topic to service on.
|
||||
* \param[in] qos_profile rmw_qos_profile_t Quality of service profile for client.
|
||||
* \param[in] group Callback group to call the service.
|
||||
* \return Shared pointer to the created client.
|
||||
* \deprecated use rclcpp::QoS instead of rmw_qos_profile_t
|
||||
*/
|
||||
template<typename ServiceT>
|
||||
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
|
||||
typename rclcpp::Client<ServiceT>::SharedPtr
|
||||
create_client(
|
||||
const std::string & service_name,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Create and return a Client.
|
||||
/**
|
||||
* \param[in] service_name The name on which the service is accessible.
|
||||
@@ -271,6 +287,24 @@ public:
|
||||
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Create and return a Service.
|
||||
/**
|
||||
* \param[in] service_name The topic to service on.
|
||||
* \param[in] callback User-defined callback function.
|
||||
* \param[in] qos_profile rmw_qos_profile_t Quality of service profile for client.
|
||||
* \param[in] group Callback group to call the service.
|
||||
* \return Shared pointer to the created service.
|
||||
* \deprecated use rclcpp::QoS instead of rmw_qos_profile_t
|
||||
*/
|
||||
template<typename ServiceT, typename CallbackT>
|
||||
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
|
||||
typename rclcpp::Service<ServiceT>::SharedPtr
|
||||
create_service(
|
||||
const std::string & service_name,
|
||||
CallbackT && callback,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Create and return a Service.
|
||||
/**
|
||||
* \param[in] service_name The topic to service on.
|
||||
@@ -981,6 +1015,8 @@ public:
|
||||
rclcpp::node_interfaces::OnSetParametersCallbackHandle;
|
||||
using OnSetParametersCallbackType =
|
||||
rclcpp::node_interfaces::NodeParametersInterface::OnSetParametersCallbackType;
|
||||
using OnParametersSetCallbackType [[deprecated("use OnSetParametersCallbackType instead")]] =
|
||||
OnSetParametersCallbackType;
|
||||
|
||||
using PostSetParametersCallbackHandle =
|
||||
rclcpp::node_interfaces::PostSetParametersCallbackHandle;
|
||||
|
||||
@@ -154,6 +154,22 @@ Node::create_client(
|
||||
group);
|
||||
}
|
||||
|
||||
template<typename ServiceT>
|
||||
typename Client<ServiceT>::SharedPtr
|
||||
Node::create_client(
|
||||
const std::string & service_name,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
return rclcpp::create_client<ServiceT>(
|
||||
node_base_,
|
||||
node_graph_,
|
||||
node_services_,
|
||||
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
|
||||
qos_profile,
|
||||
group);
|
||||
}
|
||||
|
||||
template<typename ServiceT, typename CallbackT>
|
||||
typename rclcpp::Service<ServiceT>::SharedPtr
|
||||
Node::create_service(
|
||||
@@ -171,6 +187,23 @@ Node::create_service(
|
||||
group);
|
||||
}
|
||||
|
||||
template<typename ServiceT, typename CallbackT>
|
||||
typename rclcpp::Service<ServiceT>::SharedPtr
|
||||
Node::create_service(
|
||||
const std::string & service_name,
|
||||
CallbackT && callback,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
return rclcpp::create_service<ServiceT, CallbackT>(
|
||||
node_base_,
|
||||
node_services_,
|
||||
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
|
||||
std::forward<CallbackT>(callback),
|
||||
qos_profile,
|
||||
group);
|
||||
}
|
||||
|
||||
template<typename AllocatorT>
|
||||
std::shared_ptr<rclcpp::GenericPublisher>
|
||||
Node::create_generic_publisher(
|
||||
|
||||
@@ -199,6 +199,12 @@ init_tuple(NodeT & n)
|
||||
{ \
|
||||
return StorageClassT::template get<NodeInterfaceType>(); \
|
||||
} \
|
||||
\
|
||||
std::shared_ptr<const NodeInterfaceType> \
|
||||
get_node_ ## NodeInterfaceName ## _interface() const \
|
||||
{ \
|
||||
return StorageClassT::template get<NodeInterfaceType>(); \
|
||||
} \
|
||||
}; \
|
||||
} // namespace rclcpp::node_interfaces::detail
|
||||
// *INDENT-ON*
|
||||
|
||||
@@ -52,6 +52,8 @@ struct OnSetParametersCallbackHandle
|
||||
std::function<
|
||||
rcl_interfaces::msg::SetParametersResult(
|
||||
const std::vector<rclcpp::Parameter> &)>;
|
||||
using OnParametersSetCallbackType [[deprecated("use OnSetParametersCallbackType instead")]] =
|
||||
OnSetParametersCallbackType;
|
||||
|
||||
OnSetParametersCallbackType callback;
|
||||
};
|
||||
|
||||
@@ -146,6 +146,14 @@ public:
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Append a single parameter override, parameter idiom style.
|
||||
NodeOptions &
|
||||
append_parameter_override(const rclcpp::Parameter & param)
|
||||
{
|
||||
this->parameter_overrides().push_back(param);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Return the use_global_arguments flag.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
|
||||
@@ -52,6 +52,37 @@ class AsyncParametersClient
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(AsyncParametersClient)
|
||||
|
||||
/// Create an async parameters client.
|
||||
/**
|
||||
* \param[in] node_base_interface The node base interface of the corresponding node.
|
||||
* \param[in] node_topics_interface Node topic base interface.
|
||||
* \param[in] node_graph_interface The node graph interface of the corresponding node.
|
||||
* \param[in] node_services_interface Node service interface.
|
||||
* \param[in] remote_node_name Name of the remote node
|
||||
* \param[in] qos_profile The rmw qos profile to use to subscribe
|
||||
* \param[in] group (optional) The async parameter client will be added to this callback group.
|
||||
* \deprecated use rclcpp::QoS instead of rmw_qos_profile_t
|
||||
*/
|
||||
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
|
||||
RCLCPP_PUBLIC
|
||||
AsyncParametersClient(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
|
||||
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
|
||||
const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
|
||||
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr)
|
||||
: AsyncParametersClient(
|
||||
node_base_interface,
|
||||
node_topics_interface,
|
||||
node_graph_interface,
|
||||
node_services_interface,
|
||||
remote_node_name,
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)),
|
||||
group)
|
||||
{}
|
||||
|
||||
/// Create an async parameters client.
|
||||
/**
|
||||
* \param[in] node_base_interface The node base interface of the corresponding node.
|
||||
@@ -72,6 +103,31 @@ public:
|
||||
const rclcpp::QoS & qos_profile = rclcpp::ParametersQoS(),
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Constructor
|
||||
/**
|
||||
* \param[in] node The async parameters client will be added to this node.
|
||||
* \param[in] remote_node_name name of the remote node
|
||||
* \param[in] qos_profile The rmw qos profile to use to subscribe
|
||||
* \param[in] group (optional) The async parameter client will be added to this callback group.
|
||||
* \deprecated use rclcpp::QoS instead of rmw_qos_profile_t
|
||||
*/
|
||||
template<typename NodeT>
|
||||
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
|
||||
AsyncParametersClient(
|
||||
const std::shared_ptr<NodeT> node,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr)
|
||||
: AsyncParametersClient(
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
remote_node_name,
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)),
|
||||
group)
|
||||
{}
|
||||
|
||||
/**
|
||||
* \param[in] node The async parameters client will be added to this node.
|
||||
* \param[in] remote_node_name (optional) name of the remote node
|
||||
@@ -94,6 +150,31 @@ public:
|
||||
group)
|
||||
{}
|
||||
|
||||
/// Constructor
|
||||
/**
|
||||
* \param[in] node The async parameters client will be added to this node.
|
||||
* \param[in] remote_node_name Name of the remote node
|
||||
* \param[in] qos_profile The rmw qos profile to use to subscribe
|
||||
* \param[in] group (optional) The async parameter client will be added to this callback group.
|
||||
* \deprecated use rclcpp::QoS instead of rmw_qos_profile_t
|
||||
*/
|
||||
template<typename NodeT>
|
||||
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
|
||||
AsyncParametersClient(
|
||||
NodeT * node,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr)
|
||||
: AsyncParametersClient(
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
remote_node_name,
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)),
|
||||
group)
|
||||
{}
|
||||
|
||||
/**
|
||||
* \param[in] node The async parameters client will be added to this node.
|
||||
* \param[in] remote_node_name (optional) name of the remote node
|
||||
@@ -302,6 +383,19 @@ class SyncParametersClient
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient)
|
||||
|
||||
template<typename NodeT>
|
||||
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
|
||||
SyncParametersClient(
|
||||
std::shared_ptr<NodeT> node,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: SyncParametersClient(
|
||||
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(),
|
||||
node,
|
||||
remote_node_name,
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)))
|
||||
{}
|
||||
|
||||
template<typename NodeT>
|
||||
explicit SyncParametersClient(
|
||||
std::shared_ptr<NodeT> node,
|
||||
@@ -314,6 +408,23 @@ public:
|
||||
qos_profile)
|
||||
{}
|
||||
|
||||
template<typename NodeT>
|
||||
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
|
||||
SyncParametersClient(
|
||||
rclcpp::Executor::SharedPtr executor,
|
||||
std::shared_ptr<NodeT> node,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: SyncParametersClient(
|
||||
executor,
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
remote_node_name,
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)))
|
||||
{}
|
||||
|
||||
template<typename NodeT>
|
||||
SyncParametersClient(
|
||||
rclcpp::Executor::SharedPtr executor,
|
||||
@@ -330,6 +441,19 @@ public:
|
||||
qos_profile)
|
||||
{}
|
||||
|
||||
template<typename NodeT>
|
||||
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
|
||||
SyncParametersClient(
|
||||
NodeT * node,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: SyncParametersClient(
|
||||
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(),
|
||||
node,
|
||||
remote_node_name,
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)))
|
||||
{}
|
||||
|
||||
template<typename NodeT>
|
||||
explicit SyncParametersClient(
|
||||
NodeT * node,
|
||||
@@ -342,6 +466,23 @@ public:
|
||||
qos_profile)
|
||||
{}
|
||||
|
||||
template<typename NodeT>
|
||||
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
|
||||
SyncParametersClient(
|
||||
rclcpp::Executor::SharedPtr executor,
|
||||
NodeT * node,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: SyncParametersClient(
|
||||
executor,
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
remote_node_name,
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)))
|
||||
{}
|
||||
|
||||
template<typename NodeT>
|
||||
SyncParametersClient(
|
||||
rclcpp::Executor::SharedPtr executor,
|
||||
@@ -358,6 +499,28 @@ public:
|
||||
qos_profile)
|
||||
{}
|
||||
|
||||
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
|
||||
RCLCPP_PUBLIC
|
||||
SyncParametersClient(
|
||||
rclcpp::Executor::SharedPtr executor,
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
|
||||
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
|
||||
const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
|
||||
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: executor_(executor), node_base_interface_(node_base_interface)
|
||||
{
|
||||
async_parameters_client_ =
|
||||
std::make_shared<AsyncParametersClient>(
|
||||
node_base_interface,
|
||||
node_topics_interface,
|
||||
node_graph_interface,
|
||||
node_services_interface,
|
||||
remote_node_name,
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)));
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
SyncParametersClient(
|
||||
rclcpp::Executor::SharedPtr executor,
|
||||
|
||||
@@ -45,6 +45,7 @@ public:
|
||||
* \param[in] names A list of parameter names of interest.
|
||||
* \param[in] types A list of the types of parameter events of iterest.
|
||||
* EventType NEW, DELETED, or CHANGED
|
||||
* \throws std::invalid_argument if event is NULL.
|
||||
*
|
||||
* Example Usage:
|
||||
*
|
||||
|
||||
@@ -40,6 +40,20 @@ class ParameterService
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ParameterService)
|
||||
|
||||
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
|
||||
RCLCPP_PUBLIC
|
||||
ParameterService(
|
||||
const std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
|
||||
const std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
|
||||
rclcpp::node_interfaces::NodeParametersInterface * node_params,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: ParameterService(
|
||||
node_base,
|
||||
node_services,
|
||||
node_params,
|
||||
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)))
|
||||
{}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
ParameterService(
|
||||
const std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
|
||||
|
||||
@@ -96,6 +96,22 @@ public:
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
|
||||
|
||||
using MessageAllocatorTraits
|
||||
[[deprecated("use PublishedTypeAllocatorTraits")]] =
|
||||
PublishedTypeAllocatorTraits;
|
||||
using MessageAllocator
|
||||
[[deprecated("use PublishedTypeAllocator")]] =
|
||||
PublishedTypeAllocator;
|
||||
using MessageDeleter
|
||||
[[deprecated("use PublishedTypeDeleter")]] =
|
||||
PublishedTypeDeleter;
|
||||
using MessageUniquePtr
|
||||
[[deprecated("use std::unique_ptr<PublishedType, PublishedTypeDeleter>")]] =
|
||||
std::unique_ptr<PublishedType, PublishedTypeDeleter>;
|
||||
using MessageSharedPtr
|
||||
[[deprecated("use std::shared_ptr<const PublishedType>")]] =
|
||||
std::shared_ptr<const PublishedType>;
|
||||
|
||||
using BufferSharedPtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
|
||||
ROSMessageType,
|
||||
ROSMessageTypeAllocator,
|
||||
@@ -112,8 +128,8 @@ public:
|
||||
*
|
||||
* \param[in] node_base NodeBaseInterface pointer that is used in part of the setup.
|
||||
* \param[in] topic Name of the topic to publish to.
|
||||
* \param[in] qos QoS profile for the publisher.
|
||||
* \param[in] options Options for the publisher.
|
||||
* \param[in] qos QoS profile for Subcription.
|
||||
* \param[in] options Options for the subscription.
|
||||
*/
|
||||
Publisher(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
@@ -146,8 +162,7 @@ public:
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
|
||||
{
|
||||
// Topic is unused for now.
|
||||
(void)topic;
|
||||
(void)qos;
|
||||
(void)options;
|
||||
|
||||
// If needed, setup intra process communication.
|
||||
@@ -155,22 +170,26 @@ public:
|
||||
auto context = node_base->get_context();
|
||||
// Get the intra process manager instance for this context.
|
||||
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
|
||||
// Register the publisher with the intra process manager.
|
||||
if (qos.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
// Check if the QoS is compatible with intra-process.
|
||||
auto qos_profile = get_actual_qos();
|
||||
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
"intraprocess communication on topic '" + topic +
|
||||
"' allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos.depth() == 0) {
|
||||
if (qos_profile.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with a zero qos history depth value");
|
||||
"intraprocess communication on topic '" + topic +
|
||||
"' is not allowed with a zero qos history depth value");
|
||||
}
|
||||
if (qos.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
|
||||
if (qos_profile.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
|
||||
buffer_ = rclcpp::experimental::create_intra_process_buffer<
|
||||
ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter>(
|
||||
rclcpp::detail::resolve_intra_process_buffer_type(options_.intra_process_buffer_type),
|
||||
qos,
|
||||
qos_profile,
|
||||
std::make_shared<ROSMessageTypeAllocator>(ros_message_type_allocator_));
|
||||
}
|
||||
// Register the publisher with the intra process manager.
|
||||
uint64_t intra_process_publisher_id = ipm->add_publisher(this->shared_from_this(), buffer_);
|
||||
this->setup_intra_process(
|
||||
intra_process_publisher_id,
|
||||
@@ -232,8 +251,12 @@ public:
|
||||
// interprocess publish, resulting in lower publish-to-subscribe latency.
|
||||
// It's not possible to do that with an unique_ptr,
|
||||
// as do_intra_process_publish takes the ownership of the message.
|
||||
|
||||
// When durability is set to TransientLocal (i.e. there is a buffer),
|
||||
// inter process publish should always take place to ensure
|
||||
// late joiners receive past data.
|
||||
bool inter_process_publish_needed =
|
||||
get_subscription_count() > get_intra_process_subscription_count();
|
||||
get_subscription_count() > get_intra_process_subscription_count() || buffer_;
|
||||
|
||||
if (inter_process_publish_needed) {
|
||||
auto shared_msg =
|
||||
@@ -310,8 +333,11 @@ public:
|
||||
return;
|
||||
}
|
||||
|
||||
// When durability is set to TransientLocal (i.e. there is a buffer),
|
||||
// inter process publish should always take place to ensure
|
||||
// late joiners receive past data.
|
||||
bool inter_process_publish_needed =
|
||||
get_subscription_count() > get_intra_process_subscription_count();
|
||||
get_subscription_count() > get_intra_process_subscription_count() || buffer_;
|
||||
|
||||
if (inter_process_publish_needed) {
|
||||
auto ros_msg_ptr = std::make_shared<ROSMessageType>();
|
||||
@@ -407,6 +433,13 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
[[deprecated("use get_published_type_allocator() or get_ros_message_type_allocator() instead")]]
|
||||
std::shared_ptr<PublishedTypeAllocator>
|
||||
get_allocator() const
|
||||
{
|
||||
return std::make_shared<PublishedTypeAllocator>(published_type_allocator_);
|
||||
}
|
||||
|
||||
PublishedTypeAllocator
|
||||
get_published_type_allocator() const
|
||||
{
|
||||
|
||||
@@ -39,6 +39,10 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool sleep() = 0;
|
||||
|
||||
[[deprecated("use get_type() instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool is_steady() const = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual rcl_clock_type_t get_type() const = 0;
|
||||
|
||||
@@ -50,6 +54,82 @@ using std::chrono::duration;
|
||||
using std::chrono::duration_cast;
|
||||
using std::chrono::nanoseconds;
|
||||
|
||||
template<class Clock = std::chrono::high_resolution_clock>
|
||||
class [[deprecated("use rclcpp::Rate class instead of GenericRate")]] GenericRate : public RateBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate)
|
||||
|
||||
explicit GenericRate(double rate)
|
||||
: period_(duration_cast<nanoseconds>(duration<double>(1.0 / rate))), last_interval_(Clock::now())
|
||||
{}
|
||||
explicit GenericRate(std::chrono::nanoseconds period)
|
||||
: period_(period), last_interval_(Clock::now())
|
||||
{}
|
||||
|
||||
virtual bool
|
||||
sleep()
|
||||
{
|
||||
// Time coming into sleep
|
||||
auto now = Clock::now();
|
||||
// Time of next interval
|
||||
auto next_interval = last_interval_ + period_;
|
||||
// Detect backwards time flow
|
||||
if (now < last_interval_) {
|
||||
// Best thing to do is to set the next_interval to now + period
|
||||
next_interval = now + period_;
|
||||
}
|
||||
// Calculate the time to sleep
|
||||
auto time_to_sleep = next_interval - now;
|
||||
// Update the interval
|
||||
last_interval_ += period_;
|
||||
// If the time_to_sleep is negative or zero, don't sleep
|
||||
if (time_to_sleep <= std::chrono::seconds(0)) {
|
||||
// If an entire cycle was missed then reset next interval.
|
||||
// This might happen if the loop took more than a cycle.
|
||||
// Or if time jumps forward.
|
||||
if (now > next_interval + period_) {
|
||||
last_interval_ = now + period_;
|
||||
}
|
||||
// Either way do not sleep and return false
|
||||
return false;
|
||||
}
|
||||
// Sleep (will get interrupted by ctrl-c, may not sleep full time)
|
||||
rclcpp::sleep_for(time_to_sleep);
|
||||
return true;
|
||||
}
|
||||
|
||||
[[deprecated("use get_type() instead")]]
|
||||
virtual bool
|
||||
is_steady() const
|
||||
{
|
||||
return Clock::is_steady;
|
||||
}
|
||||
|
||||
virtual rcl_clock_type_t get_type() const
|
||||
{
|
||||
return Clock::is_steady ? RCL_STEADY_TIME : RCL_SYSTEM_TIME;
|
||||
}
|
||||
|
||||
virtual void
|
||||
reset()
|
||||
{
|
||||
last_interval_ = Clock::now();
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds period() const
|
||||
{
|
||||
return period_;
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(GenericRate)
|
||||
|
||||
std::chrono::nanoseconds period_;
|
||||
using ClockDurationNano = std::chrono::duration<typename Clock::rep, std::nano>;
|
||||
std::chrono::time_point<Clock, ClockDurationNano> last_interval_;
|
||||
};
|
||||
|
||||
class Rate : public RateBase
|
||||
{
|
||||
public:
|
||||
@@ -69,6 +149,11 @@ public:
|
||||
virtual bool
|
||||
sleep();
|
||||
|
||||
[[deprecated("use get_type() instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
virtual bool
|
||||
is_steady() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual rcl_clock_type_t
|
||||
get_type() const;
|
||||
|
||||
@@ -307,7 +307,7 @@ public:
|
||||
* \param[in] node_handle NodeBaseInterface pointer that is used in part of the setup.
|
||||
* \param[in] service_name Name of the topic to publish to.
|
||||
* \param[in] any_callback User defined callback to call when a client request is received.
|
||||
* \param[in] service_options options for the service.
|
||||
* \param[in] service_options options for the subscription.
|
||||
*/
|
||||
Service(
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
|
||||
@@ -90,6 +90,18 @@ public:
|
||||
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
|
||||
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
|
||||
|
||||
using MessageAllocatorTraits [[deprecated("use ROSMessageTypeAllocatorTraits")]] =
|
||||
ROSMessageTypeAllocatorTraits;
|
||||
using MessageAllocator [[deprecated("use ROSMessageTypeAllocator")]] =
|
||||
ROSMessageTypeAllocator;
|
||||
using MessageDeleter [[deprecated("use ROSMessageTypeDeleter")]] =
|
||||
ROSMessageTypeDeleter;
|
||||
|
||||
using ConstMessageSharedPtr [[deprecated]] = std::shared_ptr<const ROSMessageType>;
|
||||
using MessageUniquePtr
|
||||
[[deprecated("use std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> instead")]] =
|
||||
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>;
|
||||
|
||||
private:
|
||||
using SubscriptionTopicStatisticsSharedPtr =
|
||||
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics>;
|
||||
@@ -147,11 +159,13 @@ public:
|
||||
auto qos_profile = get_actual_qos();
|
||||
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
"intraprocess communication on topic '" + topic_name +
|
||||
"' allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos_profile.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication is not allowed with 0 depth qos policy");
|
||||
"intraprocess communication on topic '" + topic_name +
|
||||
"' is not allowed with 0 depth qos policy");
|
||||
}
|
||||
|
||||
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
|
||||
|
||||
@@ -80,7 +80,8 @@ struct SubscriptionOptionsBase
|
||||
|
||||
// An optional QoS which can provide topic_statistics with a stable QoS separate from
|
||||
// the subscription's current QoS settings which could be unstable.
|
||||
rclcpp::QoS qos = SystemDefaultsQoS();
|
||||
// Explicitly set the enough depth to avoid missing the statistics messages.
|
||||
rclcpp::QoS qos = SystemDefaultsQoS().keep_last(10);
|
||||
};
|
||||
|
||||
TopicStatisticsOptions topic_stats_options;
|
||||
|
||||
@@ -49,7 +49,6 @@ public:
|
||||
/**
|
||||
* \param nanoseconds since time epoch
|
||||
* \param clock_type clock type
|
||||
* \throws std::runtime_error if nanoseconds are negative
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit Time(int64_t nanoseconds = 0, rcl_clock_type_t clock_type = RCL_SYSTEM_TIME);
|
||||
|
||||
@@ -28,26 +28,64 @@
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
/// Load the type support library for the given type.
|
||||
/**
|
||||
* \param[in] type The topic type, e.g. "std_msgs/msg/String"
|
||||
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
|
||||
* \return A shared library
|
||||
*/
|
||||
|
||||
/// \brief Extract the package name, middle module, and type name from a full type string.
|
||||
/// \details This function takes a full type string (e.g., "std_msgs/msg/String") and extracts
|
||||
/// the package name, middle module (if any), and type name. The middle module is the part
|
||||
/// between the package name and the type name, which is typically used for message types.
|
||||
/// For example, for "std_msgs/msg/String", it returns ("std_msgs", "msg", "String").
|
||||
/// \param[in] full_type
|
||||
/// \throws std::runtime_error if the input full type string is malformed or does not follow the
|
||||
/// expected format.
|
||||
/// \return A tuple containing the package name, middle module (if any), and type name.
|
||||
RCLCPP_PUBLIC
|
||||
std::tuple<std::string, std::string, std::string>
|
||||
extract_type_identifier(const std::string & full_type);
|
||||
|
||||
/// \brief Look for the library in the ament prefix paths and return the path to the type support
|
||||
/// library.
|
||||
/// \param[in] package_name The name of the package containing the type support library,
|
||||
/// e.g. "std_msgs".
|
||||
/// \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
|
||||
/// \throws std::runtime_error if the library is not found.
|
||||
/// \return The path to the type support library.
|
||||
RCLCPP_PUBLIC
|
||||
std::string get_typesupport_library_path(
|
||||
const std::string & package_name, const std::string & typesupport_identifier);
|
||||
|
||||
/// \brief Load the type support library for the given type.
|
||||
/// \param[in] type The topic type, e.g. "std_msgs/msg/String"
|
||||
/// \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
|
||||
/// \throws std::runtime_error if the library is not found or cannot be loaded.
|
||||
/// \return A shared library
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rcpputils::SharedLibrary>
|
||||
get_typesupport_library(const std::string & type, const std::string & typesupport_identifier);
|
||||
|
||||
/// Extract the message type support handle from the library.
|
||||
/**
|
||||
* The library needs to match the topic type. The shared library must stay loaded for the lifetime of the result.
|
||||
*
|
||||
* \param[in] type The topic type, e.g. "std_msgs/msg/String"
|
||||
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
|
||||
* \param[in] library The shared type support library
|
||||
* \throws std::runtime_error if the symbol of type not found in the library.
|
||||
* \return A message type support handle
|
||||
*/
|
||||
/// \brief Extract the type support handle from the library.
|
||||
/// \note The library needs to match the topic type. The shared library must stay loaded for the
|
||||
/// lifetime of the result.
|
||||
/// \deprecated Use get_message_typesupport_handle() instead
|
||||
/// \param[in] type The topic type, e.g. "std_msgs/msg/String"
|
||||
/// \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
|
||||
/// \param[in] library The shared type support library
|
||||
/// \return A type support handle
|
||||
[[deprecated("Use `get_message_typesupport_handle` instead")]]
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_message_type_support_t *
|
||||
get_typesupport_handle(
|
||||
const std::string & type,
|
||||
const std::string & typesupport_identifier,
|
||||
rcpputils::SharedLibrary & library);
|
||||
|
||||
/// \brief Extracts the message type support handle from the library.
|
||||
/// \note The library needs to match the topic type. The shared library must stay loaded for the
|
||||
/// lifetime of the result.
|
||||
/// \param[in] type The topic type, e.g. "std_msgs/msg/String"
|
||||
/// \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
|
||||
/// \param[in] library The shared type support library
|
||||
/// \throws std::runtime_error if the symbol of type not found in the library.
|
||||
/// \return A message type support handle
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_message_type_support_t *
|
||||
get_message_typesupport_handle(
|
||||
@@ -55,16 +93,14 @@ get_message_typesupport_handle(
|
||||
const std::string & typesupport_identifier,
|
||||
rcpputils::SharedLibrary & library);
|
||||
|
||||
/// Extract the service type support handle from the library.
|
||||
/**
|
||||
* The library needs to match the topic type. The shared library must stay loaded for the lifetime of the result.
|
||||
*
|
||||
* \param[in] type The service type, e.g. "std_srvs/srv/Empty"
|
||||
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
|
||||
* \param[in] library The shared type support library
|
||||
* \throws std::runtime_error if the symbol of type not found in the library.
|
||||
* \return A service type support handle
|
||||
*/
|
||||
/// \brief Extracts the service type support handle from the library.
|
||||
/// \note The library needs to match the service type. The shared library must stay loaded for the
|
||||
/// lifetime of the result.
|
||||
/// \param[in] type The service type, e.g. "std_srvs/srv/Empty"
|
||||
/// \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
|
||||
/// \param[in] library The shared type support library
|
||||
/// \throws std::runtime_error if the symbol of type not found in the library.
|
||||
/// \return A service type support handle
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_service_type_support_t *
|
||||
get_service_typesupport_handle(
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP__WAIT_FOR_MESSAGE_HPP_
|
||||
#define RCLCPP__WAIT_FOR_MESSAGE_HPP_
|
||||
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -23,6 +24,7 @@
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/wait_set.hpp"
|
||||
#include "rclcpp/qos.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -79,10 +81,11 @@ bool wait_for_message(
|
||||
/**
|
||||
* Wait for the next incoming message to arrive on a specified topic before the specified timeout.
|
||||
*
|
||||
* \param[out] out is the message to be filled when a new message is arriving.
|
||||
* \param[out] out is the message to be filled when a new message is arriving
|
||||
* \param[in] node the node pointer to initialize the subscription on.
|
||||
* \param[in] topic the topic to wait for messages.
|
||||
* \param[in] time_to_wait parameter specifying the timeout before returning.
|
||||
* \param[in] qos parameter specifying QoS settings for the subscription.
|
||||
* \return true if a message was successfully received, false if message could not
|
||||
* be obtained or shutdown was triggered asynchronously on the context.
|
||||
*/
|
||||
@@ -91,9 +94,10 @@ bool wait_for_message(
|
||||
MsgT & out,
|
||||
rclcpp::Node::SharedPtr node,
|
||||
const std::string & topic,
|
||||
std::chrono::duration<Rep, Period> time_to_wait = std::chrono::duration<Rep, Period>(-1))
|
||||
std::chrono::duration<Rep, Period> time_to_wait = std::chrono::duration<Rep, Period>(-1),
|
||||
const rclcpp::QoS & qos = rclcpp::SystemDefaultsQoS())
|
||||
{
|
||||
auto sub = node->create_subscription<MsgT>(topic, 1, [](const std::shared_ptr<const MsgT>) {});
|
||||
auto sub = node->create_subscription<MsgT>(topic, qos, [](const std::shared_ptr<const MsgT>) {});
|
||||
return wait_for_message<MsgT, Rep, Period>(
|
||||
out, sub, node->get_node_options().context(), time_to_wait);
|
||||
}
|
||||
|
||||
@@ -176,7 +176,9 @@ public:
|
||||
for (; ii < wait_set.size_of_timers(); ++ii) {
|
||||
if (rcl_wait_set.timers[ii] != nullptr) {
|
||||
ret = wait_set.timers(ii);
|
||||
break;
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -217,7 +219,9 @@ public:
|
||||
if (rcl_wait_set.subscriptions[ii] != nullptr) {
|
||||
ret = wait_set.subscriptions(ii);
|
||||
rcl_wait_set.subscriptions[ii] = nullptr;
|
||||
break;
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -237,7 +241,9 @@ public:
|
||||
if (rcl_wait_set.services[ii] != nullptr) {
|
||||
ret = wait_set.services(ii);
|
||||
rcl_wait_set.services[ii] = nullptr;
|
||||
break;
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -257,7 +263,9 @@ public:
|
||||
if (rcl_wait_set.clients[ii] != nullptr) {
|
||||
ret = wait_set.clients(ii);
|
||||
rcl_wait_set.clients[ii] = nullptr;
|
||||
break;
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,6 +216,11 @@ public:
|
||||
shared_waitables_
|
||||
);
|
||||
|
||||
if (this->needs_pruning_) {
|
||||
this->storage_prune_deleted_entities();
|
||||
this->needs_pruning_ = false;
|
||||
}
|
||||
|
||||
this->storage_release_ownerships();
|
||||
}
|
||||
|
||||
@@ -455,59 +460,60 @@ public:
|
||||
|
||||
size_t size_of_subscriptions() const
|
||||
{
|
||||
return shared_subscriptions_.size();
|
||||
return subscriptions_.size();
|
||||
}
|
||||
|
||||
size_t size_of_timers() const
|
||||
{
|
||||
return shared_timers_.size();
|
||||
return timers_.size();
|
||||
}
|
||||
|
||||
size_t size_of_clients() const
|
||||
{
|
||||
return shared_clients_.size();
|
||||
return clients_.size();
|
||||
}
|
||||
|
||||
size_t size_of_services() const
|
||||
{
|
||||
return shared_services_.size();
|
||||
return services_.size();
|
||||
}
|
||||
|
||||
size_t size_of_waitables() const
|
||||
{
|
||||
return shared_waitables_.size();
|
||||
return waitables_.size();
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::SubscriptionBase>
|
||||
subscriptions(size_t ii) const
|
||||
{
|
||||
return shared_subscriptions_[ii].subscription;
|
||||
return subscriptions_[ii].lock();
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::TimerBase>
|
||||
timers(size_t ii) const
|
||||
{
|
||||
return shared_timers_[ii];
|
||||
return timers_[ii].lock();
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::ClientBase>
|
||||
clients(size_t ii) const
|
||||
{
|
||||
return shared_clients_[ii];
|
||||
return clients_[ii].lock();
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::ServiceBase>
|
||||
services(size_t ii) const
|
||||
{
|
||||
return shared_services_[ii];
|
||||
return services_[ii].lock();
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::Waitable>
|
||||
waitables(size_t ii) const
|
||||
{
|
||||
return shared_waitables_[ii].waitable;
|
||||
return waitables_[ii].lock();
|
||||
}
|
||||
|
||||
private:
|
||||
size_t ownership_reference_counter_ = 0;
|
||||
|
||||
SequenceOfWeakSubscriptions subscriptions_;
|
||||
|
||||
@@ -160,6 +160,15 @@ public:
|
||||
services_,
|
||||
waitables_
|
||||
);
|
||||
|
||||
if(this->needs_pruning_) {
|
||||
// we need to throw here, as the indexing of the rcl_waitset is broken,
|
||||
// in case of invalid entries
|
||||
|
||||
throw std::runtime_error(
|
||||
"StaticStorage::storage_rebuild_rcl_wait_set(): entity weak_ptr "
|
||||
"unexpectedly expired in static entity storage");
|
||||
}
|
||||
}
|
||||
|
||||
// storage_add_subscription() explicitly not declared here
|
||||
|
||||
@@ -734,8 +734,6 @@ private:
|
||||
wait_result_dirty_ = false;
|
||||
// this method comes from the SynchronizationPolicy
|
||||
this->sync_wait_result_acquire();
|
||||
// this method comes from the StoragePolicy
|
||||
this->storage_acquire_ownerships();
|
||||
}
|
||||
|
||||
/// Called by the WaitResult's destructor to release resources.
|
||||
@@ -752,8 +750,6 @@ private:
|
||||
}
|
||||
wait_result_holding_ = false;
|
||||
wait_result_dirty_ = false;
|
||||
// this method comes from the StoragePolicy
|
||||
this->storage_release_ownerships();
|
||||
// this method comes from the SynchronizationPolicy
|
||||
this->sync_wait_result_release();
|
||||
}
|
||||
|
||||
@@ -101,6 +101,23 @@ public:
|
||||
size_t
|
||||
get_number_of_ready_guard_conditions();
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
|
||||
#endif
|
||||
/// Deprecated.
|
||||
/**
|
||||
* \sa add_to_wait_set(rcl_wait_set_t &)
|
||||
*/
|
||||
[[deprecated("Use add_to_wait_set(rcl_wait_set_t & wait_set) signature")]]
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set);
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
/// Add the Waitable to a wait set.
|
||||
/**
|
||||
* \param[in] wait_set A handle to the wait set to add the Waitable to.
|
||||
@@ -111,6 +128,23 @@ public:
|
||||
void
|
||||
add_to_wait_set(rcl_wait_set_t & wait_set);
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
|
||||
#endif
|
||||
/// Deprecated.
|
||||
/**
|
||||
* \sa is_ready(const rcl_wait_set_t &)
|
||||
*/
|
||||
[[deprecated("Use is_ready(const rcl_wait_set_t & wait_set) signature")]]
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
is_ready(rcl_wait_set_t * wait_set);
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
/// Check if the Waitable is ready.
|
||||
/**
|
||||
* The input wait set should be the same that was used in a previously call to
|
||||
@@ -178,6 +212,23 @@ public:
|
||||
std::shared_ptr<void>
|
||||
take_data_by_entity_id(size_t id);
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic push
|
||||
#pragma GCC diagnostic ignored "-Woverloaded-virtual"
|
||||
#endif
|
||||
/// Deprecated.
|
||||
/**
|
||||
* \sa execute(const std::shared_ptr<void> &)
|
||||
*/
|
||||
[[deprecated("Use execute(const std::shared_ptr<void> & data) signature")]]
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
execute(std::shared_ptr<void> & data);
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
/// Execute data that is passed in.
|
||||
/**
|
||||
* Before calling this method, the Waitable should be added to a wait set,
|
||||
|
||||
@@ -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>28.3.3</version>
|
||||
<version>28.1.15</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -124,8 +124,7 @@ def get_rclcpp_suffix_from_features(features):
|
||||
) \
|
||||
do { \
|
||||
static_assert( \
|
||||
::std::is_same<typename std::remove_cv_t<typename std::remove_reference_t<decltype(logger)>>, \
|
||||
typename ::rclcpp::Logger>::value, \
|
||||
::std::is_convertible_v<decltype(logger), ::rclcpp::Logger>, \
|
||||
"First argument to logging macros must be an rclcpp::Logger"); \
|
||||
@[ if 'throttle' in feature_combination]@ \
|
||||
auto get_time_point = [&c=clock](rcutils_time_point_value_t * time_point) -> rcutils_ret_t { \
|
||||
|
||||
@@ -118,7 +118,7 @@ Clock::sleep_until(
|
||||
});
|
||||
// No longer need the shutdown callback when this function exits
|
||||
auto callback_remover = rcpputils::scope_exit(
|
||||
[context, &shutdown_cb_handle]() {
|
||||
[&context, &shutdown_cb_handle]() {
|
||||
context->remove_on_shutdown_callback(shutdown_cb_handle);
|
||||
});
|
||||
|
||||
@@ -367,4 +367,245 @@ Clock::create_jump_callback(
|
||||
// *INDENT-ON*
|
||||
}
|
||||
|
||||
class ClockWaiter::ClockWaiterImpl
|
||||
{
|
||||
private:
|
||||
std::condition_variable cv_;
|
||||
|
||||
rclcpp::Clock::SharedPtr clock_;
|
||||
bool time_source_changed_ = false;
|
||||
std::function<void(const rcl_time_jump_t &)> post_time_jump_callback;
|
||||
|
||||
bool
|
||||
wait_until_system_time(
|
||||
std::unique_lock<std::mutex> & lock,
|
||||
const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
|
||||
{
|
||||
auto system_time = std::chrono::system_clock::time_point(
|
||||
// Cast because system clock resolution is too big for nanoseconds on some systems
|
||||
std::chrono::duration_cast<std::chrono::system_clock::duration>(
|
||||
std::chrono::nanoseconds(abs_time.nanoseconds())));
|
||||
|
||||
return cv_.wait_until(lock, system_time, pred);
|
||||
}
|
||||
|
||||
bool
|
||||
wait_until_steady_time(
|
||||
std::unique_lock<std::mutex> & lock,
|
||||
const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
|
||||
{
|
||||
// Synchronize because RCL steady clock epoch might differ from chrono::steady_clock epoch
|
||||
const rclcpp::Time rcl_entry = clock_->now();
|
||||
const std::chrono::steady_clock::time_point chrono_entry = std::chrono::steady_clock::now();
|
||||
const rclcpp::Duration delta_t = abs_time - rcl_entry;
|
||||
const std::chrono::steady_clock::time_point chrono_until =
|
||||
chrono_entry + std::chrono::nanoseconds(delta_t.nanoseconds());
|
||||
|
||||
return cv_.wait_until(lock, chrono_until, pred);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
wait_until_ros_time(
|
||||
std::unique_lock<std::mutex> & lock,
|
||||
const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
|
||||
{
|
||||
// Install jump handler for any amount of time change, for two purposes:
|
||||
// - if ROS time is active, check if time reached on each new clock sample
|
||||
// - Trigger via on_clock_change to detect if time source changes, to invalidate sleep
|
||||
rcl_jump_threshold_t threshold;
|
||||
threshold.on_clock_change = true;
|
||||
// 0 is disable, so -1 and 1 are smallest possible time changes
|
||||
threshold.min_backward.nanoseconds = -1;
|
||||
threshold.min_forward.nanoseconds = 1;
|
||||
|
||||
time_source_changed_ = false;
|
||||
|
||||
post_time_jump_callback = [this, &lock] (const rcl_time_jump_t & jump)
|
||||
{
|
||||
if (jump.clock_change != RCL_ROS_TIME_NO_CHANGE) {
|
||||
std::lock_guard<std::mutex> lk(*lock.mutex());
|
||||
time_source_changed_ = true;
|
||||
}
|
||||
cv_.notify_one();
|
||||
};
|
||||
|
||||
// Note this is a trade-off. Adding the callback for every call
|
||||
// is expensive for high frequency calls. For low frequency waits
|
||||
// its more overhead to have the callback being called all the time.
|
||||
// As we expect the use case to be low frequency calls to wait_until
|
||||
// with relative big pauses between the calls, we install it on demand.
|
||||
auto clock_handler = clock_->create_jump_callback(
|
||||
nullptr,
|
||||
post_time_jump_callback,
|
||||
threshold);
|
||||
|
||||
if (!clock_->ros_time_is_active()) {
|
||||
auto system_time = std::chrono::system_clock::time_point(
|
||||
// Cast because system clock resolution is too big for nanoseconds on some systems
|
||||
std::chrono::duration_cast<std::chrono::system_clock::duration>(
|
||||
std::chrono::nanoseconds(abs_time.nanoseconds())));
|
||||
|
||||
return cv_.wait_until(lock, system_time, [this, &pred] () {
|
||||
return time_source_changed_ || pred();
|
||||
});
|
||||
}
|
||||
|
||||
// RCL_ROS_TIME with ros_time_is_active.
|
||||
// Just wait without "until" because installed
|
||||
// jump callbacks wake the cv on every new sample.
|
||||
cv_.wait(lock, [this, &pred, &abs_time] () {
|
||||
return clock_->now() >= abs_time || time_source_changed_ || pred();
|
||||
});
|
||||
|
||||
return clock_->now() < abs_time;
|
||||
}
|
||||
|
||||
public:
|
||||
explicit ClockWaiterImpl(const rclcpp::Clock::SharedPtr & clock)
|
||||
:clock_(clock)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
wait_until(
|
||||
std::unique_lock<std::mutex> & lock,
|
||||
const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
|
||||
{
|
||||
switch(clock_->get_clock_type()) {
|
||||
case RCL_CLOCK_UNINITIALIZED:
|
||||
throw std::runtime_error("Error, wait on uninitialized clock called");
|
||||
case RCL_ROS_TIME:
|
||||
return wait_until_ros_time(lock, abs_time, pred);
|
||||
break;
|
||||
case RCL_STEADY_TIME:
|
||||
return wait_until_steady_time(lock, abs_time, pred);
|
||||
break;
|
||||
case RCL_SYSTEM_TIME:
|
||||
return wait_until_system_time(lock, abs_time, pred);
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
notify_one()
|
||||
{
|
||||
cv_.notify_one();
|
||||
}
|
||||
};
|
||||
|
||||
ClockWaiter::ClockWaiter(const rclcpp::Clock::SharedPtr & clock)
|
||||
:impl_(std::make_unique<ClockWaiterImpl>(clock))
|
||||
{
|
||||
}
|
||||
|
||||
ClockWaiter::~ClockWaiter() = default;
|
||||
|
||||
bool
|
||||
ClockWaiter::wait_until(
|
||||
std::unique_lock<std::mutex> & lock,
|
||||
const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
|
||||
{
|
||||
return impl_->wait_until(lock, abs_time, pred);
|
||||
}
|
||||
|
||||
void
|
||||
ClockWaiter::notify_one()
|
||||
{
|
||||
impl_->notify_one();
|
||||
}
|
||||
|
||||
class ClockConditionalVariable::Impl
|
||||
{
|
||||
std::mutex pred_mutex_;
|
||||
bool shutdown_ = false;
|
||||
rclcpp::Context::SharedPtr context_;
|
||||
rclcpp::OnShutdownCallbackHandle shutdown_cb_handle_;
|
||||
ClockWaiter::UniquePtr clock_;
|
||||
|
||||
public:
|
||||
Impl(const rclcpp::Clock::SharedPtr & clock, rclcpp::Context::SharedPtr context)
|
||||
:context_(context),
|
||||
clock_(std::make_unique<ClockWaiter>(clock))
|
||||
{
|
||||
if (!context_ || !context_->is_valid()) {
|
||||
throw std::runtime_error("context cannot be slept with because it's invalid");
|
||||
}
|
||||
// Wake this thread if the context is shutdown
|
||||
shutdown_cb_handle_ = context_->add_on_shutdown_callback(
|
||||
[this]() {
|
||||
{
|
||||
std::unique_lock lock(pred_mutex_);
|
||||
shutdown_ = true;
|
||||
}
|
||||
clock_->notify_one();
|
||||
});
|
||||
}
|
||||
|
||||
~Impl()
|
||||
{
|
||||
context_->remove_on_shutdown_callback(shutdown_cb_handle_);
|
||||
}
|
||||
|
||||
bool
|
||||
wait_until(
|
||||
std::unique_lock<std::mutex> & lock, rclcpp::Time until,
|
||||
const std::function<bool ()> & pred)
|
||||
{
|
||||
if(lock.mutex() != &pred_mutex_) {
|
||||
throw std::runtime_error(
|
||||
"ClockConditionalVariable::wait_until: Internal error, given lock does not use"
|
||||
" mutex returned by this->mutex()");
|
||||
}
|
||||
|
||||
clock_->wait_until(lock, until, [this, &pred] () -> bool {
|
||||
return shutdown_ || pred();
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
notify_one()
|
||||
{
|
||||
clock_->notify_one();
|
||||
}
|
||||
|
||||
std::mutex &
|
||||
mutex()
|
||||
{
|
||||
return pred_mutex_;
|
||||
}
|
||||
};
|
||||
|
||||
ClockConditionalVariable::ClockConditionalVariable(
|
||||
const rclcpp::Clock::SharedPtr & clock,
|
||||
rclcpp::Context::SharedPtr context)
|
||||
:impl_(std::make_unique<Impl>(clock, context))
|
||||
{
|
||||
}
|
||||
|
||||
ClockConditionalVariable::~ClockConditionalVariable() = default;
|
||||
|
||||
void
|
||||
ClockConditionalVariable::notify_one()
|
||||
{
|
||||
impl_->notify_one();
|
||||
}
|
||||
|
||||
bool
|
||||
ClockConditionalVariable::wait_until(
|
||||
std::unique_lock<std::mutex> & lock, rclcpp::Time until,
|
||||
const std::function<bool ()> & pred)
|
||||
{
|
||||
return impl_->wait_until(lock, until, pred);
|
||||
}
|
||||
|
||||
std::mutex &
|
||||
ClockConditionalVariable::mutex()
|
||||
{
|
||||
return impl_->mutex();
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "rclcpp/context.hpp"
|
||||
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
@@ -142,11 +143,52 @@ rclcpp_logging_output_handler(
|
||||
}
|
||||
} // extern "C"
|
||||
|
||||
/**
|
||||
* Global storage for pre and post shutdown recursive mutexes.
|
||||
* Note, this is a ABI compatibility hack.
|
||||
*/
|
||||
class MutexLookup
|
||||
{
|
||||
std::mutex m;
|
||||
|
||||
struct MutexHolder
|
||||
{
|
||||
std::recursive_mutex on_shutdown_callbacks_mutex_;
|
||||
std::recursive_mutex pre_shutdown_callbacks_mutex_;
|
||||
};
|
||||
|
||||
std::map<const Context *, std::unique_ptr<MutexHolder>> mutexMap;
|
||||
|
||||
public:
|
||||
MutexHolder & getMutexes(const Context *forContext)
|
||||
{
|
||||
auto it = mutexMap.find(forContext);
|
||||
if(it == mutexMap.end()) {
|
||||
it = mutexMap.emplace(forContext, std::make_unique<MutexHolder>()).first;
|
||||
}
|
||||
|
||||
return *(it->second);
|
||||
}
|
||||
|
||||
/**
|
||||
* Only supposed to be called on deletion of context
|
||||
*/
|
||||
void removeMutexes(const Context *forContext)
|
||||
{
|
||||
mutexMap.erase(forContext);
|
||||
}
|
||||
};
|
||||
|
||||
MutexLookup mutexStorage;
|
||||
|
||||
Context::Context()
|
||||
: rcl_context_(nullptr),
|
||||
shutdown_reason_(""),
|
||||
logging_mutex_(nullptr)
|
||||
{}
|
||||
{
|
||||
// allocate mutexes
|
||||
mutexStorage.getMutexes(this);
|
||||
}
|
||||
|
||||
Context::~Context()
|
||||
{
|
||||
@@ -165,6 +207,9 @@ Context::~Context()
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "unhandled exception in ~Context()");
|
||||
}
|
||||
|
||||
// delete mutexes
|
||||
mutexStorage.removeMutexes(this);
|
||||
}
|
||||
|
||||
RCLCPP_LOCAL
|
||||
@@ -311,9 +356,17 @@ Context::shutdown(const std::string & reason)
|
||||
|
||||
// call each pre-shutdown callback
|
||||
{
|
||||
std::lock_guard<std::mutex> lock{pre_shutdown_callbacks_mutex_};
|
||||
for (const auto & callback : pre_shutdown_callbacks_) {
|
||||
(*callback)();
|
||||
std::lock_guard<std::recursive_mutex> lock{mutexStorage.getMutexes(
|
||||
this).pre_shutdown_callbacks_mutex_};
|
||||
// callbacks may delete other callbacks during the execution,
|
||||
// therefore we need to save a copy and check before execution
|
||||
// if the next callback is still present
|
||||
auto cpy = pre_shutdown_callbacks_;
|
||||
for (const auto & callback : cpy) {
|
||||
auto it = std::find(pre_shutdown_callbacks_.begin(), pre_shutdown_callbacks_.end(), callback);
|
||||
if(it != pre_shutdown_callbacks_.end()) {
|
||||
(*callback)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -326,9 +379,17 @@ Context::shutdown(const std::string & reason)
|
||||
shutdown_reason_ = reason;
|
||||
// call each shutdown callback
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
|
||||
for (const auto & callback : on_shutdown_callbacks_) {
|
||||
(*callback)();
|
||||
std::lock_guard<std::recursive_mutex> lock(mutexStorage.getMutexes(
|
||||
this).on_shutdown_callbacks_mutex_);
|
||||
// callbacks may delete other callbacks during the execution,
|
||||
// therefore we need to save a copy and check before execution
|
||||
// if the next callback is still present
|
||||
auto cpy = on_shutdown_callbacks_;
|
||||
for (const auto & callback : cpy) {
|
||||
auto it = std::find(on_shutdown_callbacks_.begin(), on_shutdown_callbacks_.end(), callback);
|
||||
if(it != on_shutdown_callbacks_.end()) {
|
||||
(*callback)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -399,10 +460,12 @@ Context::add_shutdown_callback(
|
||||
shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
|
||||
|
||||
if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
|
||||
std::lock_guard<std::mutex> lock(pre_shutdown_callbacks_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(mutexStorage.getMutexes(
|
||||
this).pre_shutdown_callbacks_mutex_);
|
||||
pre_shutdown_callbacks_.emplace_back(callback_shared_ptr);
|
||||
} else {
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(mutexStorage.getMutexes(
|
||||
this).on_shutdown_callbacks_mutex_);
|
||||
on_shutdown_callbacks_.emplace_back(callback_shared_ptr);
|
||||
}
|
||||
|
||||
@@ -422,7 +485,7 @@ Context::remove_shutdown_callback(
|
||||
}
|
||||
|
||||
const auto remove_callback = [&callback_shared_ptr](auto & mutex, auto & callback_vector) {
|
||||
const std::lock_guard<std::mutex> lock(mutex);
|
||||
const std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
auto iter = callback_vector.begin();
|
||||
for (; iter != callback_vector.end(); iter++) {
|
||||
if ((*iter).get() == callback_shared_ptr.get()) {
|
||||
@@ -433,6 +496,7 @@ Context::remove_shutdown_callback(
|
||||
return false;
|
||||
}
|
||||
callback_vector.erase(iter);
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
@@ -440,9 +504,11 @@ Context::remove_shutdown_callback(
|
||||
shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
|
||||
|
||||
if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
|
||||
return remove_callback(pre_shutdown_callbacks_mutex_, pre_shutdown_callbacks_);
|
||||
return remove_callback(mutexStorage.getMutexes(this).pre_shutdown_callbacks_mutex_,
|
||||
pre_shutdown_callbacks_);
|
||||
} else {
|
||||
return remove_callback(on_shutdown_callbacks_mutex_, on_shutdown_callbacks_);
|
||||
return remove_callback(mutexStorage.getMutexes(this).on_shutdown_callbacks_mutex_,
|
||||
on_shutdown_callbacks_);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -463,7 +529,7 @@ std::vector<rclcpp::Context::ShutdownCallback>
|
||||
Context::get_shutdown_callback() const
|
||||
{
|
||||
const auto get_callback_vector = [](auto & mutex, auto & callback_set) {
|
||||
const std::lock_guard<std::mutex> lock(mutex);
|
||||
const std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
std::vector<rclcpp::Context::ShutdownCallback> callbacks;
|
||||
for (auto & callback : callback_set) {
|
||||
callbacks.push_back(*callback);
|
||||
@@ -475,9 +541,11 @@ Context::get_shutdown_callback() const
|
||||
shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
|
||||
|
||||
if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
|
||||
return get_callback_vector(pre_shutdown_callbacks_mutex_, pre_shutdown_callbacks_);
|
||||
return get_callback_vector(mutexStorage.getMutexes(this).pre_shutdown_callbacks_mutex_,
|
||||
pre_shutdown_callbacks_);
|
||||
} else {
|
||||
return get_callback_vector(on_shutdown_callbacks_mutex_, on_shutdown_callbacks_);
|
||||
return get_callback_vector(mutexStorage.getMutexes(this).on_shutdown_callbacks_mutex_,
|
||||
on_shutdown_callbacks_);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -84,7 +84,9 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
|
||||
notify_waitable_->add_guard_condition(interrupt_guard_condition_);
|
||||
notify_waitable_->add_guard_condition(shutdown_guard_condition_);
|
||||
|
||||
wait_set_.add_waitable(notify_waitable_);
|
||||
// we need to initially rebuild the collection,
|
||||
// so that the notify_waitable_ is added
|
||||
collect_entities();
|
||||
}
|
||||
|
||||
Executor::~Executor()
|
||||
@@ -129,7 +131,7 @@ Executor::~Executor()
|
||||
}
|
||||
|
||||
void
|
||||
Executor::handle_updated_entities(bool notify)
|
||||
Executor::trigger_entity_recollect(bool notify)
|
||||
{
|
||||
this->entities_need_rebuild_.store(true);
|
||||
|
||||
@@ -174,11 +176,11 @@ Executor::add_callback_group(
|
||||
this->collector_.add_callback_group(group_ptr);
|
||||
|
||||
try {
|
||||
this->handle_updated_entities(notify);
|
||||
this->trigger_entity_recollect(notify);
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to handle entities update on callback group add: ") + ex.what());
|
||||
"Failed to trigger guard condition on callback group add: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -188,11 +190,11 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
|
||||
this->collector_.add_node(node_ptr);
|
||||
|
||||
try {
|
||||
this->handle_updated_entities(notify);
|
||||
this->trigger_entity_recollect(notify);
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to handle entities update on node add: ") + ex.what());
|
||||
"Failed to trigger guard condition on node add: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -204,11 +206,11 @@ Executor::remove_callback_group(
|
||||
this->collector_.remove_callback_group(group_ptr);
|
||||
|
||||
try {
|
||||
this->handle_updated_entities(notify);
|
||||
this->trigger_entity_recollect(notify);
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to handle entities update on callback group remove: ") + ex.what());
|
||||
"Failed to trigger guard condition on callback group remove: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -224,11 +226,11 @@ Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node
|
||||
this->collector_.remove_node(node_ptr);
|
||||
|
||||
try {
|
||||
this->handle_updated_entities(notify);
|
||||
this->trigger_entity_recollect(notify);
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to handle entities update on node remove: ") + ex.what());
|
||||
"Failed to trigger guard condition on node remove: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -371,7 +373,14 @@ Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
|
||||
// both spin_some and spin_all wait for work at the beginning
|
||||
wait_result_.reset();
|
||||
wait_for_work(std::chrono::milliseconds(0));
|
||||
bool just_waited = true;
|
||||
bool entity_states_fully_polled = true;
|
||||
|
||||
if (entities_need_rebuild_) {
|
||||
// if the last wait triggered a collection rebuild, we need to call
|
||||
// wait_for_work once more, in order to do a collection rebuild and collect
|
||||
// events from the just added entities
|
||||
entity_states_fully_polled = false;
|
||||
}
|
||||
|
||||
// The logic of this while loop is as follows:
|
||||
//
|
||||
@@ -393,12 +402,14 @@ Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
|
||||
AnyExecutable any_exec;
|
||||
if (get_next_ready_executable(any_exec)) {
|
||||
execute_any_executable(any_exec);
|
||||
just_waited = false;
|
||||
// during the execution some entity might got ready therefore we need
|
||||
// to repoll the states of all entities
|
||||
entity_states_fully_polled = false;
|
||||
} else {
|
||||
// if nothing is ready, reset the result to clear it
|
||||
wait_result_.reset();
|
||||
|
||||
if (just_waited) {
|
||||
if (entity_states_fully_polled) {
|
||||
// there was no work after just waiting, always exit in this case
|
||||
// before the exhaustive condition can be checked
|
||||
break;
|
||||
@@ -408,7 +419,13 @@ Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
|
||||
// if exhaustive, wait for work again
|
||||
// this only happens for spin_all; spin_some only waits at the start
|
||||
wait_for_work(std::chrono::milliseconds(0));
|
||||
just_waited = true;
|
||||
entity_states_fully_polled = true;
|
||||
if (entities_need_rebuild_) {
|
||||
// if the last wait triggered a collection rebuild, we need to call
|
||||
// wait_for_work once more, in order to do a collection rebuild and
|
||||
// collect events from the just added entities
|
||||
entity_states_fully_polled = false;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -729,13 +746,33 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
|
||||
// Clear any previous wait result
|
||||
this->wait_result_.reset();
|
||||
|
||||
// we need to make sure that callback groups don't get out of scope
|
||||
// during the wait. As in jazzy, they are not covered by the DynamicStorage,
|
||||
// we explicitly hold them here as a bugfix
|
||||
std::vector<rclcpp::CallbackGroup::SharedPtr> cbgs;
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
|
||||
if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
|
||||
this->collect_entities();
|
||||
}
|
||||
|
||||
auto callback_groups = this->collector_.get_all_callback_groups();
|
||||
cbgs.resize(callback_groups.size());
|
||||
for(const auto & w_ptr : callback_groups) {
|
||||
auto shr_ptr = w_ptr.lock();
|
||||
if(shr_ptr) {
|
||||
cbgs.push_back(std::move(shr_ptr));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this->wait_result_.emplace(wait_set_.wait(timeout));
|
||||
|
||||
// drop references to the callback groups, before trying to execute anything
|
||||
cbgs.clear();
|
||||
|
||||
if (!this->wait_result_ || this->wait_result_->kind() == WaitResultKind::Empty) {
|
||||
RCUTILS_LOG_WARN_NAMED(
|
||||
"rclcpp",
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/executors/executor_notify_waitable.hpp"
|
||||
|
||||
@@ -89,9 +91,9 @@ ExecutorNotifyWaitable::is_ready(const rcl_wait_set_t & wait_set)
|
||||
}
|
||||
|
||||
void
|
||||
ExecutorNotifyWaitable::execute(const std::shared_ptr<void> & /*data*/)
|
||||
ExecutorNotifyWaitable::execute(const std::shared_ptr<void> & data)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(execute_mutex_);
|
||||
(void) data;
|
||||
this->execute_callback_();
|
||||
}
|
||||
|
||||
@@ -147,14 +149,6 @@ ExecutorNotifyWaitable::clear_on_ready_callback()
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
ExecutorNotifyWaitable::set_execute_callback(std::function<void(void)> on_execute_callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(execute_mutex_);
|
||||
execute_callback_ = on_execute_callback;
|
||||
}
|
||||
|
||||
void
|
||||
ExecutorNotifyWaitable::add_guard_condition(rclcpp::GuardCondition::WeakPtr weak_guard_condition)
|
||||
{
|
||||
|
||||
@@ -110,10 +110,29 @@ StaticSingleThreadedExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
|
||||
std::optional<rclcpp::WaitResult<rclcpp::WaitSet>>
|
||||
StaticSingleThreadedExecutor::collect_and_wait(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
// we need to make sure that callback groups don't get out of scope
|
||||
// during the wait. As in jazzy, they are not covered by the DynamicStorage,
|
||||
// we explicitly hold them here as a bugfix
|
||||
std::vector<rclcpp::CallbackGroup::SharedPtr> cbgs;
|
||||
|
||||
if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
|
||||
this->collect_entities();
|
||||
}
|
||||
|
||||
auto callback_groups = this->collector_.get_all_callback_groups();
|
||||
cbgs.resize(callback_groups.size());
|
||||
for(const auto & w_ptr : callback_groups) {
|
||||
auto shr_ptr = w_ptr.lock();
|
||||
if(shr_ptr) {
|
||||
cbgs.push_back(std::move(shr_ptr));
|
||||
}
|
||||
}
|
||||
|
||||
auto wait_result = wait_set_.wait(std::chrono::nanoseconds(timeout));
|
||||
|
||||
// drop references to the callback groups, before trying to execute anything
|
||||
cbgs.clear();
|
||||
|
||||
if (wait_result.kind() == WaitResultKind::Empty) {
|
||||
RCUTILS_LOG_WARN_NAMED(
|
||||
"rclcpp",
|
||||
|
||||
@@ -52,37 +52,29 @@ EventsExecutor::EventsExecutor(
|
||||
timers_manager_ =
|
||||
std::make_shared<rclcpp::experimental::TimersManager>(context_, timer_on_ready_cb);
|
||||
|
||||
entities_need_rebuild_ = false;
|
||||
this->current_entities_collection_ =
|
||||
std::make_shared<rclcpp::executors::ExecutorEntitiesCollection>();
|
||||
|
||||
this->setup_notify_waitable();
|
||||
|
||||
// Ensure that the entities collection is empty (the base class may have added elements
|
||||
// that we are not interested in)
|
||||
this->current_collection_.clear();
|
||||
|
||||
// Make sure that the notify waitable is immediately added to the collection
|
||||
// to avoid missing events
|
||||
this->add_notify_waitable_to_collection(current_collection_.waitables);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::setup_notify_waitable()
|
||||
{
|
||||
// The base class already created this object but the events-executor
|
||||
// needs different callbacks.
|
||||
assert(notify_waitable_ && "The notify waitable should have already been constructed");
|
||||
|
||||
notify_waitable_->set_execute_callback(
|
||||
notify_waitable_ = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(
|
||||
[this]() {
|
||||
// This callback is invoked when:
|
||||
// - the interrupt or shutdown guard condition is triggered:
|
||||
// ---> we need to wake up the executor so that it can terminate
|
||||
// - a node or callback group guard condition is triggered:
|
||||
// ---> the entities collection is changed, we need to update callbacks
|
||||
entities_need_rebuild_ = false;
|
||||
this->handle_updated_entities(false);
|
||||
this->refresh_current_collection_from_callback_groups();
|
||||
});
|
||||
|
||||
// Make sure that the notify waitable is immediately added to the collection
|
||||
// to avoid missing events
|
||||
this->add_notify_waitable_to_collection(current_entities_collection_->waitables);
|
||||
|
||||
notify_waitable_->add_guard_condition(interrupt_guard_condition_);
|
||||
notify_waitable_->add_guard_condition(shutdown_guard_condition_);
|
||||
|
||||
notify_waitable_->set_on_ready_callback(
|
||||
this->create_waitable_callback(notify_waitable_.get()));
|
||||
|
||||
auto notify_waitable_entity_id = notify_waitable_.get();
|
||||
notify_waitable_->set_on_ready_callback(
|
||||
[this, notify_waitable_entity_id](size_t num_events, int waitable_data) {
|
||||
@@ -92,7 +84,7 @@ EventsExecutor::setup_notify_waitable()
|
||||
// For the same reason, if an event of this type has already been pushed but it has not been
|
||||
// processed yet, we avoid pushing additional events.
|
||||
(void)num_events;
|
||||
if (entities_need_rebuild_.exchange(true)) {
|
||||
if (notify_waitable_event_pushed_.exchange(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -100,6 +92,9 @@ EventsExecutor::setup_notify_waitable()
|
||||
{notify_waitable_entity_id, nullptr, waitable_data, ExecutorEventType::WAITABLE_EVENT, 1};
|
||||
this->events_queue_->enqueue(event);
|
||||
});
|
||||
|
||||
this->entities_collector_ =
|
||||
std::make_shared<rclcpp::executors::ExecutorEntitiesCollector>(notify_waitable_);
|
||||
}
|
||||
|
||||
EventsExecutor::~EventsExecutor()
|
||||
@@ -168,6 +163,14 @@ EventsExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhau
|
||||
return false;
|
||||
};
|
||||
|
||||
// If this spin is not exhaustive (e.g. spin_some), we need to explicitly check
|
||||
// if entities need to be rebuilt here rather than letting the notify waitable event do it.
|
||||
// A non-exhaustive spin would not check for work a second time, thus delaying the execution
|
||||
// of some entities to the next invocation of spin.
|
||||
if (!exhaustive) {
|
||||
this->refresh_current_collection_from_callback_groups();
|
||||
}
|
||||
|
||||
// Get the number of events and timers ready at start
|
||||
const size_t ready_events_at_start = events_queue_->size();
|
||||
size_t executed_events = 0;
|
||||
@@ -233,6 +236,46 @@ EventsExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::add_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
||||
{
|
||||
// This field is unused because we don't have to wake up the executor when a node is added.
|
||||
(void) notify;
|
||||
|
||||
// Add node to entities collector
|
||||
this->entities_collector_->add_node(node_ptr);
|
||||
|
||||
this->refresh_current_collection_from_callback_groups();
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify)
|
||||
{
|
||||
this->add_node(node_ptr->get_node_base_interface(), notify);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::remove_node(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
||||
{
|
||||
// This field is unused because we don't have to wake up the executor when a node is removed.
|
||||
(void)notify;
|
||||
|
||||
// Remove node from entities collector.
|
||||
// This will result in un-setting all the event callbacks from its entities.
|
||||
// After this function returns, this executor will not receive any more events associated
|
||||
// to these entities.
|
||||
this->entities_collector_->remove_node(node_ptr);
|
||||
|
||||
this->refresh_current_collection_from_callback_groups();
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify)
|
||||
{
|
||||
this->remove_node(node_ptr->get_node_base_interface(), notify);
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
@@ -242,9 +285,10 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
{
|
||||
rclcpp::ClientBase::SharedPtr client;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
client = this->retrieve_entity(
|
||||
static_cast<const rcl_client_t *>(event.entity_key),
|
||||
current_collection_.clients);
|
||||
current_entities_collection_->clients);
|
||||
}
|
||||
if (client) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
@@ -258,9 +302,10 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
{
|
||||
rclcpp::SubscriptionBase::SharedPtr subscription;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
subscription = this->retrieve_entity(
|
||||
static_cast<const rcl_subscription_t *>(event.entity_key),
|
||||
current_collection_.subscriptions);
|
||||
current_entities_collection_->subscriptions);
|
||||
}
|
||||
if (subscription) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
@@ -273,9 +318,10 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
{
|
||||
rclcpp::ServiceBase::SharedPtr service;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
service = this->retrieve_entity(
|
||||
static_cast<const rcl_service_t *>(event.entity_key),
|
||||
current_collection_.services);
|
||||
current_entities_collection_->services);
|
||||
}
|
||||
if (service) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
@@ -295,9 +341,10 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
{
|
||||
rclcpp::Waitable::SharedPtr waitable;
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
waitable = this->retrieve_entity(
|
||||
static_cast<const rclcpp::Waitable *>(event.entity_key),
|
||||
current_collection_.waitables);
|
||||
current_entities_collection_->waitables);
|
||||
}
|
||||
if (waitable) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
@@ -311,12 +358,71 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::handle_updated_entities(bool notify)
|
||||
EventsExecutor::add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
bool notify)
|
||||
{
|
||||
// This field is unused because we don't have to wake up
|
||||
// the executor when a callback group is added.
|
||||
(void)notify;
|
||||
(void)node_ptr;
|
||||
|
||||
this->entities_collector_->add_callback_group(group_ptr);
|
||||
|
||||
this->refresh_current_collection_from_callback_groups();
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::remove_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr, bool notify)
|
||||
{
|
||||
// This field is unused because we don't have to wake up
|
||||
// the executor when a callback group is removed.
|
||||
(void)notify;
|
||||
|
||||
this->entities_collector_->remove_callback_group(group_ptr);
|
||||
|
||||
this->refresh_current_collection_from_callback_groups();
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
EventsExecutor::get_all_callback_groups()
|
||||
{
|
||||
this->entities_collector_->update_collections();
|
||||
return this->entities_collector_->get_all_callback_groups();
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
EventsExecutor::get_manually_added_callback_groups()
|
||||
{
|
||||
this->entities_collector_->update_collections();
|
||||
return this->entities_collector_->get_manually_added_callback_groups();
|
||||
}
|
||||
|
||||
std::vector<rclcpp::CallbackGroup::WeakPtr>
|
||||
EventsExecutor::get_automatically_added_callback_groups_from_nodes()
|
||||
{
|
||||
this->entities_collector_->update_collections();
|
||||
return this->entities_collector_->get_automatically_added_callback_groups();
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::refresh_current_collection_from_callback_groups()
|
||||
{
|
||||
// Do not rebuild if we don't need to.
|
||||
// A rebuild event could be generated, but then
|
||||
// this function could end up being called from somewhere else
|
||||
// before that event gets processed, for example if
|
||||
// a node or callback group is manually added to the executor.
|
||||
const bool notify_waitable_triggered = notify_waitable_event_pushed_.exchange(false);
|
||||
if (!notify_waitable_triggered && !this->entities_collector_->has_pending()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the new collection
|
||||
this->collector_.update_collections();
|
||||
auto callback_groups = this->collector_.get_all_callback_groups();
|
||||
this->entities_collector_->update_collections();
|
||||
auto callback_groups = this->entities_collector_->get_all_callback_groups();
|
||||
rclcpp::executors::ExecutorEntitiesCollection new_collection;
|
||||
rclcpp::executors::build_entities_collection(callback_groups, new_collection);
|
||||
|
||||
@@ -326,11 +432,14 @@ EventsExecutor::handle_updated_entities(bool notify)
|
||||
// We could explicitly check for the notify waitable ID when we receive a waitable event
|
||||
// but I think that it's better if the waitable was in the collection and it could be
|
||||
// retrieved in the "standard" way.
|
||||
// To do it, we need to add the notify waitable as an entry in the new collection
|
||||
// such that it's neither added or removed (it should have already been added
|
||||
// to the current collection in the constructor)
|
||||
// To do it, we need to add the notify waitable as an entry in both the new and
|
||||
// current collections such that it's neither added or removed.
|
||||
this->add_notify_waitable_to_collection(new_collection.waitables);
|
||||
|
||||
// Acquire lock before modifying the current collection
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
this->add_notify_waitable_to_collection(current_entities_collection_->waitables);
|
||||
|
||||
this->refresh_current_collection(new_collection);
|
||||
}
|
||||
|
||||
@@ -339,14 +448,14 @@ EventsExecutor::refresh_current_collection(
|
||||
const rclcpp::executors::ExecutorEntitiesCollection & new_collection)
|
||||
{
|
||||
// Acquire lock before modifying the current collection
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
|
||||
current_collection_.timers.update(
|
||||
current_entities_collection_->timers.update(
|
||||
new_collection.timers,
|
||||
[this](rclcpp::TimerBase::SharedPtr timer) {timers_manager_->add_timer(timer);},
|
||||
[this](rclcpp::TimerBase::SharedPtr timer) {timers_manager_->remove_timer(timer);});
|
||||
|
||||
current_collection_.subscriptions.update(
|
||||
current_entities_collection_->subscriptions.update(
|
||||
new_collection.subscriptions,
|
||||
[this](auto subscription) {
|
||||
subscription->set_on_new_message_callback(
|
||||
@@ -355,7 +464,7 @@ EventsExecutor::refresh_current_collection(
|
||||
},
|
||||
[](auto subscription) {subscription->clear_on_new_message_callback();});
|
||||
|
||||
current_collection_.clients.update(
|
||||
current_entities_collection_->clients.update(
|
||||
new_collection.clients,
|
||||
[this](auto client) {
|
||||
client->set_on_new_response_callback(
|
||||
@@ -364,7 +473,7 @@ EventsExecutor::refresh_current_collection(
|
||||
},
|
||||
[](auto client) {client->clear_on_new_response_callback();});
|
||||
|
||||
current_collection_.services.update(
|
||||
current_entities_collection_->services.update(
|
||||
new_collection.services,
|
||||
[this](auto service) {
|
||||
service->set_on_new_request_callback(
|
||||
@@ -375,12 +484,12 @@ EventsExecutor::refresh_current_collection(
|
||||
|
||||
// DO WE NEED THIS? WE ARE NOT DOING ANYTHING WITH GUARD CONDITIONS
|
||||
/*
|
||||
current_collection_.guard_conditions.update(new_collection.guard_conditions,
|
||||
current_entities_collection_->guard_conditions.update(new_collection.guard_conditions,
|
||||
[](auto guard_condition) {(void)guard_condition;},
|
||||
[](auto guard_condition) {guard_condition->set_on_trigger_callback(nullptr);});
|
||||
*/
|
||||
|
||||
current_collection_.waitables.update(
|
||||
current_entities_collection_->waitables.update(
|
||||
new_collection.waitables,
|
||||
[this](auto waitable) {
|
||||
waitable->set_on_ready_callback(
|
||||
|
||||
@@ -273,20 +273,22 @@ void TimersManager::run_timers()
|
||||
if (!time_to_sleep.has_value() || (time_to_sleep.value() == std::chrono::nanoseconds::max()) ) {
|
||||
// Wait until notification that timers have been updated
|
||||
timers_cv_.wait(lock, [this]() {return timers_updated_;});
|
||||
|
||||
// Re-heap in case ordering changed due to a cancelled timer
|
||||
// re-activating.
|
||||
TimersHeap locked_heap = weak_timers_heap_.validate_and_lock();
|
||||
locked_heap.heapify();
|
||||
weak_timers_heap_.store(locked_heap);
|
||||
} else if (time_to_sleep.value() != std::chrono::nanoseconds::zero()) {
|
||||
// If time_to_sleep is zero, we immediately execute. Otherwise, wait
|
||||
// until timeout or notification that timers have been updated
|
||||
timers_cv_.wait_for(lock, time_to_sleep.value(), [this]() {return timers_updated_;});
|
||||
}
|
||||
|
||||
// Reset timers updated flag
|
||||
timers_updated_ = false;
|
||||
if (timers_updated_) {
|
||||
// Re-heap in case ordering changed due to a cancelled timer
|
||||
// re-activating.
|
||||
TimersHeap locked_heap = weak_timers_heap_.validate_and_lock();
|
||||
locked_heap.heapify();
|
||||
weak_timers_heap_.store(locked_heap);
|
||||
|
||||
// Reset timers updated flag
|
||||
timers_updated_ = false;
|
||||
}
|
||||
|
||||
// Execute timers
|
||||
this->execute_ready_timers_unsafe();
|
||||
|
||||
@@ -12,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -55,14 +54,6 @@ get_node_logger(const rcl_node_t * node)
|
||||
return rclcpp::get_logger(logger_name);
|
||||
}
|
||||
|
||||
// TODO(ahcorde): Remove deprecated class on the next release (in Rolling after Kilted).
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
rcpputils::fs::path
|
||||
get_logging_directory()
|
||||
{
|
||||
@@ -76,26 +67,6 @@ get_logging_directory()
|
||||
allocator.deallocate(log_dir, allocator.state);
|
||||
return path;
|
||||
}
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
std::filesystem::path
|
||||
get_log_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;
|
||||
}
|
||||
|
||||
Logger
|
||||
Logger::get_child(const std::string & suffix)
|
||||
|
||||
@@ -65,11 +65,11 @@ public:
|
||||
: logger_(node_logging->get_logger()),
|
||||
node_base_(node_base)
|
||||
{
|
||||
rclcpp::ParameterValue enable_param;
|
||||
const std::string enable_param_name = "start_type_description_service";
|
||||
|
||||
bool enabled = false;
|
||||
try {
|
||||
auto enable_param = node_parameters->declare_parameter(
|
||||
if (!node_parameters->has_parameter(enable_param_name)) {
|
||||
enable_param = node_parameters->declare_parameter(
|
||||
enable_param_name,
|
||||
rclcpp::ParameterValue(true),
|
||||
rcl_interfaces::msg::ParameterDescriptor()
|
||||
@@ -77,13 +77,21 @@ public:
|
||||
.set__type(rclcpp::PARAMETER_BOOL)
|
||||
.set__description("Start the ~/get_type_description service for this node.")
|
||||
.set__read_only(true));
|
||||
enabled = enable_param.get<bool>();
|
||||
} catch (const rclcpp::exceptions::InvalidParameterTypeException & exc) {
|
||||
RCLCPP_ERROR(logger_, "%s", exc.what());
|
||||
throw;
|
||||
} else {
|
||||
enable_param = node_parameters->get_parameter(enable_param_name).get_parameter_value();
|
||||
}
|
||||
if (enable_param.get_type() != rclcpp::PARAMETER_BOOL) {
|
||||
RCLCPP_ERROR(
|
||||
logger_,
|
||||
"Invalid type '%s' for parameter 'start_type_description_service', should be 'bool'",
|
||||
rclcpp::to_string(enable_param.get_type()).c_str());
|
||||
std::ostringstream ss;
|
||||
ss << "Wrong parameter type, parameter {" << enable_param_name << "} is of type {bool}, "
|
||||
<< "setting it to {" << to_string(enable_param.get_type()) << "} is not allowed.";
|
||||
throw rclcpp::exceptions::InvalidParameterTypeException(enable_param_name, ss.str());
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
if (enable_param.get<bool>()) {
|
||||
auto * rcl_node = node_base->get_rcl_node_handle();
|
||||
std::shared_ptr<rcl_service_t> rcl_srv(
|
||||
new rcl_service_t,
|
||||
|
||||
@@ -80,6 +80,7 @@ NodeOptions::operator=(const NodeOptions & other)
|
||||
this->clock_type_ = other.clock_type_;
|
||||
this->clock_qos_ = other.clock_qos_;
|
||||
this->use_clock_thread_ = other.use_clock_thread_;
|
||||
this->enable_logger_service_ = other.enable_logger_service_;
|
||||
this->parameter_event_qos_ = other.parameter_event_qos_;
|
||||
this->rosout_qos_ = other.rosout_qos_;
|
||||
this->parameter_event_publisher_options_ = other.parameter_event_publisher_options_;
|
||||
|
||||
@@ -28,6 +28,9 @@ ParameterEventsFilter::ParameterEventsFilter(
|
||||
const std::vector<EventType> & types)
|
||||
: event_(event)
|
||||
{
|
||||
if (!event) {
|
||||
throw std::invalid_argument("event cannot be null");
|
||||
}
|
||||
if (std::find(types.begin(), types.end(), EventType::NEW) != types.end()) {
|
||||
for (auto & new_parameter : event->new_parameters) {
|
||||
if (std::find(names.begin(), names.end(), new_parameter.name) != names.end()) {
|
||||
|
||||
@@ -81,7 +81,15 @@ rclcpp::parameter_map_from(const rcl_params_t * const c_params, const char * nod
|
||||
throw InvalidParametersException(message);
|
||||
}
|
||||
const rcl_variant_t * const c_param_value = &(c_params_node->parameter_values[p]);
|
||||
params_node.emplace_back(c_param_name, parameter_value_from(c_param_value));
|
||||
ParameterValue value;
|
||||
try {
|
||||
value = parameter_value_from(c_param_value);
|
||||
} catch (const InvalidParameterValueException & e) {
|
||||
throw InvalidParameterValueException(
|
||||
std::string("parameter_value_from failed for parameter '") +
|
||||
c_param_name + "': " + e.what());
|
||||
}
|
||||
params_node.emplace_back(c_param_name, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,8 +69,10 @@ QoSInitialization::from_rmw(const rmw_qos_profile_t & rmw_qos)
|
||||
return KeepLast(rmw_qos.depth, false);
|
||||
case RMW_QOS_POLICY_HISTORY_KEEP_LAST:
|
||||
case RMW_QOS_POLICY_HISTORY_UNKNOWN:
|
||||
default:
|
||||
return KeepLast(rmw_qos.depth);
|
||||
default:
|
||||
throw std::invalid_argument(
|
||||
"Invalid history policy enum value passed to QoSInitialization::from_rmw");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,6 +71,12 @@ Rate::sleep()
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Rate::is_steady() const
|
||||
{
|
||||
return clock_->get_clock_type() == RCL_STEADY_TIME;
|
||||
}
|
||||
|
||||
rcl_clock_type_t
|
||||
Rate::get_type() const
|
||||
{
|
||||
|
||||
@@ -36,7 +36,6 @@ SerializationBase::SerializationBase(const rosidl_message_type_support_t * type_
|
||||
void SerializationBase::serialize_message(
|
||||
const void * ros_message, SerializedMessage * serialized_message) const
|
||||
{
|
||||
rcpputils::check_true(nullptr != type_support_, "Typesupport is nullpointer.");
|
||||
rcpputils::check_true(nullptr != ros_message, "ROS message is nullpointer.");
|
||||
rcpputils::check_true(nullptr != serialized_message, "Serialized message is nullpointer.");
|
||||
|
||||
@@ -52,7 +51,6 @@ void SerializationBase::serialize_message(
|
||||
void SerializationBase::deserialize_message(
|
||||
const SerializedMessage * serialized_message, void * ros_message) const
|
||||
{
|
||||
rcpputils::check_true(nullptr != type_support_, "Typesupport is nullpointer.");
|
||||
rcpputils::check_true(nullptr != serialized_message, "Serialized message is nullpointer.");
|
||||
rcpputils::check_true(
|
||||
0u != serialized_message->capacity(),
|
||||
|
||||
@@ -26,8 +26,13 @@ namespace rclcpp
|
||||
|
||||
inline void copy_rcl_message(const rcl_serialized_message_t & from, rcl_serialized_message_t & to)
|
||||
{
|
||||
const auto ret = rmw_serialized_message_init(
|
||||
&to, from.buffer_capacity, &from.allocator);
|
||||
auto ret = RCL_RET_ERROR;
|
||||
if (nullptr == to.buffer) {
|
||||
ret = rmw_serialized_message_init(&to, from.buffer_capacity, &from.allocator);
|
||||
} else {
|
||||
ret = rmw_serialized_message_resize(&to, from.buffer_capacity);
|
||||
}
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
@@ -78,7 +83,6 @@ SerializedMessage::SerializedMessage(rcl_serialized_message_t && other)
|
||||
SerializedMessage & SerializedMessage::operator=(const SerializedMessage & other)
|
||||
{
|
||||
if (this != &other) {
|
||||
serialized_message_ = rmw_get_zero_initialized_serialized_message();
|
||||
copy_rcl_message(other.serialized_message_, serialized_message_);
|
||||
}
|
||||
|
||||
@@ -88,7 +92,6 @@ SerializedMessage & SerializedMessage::operator=(const SerializedMessage & other
|
||||
SerializedMessage & SerializedMessage::operator=(const rcl_serialized_message_t & other)
|
||||
{
|
||||
if (&serialized_message_ != &other) {
|
||||
serialized_message_ = rmw_get_zero_initialized_serialized_message();
|
||||
copy_rcl_message(other, serialized_message_);
|
||||
}
|
||||
|
||||
@@ -98,6 +101,14 @@ SerializedMessage & SerializedMessage::operator=(const rcl_serialized_message_t
|
||||
SerializedMessage & SerializedMessage::operator=(SerializedMessage && other)
|
||||
{
|
||||
if (this != &other) {
|
||||
if (nullptr != serialized_message_.buffer) {
|
||||
const auto fini_ret = rmw_serialized_message_fini(&serialized_message_);
|
||||
if (RCL_RET_OK != fini_ret) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger("rclcpp"),
|
||||
"Failed to destroy serialized message: %s", rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
serialized_message_ =
|
||||
std::exchange(other.serialized_message_, rmw_get_zero_initialized_serialized_message());
|
||||
}
|
||||
@@ -108,6 +119,14 @@ SerializedMessage & SerializedMessage::operator=(SerializedMessage && other)
|
||||
SerializedMessage & SerializedMessage::operator=(rcl_serialized_message_t && other)
|
||||
{
|
||||
if (&serialized_message_ != &other) {
|
||||
if (nullptr != serialized_message_.buffer) {
|
||||
const auto fini_ret = rmw_serialized_message_fini(&serialized_message_);
|
||||
if (RCL_RET_OK != fini_ret) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger("rclcpp"),
|
||||
"Failed to destroy serialized message: %s", rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
serialized_message_ =
|
||||
std::exchange(other, rmw_get_zero_initialized_serialized_message());
|
||||
}
|
||||
|
||||
@@ -68,7 +68,6 @@ void
|
||||
SignalHandler::signal_handler(
|
||||
int signum, siginfo_t * siginfo, void * context)
|
||||
{
|
||||
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum);
|
||||
auto & instance = SignalHandler::get_global_signal_handler();
|
||||
|
||||
auto old_signal_handler = instance.get_old_signal_handler(signum);
|
||||
@@ -91,7 +90,6 @@ SignalHandler::signal_handler(
|
||||
void
|
||||
SignalHandler::signal_handler(int signum)
|
||||
{
|
||||
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum);
|
||||
auto & instance = SignalHandler::get_global_signal_handler();
|
||||
auto old_signal_handler = instance.get_old_signal_handler(signum);
|
||||
if (
|
||||
@@ -249,9 +247,6 @@ SignalHandler::signal_handler_common()
|
||||
{
|
||||
auto & instance = SignalHandler::get_global_signal_handler();
|
||||
instance.signal_received_.store(true);
|
||||
RCLCPP_DEBUG(
|
||||
get_logger(),
|
||||
"signal_handler(): notifying deferred signal handler");
|
||||
instance.notify_signal_handler();
|
||||
}
|
||||
|
||||
@@ -260,6 +255,7 @@ SignalHandler::deferred_signal_handler()
|
||||
{
|
||||
while (true) {
|
||||
if (signal_received_.exchange(false)) {
|
||||
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(SIGINT/SIGTERM)");
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): shutting down");
|
||||
for (auto context_ptr : rclcpp::get_contexts()) {
|
||||
if (context_ptr->get_init_options().shutdown_on_signal) {
|
||||
|
||||
@@ -60,10 +60,6 @@ Time::Time(int32_t seconds, uint32_t nanoseconds, rcl_clock_type_t clock_type)
|
||||
Time::Time(int64_t nanoseconds, rcl_clock_type_t clock_type)
|
||||
: rcl_time_(init_time_point(clock_type))
|
||||
{
|
||||
if (nanoseconds < 0) {
|
||||
throw std::runtime_error("cannot store a negative time point in rclcpp::Time");
|
||||
}
|
||||
|
||||
rcl_time_.nanoseconds = nanoseconds;
|
||||
}
|
||||
|
||||
@@ -253,9 +249,6 @@ Time::operator+=(const rclcpp::Duration & rhs)
|
||||
}
|
||||
|
||||
rcl_time_.nanoseconds += rhs.nanoseconds();
|
||||
if (rcl_time_.nanoseconds < 0) {
|
||||
throw std::runtime_error("cannot store a negative time point in rclcpp::Time");
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
@@ -271,9 +264,6 @@ Time::operator-=(const rclcpp::Duration & rhs)
|
||||
}
|
||||
|
||||
rcl_time_.nanoseconds -= rhs.nanoseconds();
|
||||
if (rcl_time_.nanoseconds < 0) {
|
||||
throw std::runtime_error("cannot store a negative time point in rclcpp::Time");
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -35,62 +35,6 @@ namespace rclcpp
|
||||
namespace
|
||||
{
|
||||
|
||||
// Look for the library in the ament prefix paths.
|
||||
std::string get_typesupport_library_path(
|
||||
const std::string & package_name, const std::string & typesupport_identifier)
|
||||
{
|
||||
const char * dynamic_library_folder;
|
||||
#ifdef _WIN32
|
||||
dynamic_library_folder = "/bin/";
|
||||
#elif __APPLE__
|
||||
dynamic_library_folder = "/lib/";
|
||||
#else
|
||||
dynamic_library_folder = "/lib/";
|
||||
#endif
|
||||
|
||||
std::string package_prefix;
|
||||
try {
|
||||
package_prefix = ament_index_cpp::get_package_prefix(package_name);
|
||||
} catch (ament_index_cpp::PackageNotFoundError & e) {
|
||||
throw std::runtime_error(e.what());
|
||||
}
|
||||
|
||||
const std::string library_path = rcpputils::path_for_library(
|
||||
package_prefix + dynamic_library_folder,
|
||||
package_name + "__" + typesupport_identifier);
|
||||
if (library_path.empty()) {
|
||||
throw std::runtime_error(
|
||||
"Typesupport library for " + package_name + " does not exist in '" + package_prefix +
|
||||
"'.");
|
||||
}
|
||||
return library_path;
|
||||
}
|
||||
|
||||
std::tuple<std::string, std::string, std::string>
|
||||
extract_type_identifier(const std::string & full_type)
|
||||
{
|
||||
char type_separator = '/';
|
||||
auto sep_position_back = full_type.find_last_of(type_separator);
|
||||
auto sep_position_front = full_type.find_first_of(type_separator);
|
||||
if (sep_position_back == std::string::npos ||
|
||||
sep_position_back == 0 ||
|
||||
sep_position_back == full_type.length() - 1)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Message type is not of the form package/type and cannot be processed");
|
||||
}
|
||||
|
||||
std::string package_name = full_type.substr(0, sep_position_front);
|
||||
std::string middle_module = "";
|
||||
if (sep_position_back - sep_position_front > 0) {
|
||||
middle_module =
|
||||
full_type.substr(sep_position_front + 1, sep_position_back - sep_position_front - 1);
|
||||
}
|
||||
std::string type_name = full_type.substr(sep_position_back + 1);
|
||||
|
||||
return std::make_tuple(package_name, middle_module, type_name);
|
||||
}
|
||||
|
||||
const void * get_typesupport_handle_impl(
|
||||
const std::string & type,
|
||||
const std::string & typesupport_identifier,
|
||||
@@ -131,6 +75,61 @@ const void * get_typesupport_handle_impl(
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
std::tuple<std::string, std::string, std::string>
|
||||
extract_type_identifier(const std::string & full_type)
|
||||
{
|
||||
char type_separator = '/';
|
||||
auto sep_position_back = full_type.find_last_of(type_separator);
|
||||
auto sep_position_front = full_type.find_first_of(type_separator);
|
||||
if (sep_position_back == std::string::npos ||
|
||||
sep_position_back == 0 ||
|
||||
sep_position_back == full_type.length() - 1)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Message type is not of the form package/type and cannot be processed");
|
||||
}
|
||||
|
||||
std::string package_name = full_type.substr(0, sep_position_front);
|
||||
std::string middle_module = "";
|
||||
if (sep_position_back - sep_position_front > 0) {
|
||||
middle_module =
|
||||
full_type.substr(sep_position_front + 1, sep_position_back - sep_position_front - 1);
|
||||
}
|
||||
std::string type_name = full_type.substr(sep_position_back + 1);
|
||||
|
||||
return std::make_tuple(package_name, middle_module, type_name);
|
||||
}
|
||||
|
||||
std::string get_typesupport_library_path(
|
||||
const std::string & package_name, const std::string & typesupport_identifier)
|
||||
{
|
||||
const char * dynamic_library_folder;
|
||||
#ifdef _WIN32
|
||||
dynamic_library_folder = "/bin/";
|
||||
#elif __APPLE__
|
||||
dynamic_library_folder = "/lib/";
|
||||
#else
|
||||
dynamic_library_folder = "/lib/";
|
||||
#endif
|
||||
|
||||
std::string package_prefix;
|
||||
try {
|
||||
package_prefix = ament_index_cpp::get_package_prefix(package_name);
|
||||
} catch (ament_index_cpp::PackageNotFoundError & e) {
|
||||
throw std::runtime_error(e.what());
|
||||
}
|
||||
|
||||
const std::string library_path = rcpputils::path_for_library(
|
||||
package_prefix + dynamic_library_folder,
|
||||
package_name + "__" + typesupport_identifier);
|
||||
if (library_path.empty()) {
|
||||
throw std::runtime_error(
|
||||
"Typesupport library for " + package_name + " does not exist in '" + package_prefix +
|
||||
"'.");
|
||||
}
|
||||
return library_path;
|
||||
}
|
||||
|
||||
std::shared_ptr<rcpputils::SharedLibrary>
|
||||
get_typesupport_library(const std::string & type, const std::string & typesupport_identifier)
|
||||
{
|
||||
|
||||
@@ -87,21 +87,81 @@ Waitable::clear_on_ready_callback()
|
||||
"want to use it and make sure to call it on the waitable destructor.");
|
||||
}
|
||||
|
||||
bool
|
||||
Waitable::is_ready(const rcl_wait_set_t & wait_set)
|
||||
void
|
||||
Waitable::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
return this->is_ready(wait_set);
|
||||
this->add_to_wait_set(*wait_set);
|
||||
}
|
||||
|
||||
void
|
||||
Waitable::add_to_wait_set(rcl_wait_set_t & wait_set)
|
||||
{
|
||||
this->add_to_wait_set(wait_set);
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
this->add_to_wait_set(&wait_set);
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
Waitable::is_ready(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
const rcl_wait_set_t & const_wait_set_ref = *wait_set;
|
||||
return this->is_ready(const_wait_set_ref);
|
||||
}
|
||||
|
||||
bool
|
||||
Waitable::is_ready(const rcl_wait_set_t & wait_set)
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
// note this const cast is only required to support a deprecated function
|
||||
return this->is_ready(&const_cast<rcl_wait_set_t &>(wait_set));
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
Waitable::execute(std::shared_ptr<void> & data)
|
||||
{
|
||||
const std::shared_ptr<void> & const_data = data;
|
||||
this->execute(const_data);
|
||||
}
|
||||
|
||||
void
|
||||
Waitable::execute(const std::shared_ptr<void> & data)
|
||||
{
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
// note this const cast is only required to support a deprecated function
|
||||
this->execute(const_cast<std::shared_ptr<void> &>(data));
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -31,7 +31,6 @@ endif()
|
||||
ament_add_gtest(
|
||||
test_exceptions
|
||||
exceptions/test_exceptions.cpp)
|
||||
ament_add_test_label(test_exceptions mimick)
|
||||
if(TARGET test_exceptions)
|
||||
target_link_libraries(test_exceptions ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
@@ -53,15 +52,18 @@ if(TARGET test_any_subscription_callback)
|
||||
target_link_libraries(test_any_subscription_callback ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
ament_add_gtest(test_client test_client.cpp)
|
||||
ament_add_test_label(test_client mimick)
|
||||
if(TARGET test_client)
|
||||
target_link_libraries(test_client ${PROJECT_NAME} mimick ${rcl_interfaces_TARGETS} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
ament_add_gtest(test_clock test_clock.cpp)
|
||||
ament_add_test_label(test_clock mimick)
|
||||
if(TARGET test_clock)
|
||||
target_link_libraries(test_clock ${PROJECT_NAME} mimick ${rcl_interfaces_TARGETS} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
ament_add_gtest(test_clock_conditional test_clock_conditional.cpp)
|
||||
ament_add_test_label(test_clock_conditional mimick)
|
||||
if(TARGET test_clock_conditional)
|
||||
target_link_libraries(test_clock_conditional ${PROJECT_NAME} mimick ${rcl_interfaces_TARGETS} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
ament_add_gtest(test_copy_all_parameter_values test_copy_all_parameter_values.cpp)
|
||||
if(TARGET test_copy_all_parameter_values)
|
||||
target_link_libraries(test_copy_all_parameter_values ${PROJECT_NAME})
|
||||
@@ -72,7 +74,6 @@ if(TARGET test_create_timer)
|
||||
target_include_directories(test_create_timer PRIVATE ./)
|
||||
endif()
|
||||
ament_add_gtest(test_generic_client test_generic_client.cpp)
|
||||
ament_add_test_label(test_generic_client mimick)
|
||||
if(TARGET test_generic_client)
|
||||
target_link_libraries(test_generic_client ${PROJECT_NAME}
|
||||
mimick
|
||||
@@ -84,7 +85,6 @@ if(TARGET test_generic_client)
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_client_common test_client_common.cpp)
|
||||
ament_add_test_label(test_client_common mimick)
|
||||
if(TARGET test_client_common)
|
||||
target_link_libraries(test_client_common ${PROJECT_NAME}
|
||||
mimick
|
||||
@@ -99,8 +99,18 @@ ament_add_gtest(test_create_subscription test_create_subscription.cpp)
|
||||
if(TARGET test_create_subscription)
|
||||
target_link_libraries(test_create_subscription ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
function(test_add_callback_groups_to_executor_for_rmw_implementation)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
ament_add_gmock(test_add_callback_groups_to_executor${target_suffix} test_add_callback_groups_to_executor.cpp
|
||||
ENV ${rmw_implementation_env_var}
|
||||
TIMEOUT 120
|
||||
)
|
||||
if(TARGET test_add_callback_groups_to_executor${target_suffix})
|
||||
target_link_libraries(test_add_callback_groups_to_executor${target_suffix} ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
endfunction()
|
||||
call_for_each_rmw_implementation(test_add_callback_groups_to_executor_for_rmw_implementation)
|
||||
ament_add_gtest(test_expand_topic_or_service_name test_expand_topic_or_service_name.cpp)
|
||||
ament_add_test_label(test_expand_topic_or_service_name mimick)
|
||||
if(TARGET test_expand_topic_or_service_name)
|
||||
target_link_libraries(test_expand_topic_or_service_name ${PROJECT_NAME} mimick rcl::rcl rmw::rmw)
|
||||
endif()
|
||||
@@ -132,7 +142,6 @@ if(TARGET test_intra_process_buffer)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_loaned_message test_loaned_message.cpp)
|
||||
ament_add_test_label(test_loaned_message mimick)
|
||||
target_link_libraries(test_loaned_message ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
|
||||
|
||||
ament_add_gtest(test_memory_strategy test_memory_strategy.cpp)
|
||||
@@ -142,7 +151,6 @@ ament_add_gtest(test_message_memory_strategy test_message_memory_strategy.cpp)
|
||||
target_link_libraries(test_message_memory_strategy ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
|
||||
ament_add_gtest(test_node test_node.cpp TIMEOUT 240)
|
||||
ament_add_test_label(test_node mimick)
|
||||
if(TARGET test_node)
|
||||
target_link_libraries(test_node ${PROJECT_NAME} mimick rcpputils::rcpputils rmw::rmw ${test_msgs_TARGETS})
|
||||
endif()
|
||||
@@ -154,7 +162,6 @@ if(TARGET test_node_interfaces__get_node_interfaces)
|
||||
endif()
|
||||
ament_add_gtest(test_node_interfaces__node_base
|
||||
node_interfaces/test_node_base.cpp)
|
||||
ament_add_test_label(test_node_interfaces__node_base mimick)
|
||||
if(TARGET test_node_interfaces__node_base)
|
||||
target_link_libraries(test_node_interfaces__node_base ${PROJECT_NAME} mimick rcl::rcl rmw::rmw)
|
||||
endif()
|
||||
@@ -166,7 +173,6 @@ endif()
|
||||
ament_add_gtest(test_node_interfaces__node_graph
|
||||
node_interfaces/test_node_graph.cpp
|
||||
TIMEOUT 120)
|
||||
ament_add_test_label(test_node_interfaces__node_graph mimick)
|
||||
if(TARGET test_node_interfaces__node_graph)
|
||||
target_link_libraries(test_node_interfaces__node_graph ${PROJECT_NAME} mimick rcl::rcl ${test_msgs_TARGETS})
|
||||
endif()
|
||||
@@ -177,25 +183,21 @@ if(TARGET test_node_interfaces__node_interfaces)
|
||||
endif()
|
||||
ament_add_gtest(test_node_interfaces__node_parameters
|
||||
node_interfaces/test_node_parameters.cpp)
|
||||
ament_add_test_label(test_node_interfaces__node_parameters mimick)
|
||||
if(TARGET test_node_interfaces__node_parameters)
|
||||
target_link_libraries(test_node_interfaces__node_parameters ${PROJECT_NAME} mimick rcpputils::rcpputils)
|
||||
endif()
|
||||
ament_add_gtest(test_node_interfaces__node_services
|
||||
node_interfaces/test_node_services.cpp)
|
||||
ament_add_test_label(test_node_interfaces__node_services mimick)
|
||||
if(TARGET test_node_interfaces__node_services)
|
||||
target_link_libraries(test_node_interfaces__node_services ${PROJECT_NAME} mimick rcl::rcl)
|
||||
endif()
|
||||
ament_add_gtest(test_node_interfaces__node_timers
|
||||
node_interfaces/test_node_timers.cpp)
|
||||
ament_add_test_label(test_node_interfaces__node_timers mimick)
|
||||
if(TARGET test_node_interfaces__node_timers)
|
||||
target_link_libraries(test_node_interfaces__node_timers ${PROJECT_NAME} mimick rcl::rcl)
|
||||
endif()
|
||||
ament_add_gtest(test_node_interfaces__node_topics
|
||||
node_interfaces/test_node_topics.cpp)
|
||||
ament_add_test_label(test_node_interfaces__node_topics mimick)
|
||||
if(TARGET test_node_interfaces__node_topics)
|
||||
target_link_libraries(test_node_interfaces__node_topics ${PROJECT_NAME} mimick rcl::rcl ${test_msgs_TARGETS})
|
||||
endif()
|
||||
@@ -206,7 +208,6 @@ if(TARGET test_node_interfaces__node_type_descriptions)
|
||||
endif()
|
||||
ament_add_gtest(test_node_interfaces__node_waitables
|
||||
node_interfaces/test_node_waitables.cpp)
|
||||
ament_add_test_label(test_node_interfaces__node_waitables mimick)
|
||||
if(TARGET test_node_interfaces__node_waitables)
|
||||
target_link_libraries(test_node_interfaces__node_waitables ${PROJECT_NAME} mimick rcl::rcl)
|
||||
endif()
|
||||
@@ -242,12 +243,10 @@ if(TARGET test_node_global_args)
|
||||
target_link_libraries(test_node_global_args ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_node_options test_node_options.cpp)
|
||||
ament_add_test_label(test_node_options mimick)
|
||||
if(TARGET test_node_options)
|
||||
target_link_libraries(test_node_options ${PROJECT_NAME} mimick rcl::rcl)
|
||||
endif()
|
||||
ament_add_gtest(test_init_options test_init_options.cpp)
|
||||
ament_add_test_label(test_init_options mimick)
|
||||
if(TARGET test_init_options)
|
||||
target_link_libraries(test_init_options ${PROJECT_NAME} mimick rcl::rcl)
|
||||
endif()
|
||||
@@ -276,7 +275,6 @@ if(TARGET test_parameter_map)
|
||||
target_link_libraries(test_parameter_map ${PROJECT_NAME} rcl::rcl rcl_yaml_param_parser::rcl_yaml_param_parser rcutils::rcutils)
|
||||
endif()
|
||||
ament_add_gtest(test_publisher test_publisher.cpp TIMEOUT 120)
|
||||
ament_add_test_label(test_publisher mimick)
|
||||
if(TARGET test_publisher)
|
||||
target_link_libraries(test_publisher ${PROJECT_NAME} mimick rcl::rcl rcutils::rcutils ${test_msgs_TARGETS})
|
||||
endif()
|
||||
@@ -326,6 +324,27 @@ if(TARGET test_qos)
|
||||
rmw::rmw
|
||||
)
|
||||
endif()
|
||||
function(test_generic_pubsub_for_rmw_implementation)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
ament_add_gmock(test_generic_pubsub${target_suffix} test_generic_pubsub.cpp
|
||||
ENV ${rmw_implementation_env_var}
|
||||
)
|
||||
if(TARGET test_generic_pubsub${target_suffix})
|
||||
target_link_libraries(test_generic_pubsub${target_suffix} ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
|
||||
endif()
|
||||
endfunction()
|
||||
call_for_each_rmw_implementation(test_generic_pubsub_for_rmw_implementation)
|
||||
|
||||
function(test_qos_event_for_rmw_implementation)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
ament_add_gmock(test_qos_event${target_suffix} test_qos_event.cpp
|
||||
ENV ${rmw_implementation_env_var}
|
||||
)
|
||||
if(TARGET test_qos_event${target_suffix})
|
||||
target_link_libraries(test_qos_event${target_suffix} ${PROJECT_NAME} mimick rcutils::rcutils rmw::rmw ${test_msgs_TARGETS})
|
||||
endif()
|
||||
endfunction()
|
||||
call_for_each_rmw_implementation(test_qos_event_for_rmw_implementation)
|
||||
|
||||
ament_add_gmock(test_qos_overriding_options test_qos_overriding_options.cpp)
|
||||
if(TARGET test_qos_overriding_options)
|
||||
@@ -348,18 +367,15 @@ if(TARGET test_serialized_message)
|
||||
target_link_libraries(test_serialized_message ${PROJECT_NAME} rcpputils::rcpputils ${test_msgs_TARGETS})
|
||||
endif()
|
||||
ament_add_gtest(test_service test_service.cpp)
|
||||
ament_add_test_label(test_service mimick)
|
||||
if(TARGET test_service)
|
||||
target_link_libraries(test_service ${PROJECT_NAME} mimick ${rcl_interfaces_TARGES} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
ament_add_gmock(test_service_introspection test_service_introspection.cpp)
|
||||
ament_add_test_label(test_service_introspection mimick)
|
||||
if(TARGET test_service_introspection)
|
||||
target_link_libraries(test_service_introspection ${PROJECT_NAME} mimick ${service_msgs_TARGETS} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
# Creating and destroying nodes is slow with Connext, so this needs larger timeout.
|
||||
ament_add_gtest(test_subscription test_subscription.cpp TIMEOUT 120)
|
||||
ament_add_test_label(test_subscription mimick)
|
||||
if(TARGET test_subscription)
|
||||
target_link_libraries(test_subscription ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
|
||||
endif()
|
||||
@@ -410,7 +426,6 @@ endif()
|
||||
|
||||
ament_add_gtest(test_timer test_timer.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
ament_add_test_label(test_timer mimick)
|
||||
if(TARGET test_timer)
|
||||
target_link_libraries(test_timer ${PROJECT_NAME} mimick rcl::rcl)
|
||||
endif()
|
||||
@@ -429,7 +444,6 @@ endif()
|
||||
|
||||
ament_add_gtest(test_utilities test_utilities.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
ament_add_test_label(test_utilities mimick)
|
||||
if(TARGET test_utilities)
|
||||
target_link_libraries(test_utilities ${PROJECT_NAME} mimick rcl::rcl)
|
||||
endif()
|
||||
@@ -496,13 +510,22 @@ ament_add_gtest(
|
||||
executors/test_waitable.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
TIMEOUT 180)
|
||||
if(TARGET test_executors)
|
||||
if(TARGET test_executors_busy_waiting)
|
||||
target_link_libraries(test_executors_busy_waiting ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(
|
||||
test_executors_warmup
|
||||
executors/test_executors_warmup.cpp
|
||||
executors/test_waitable.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
TIMEOUT 180)
|
||||
if(TARGET test_executors_warmup)
|
||||
target_link_libraries(test_executors_warmup ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_static_single_threaded_executor executors/test_static_single_threaded_executor.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
ament_add_test_label(test_static_single_threaded_executor mimick)
|
||||
if(TARGET test_static_single_threaded_executor)
|
||||
target_link_libraries(test_static_single_threaded_executor ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
|
||||
endif()
|
||||
@@ -521,7 +544,6 @@ endif()
|
||||
|
||||
ament_add_gtest(test_executor_notify_waitable executors/test_executor_notify_waitable.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}" TIMEOUT 120)
|
||||
ament_add_test_label(test_executor_notify_waitable mimick)
|
||||
if(TARGET test_executor_notify_waitable)
|
||||
target_link_libraries(test_executor_notify_waitable ${PROJECT_NAME} mimick rcpputils::rcpputils)
|
||||
endif()
|
||||
@@ -539,7 +561,6 @@ endif()
|
||||
|
||||
ament_add_gtest(test_guard_condition test_guard_condition.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
ament_add_test_label(test_guard_condition mimick)
|
||||
if(TARGET test_guard_condition)
|
||||
target_link_libraries(test_guard_condition ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
@@ -573,7 +594,6 @@ if(TARGET test_dynamic_storage)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_storage_policy_common wait_set_policies/test_storage_policy_common.cpp)
|
||||
ament_add_test_label(test_storage_policy_common mimick)
|
||||
if(TARGET test_storage_policy_common)
|
||||
target_link_libraries(test_storage_policy_common ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
|
||||
endif()
|
||||
@@ -606,62 +626,24 @@ endif()
|
||||
ament_add_gtest(test_executor test_executor.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
TIMEOUT 120)
|
||||
ament_add_test_label(test_executor mimick)
|
||||
if(TARGET test_executor)
|
||||
target_link_libraries(test_executor ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_graph_listener test_graph_listener.cpp)
|
||||
ament_add_test_label(test_graph_listener mimick)
|
||||
if(TARGET test_graph_listener)
|
||||
target_link_libraries(test_graph_listener ${PROJECT_NAME} mimick)
|
||||
endif()
|
||||
|
||||
ament_add_gmock_executable(test_qos_event test_qos_event.cpp)
|
||||
if(TARGET test_qos_event)
|
||||
target_link_libraries(test_qos_event ${PROJECT_NAME} mimick rcutils::rcutils rmw::rmw ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
ament_add_gmock_executable(test_generic_pubsub test_generic_pubsub.cpp)
|
||||
if(TARGET test_generic_pubsub)
|
||||
target_link_libraries(test_generic_pubsub ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
ament_add_gmock_executable(test_add_callback_groups_to_executor test_add_callback_groups_to_executor.cpp)
|
||||
if(TARGET test_add_callback_groups_to_executor)
|
||||
target_link_libraries(test_add_callback_groups_to_executor ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
ament_add_gmock_executable(test_subscription_content_filter test_subscription_content_filter.cpp)
|
||||
if(TARGET test_subscription_content_filter)
|
||||
target_link_libraries(test_subscription_content_filter ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
function(test_on_all_rmws)
|
||||
function(test_subscription_content_filter_for_rmw_implementation)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
|
||||
ament_add_gmock_test(test_qos_event
|
||||
TEST_NAME test_qos_event${target_suffix}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
)
|
||||
ament_add_test_label(test_qos_event${target_suffix} mimick)
|
||||
|
||||
ament_add_gmock_test(test_generic_pubsub
|
||||
TEST_NAME test_generic_pubsub${target_suffix}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
)
|
||||
|
||||
ament_add_gmock_test(test_add_callback_groups_to_executor
|
||||
TEST_NAME test_add_callback_groups_to_executor${target_suffix}
|
||||
ament_add_gmock(test_subscription_content_filter${target_suffix}
|
||||
test_subscription_content_filter.cpp
|
||||
ENV ${rmw_implementation_env_var}
|
||||
TIMEOUT 120
|
||||
)
|
||||
|
||||
ament_add_gmock_test(test_subscription_content_filter
|
||||
TEST_NAME test_subscription_content_filter${target_suffix}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
TIMEOUT 120
|
||||
)
|
||||
ament_add_test_label(test_subscription_content_filter${target_suffix} mimick)
|
||||
if(TARGET test_subscription_content_filter${target_suffix})
|
||||
target_link_libraries(test_subscription_content_filter${target_suffix} ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
|
||||
endif()
|
||||
endfunction()
|
||||
call_for_each_rmw_implementation(test_on_all_rmws)
|
||||
call_for_each_rmw_implementation(test_subscription_content_filter_for_rmw_implementation)
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
|
||||
|
||||
@@ -76,8 +76,7 @@ TEST_F(TestExecutorNotifyWaitable, wait) {
|
||||
std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(on_execute_callback);
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto notify_guard_condition =
|
||||
node->get_node_base_interface()->get_shared_notify_guard_condition();
|
||||
auto notify_guard_condition = std::make_shared<rclcpp::GuardCondition>();
|
||||
EXPECT_NO_THROW(waitable->add_guard_condition(notify_guard_condition));
|
||||
|
||||
auto default_cbg = node->get_node_base_interface()->get_default_callback_group();
|
||||
@@ -86,12 +85,15 @@ TEST_F(TestExecutorNotifyWaitable, wait) {
|
||||
auto waitables = node->get_node_waitables_interface();
|
||||
waitables->add_waitable(std::static_pointer_cast<rclcpp::Waitable>(waitable), default_cbg);
|
||||
|
||||
// notify the guard condition, this should trigger the on_execute_callback
|
||||
notify_guard_condition->trigger();
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node);
|
||||
executor.spin_all(std::chrono::seconds(1));
|
||||
EXPECT_EQ(1u, on_execute_calls);
|
||||
|
||||
// on_execute_callback doesn't change if the topology doesn't change
|
||||
// no further trigger, therefore no further callback
|
||||
executor.spin_all(std::chrono::seconds(1));
|
||||
EXPECT_EQ(1u, on_execute_calls);
|
||||
}
|
||||
|
||||
@@ -388,7 +388,7 @@ to_nanoseconds_helper(DurationT duration)
|
||||
// - works nominally (it can execute entities)
|
||||
// - it can execute multiple items at once
|
||||
// - it does not wait for work to be available before returning
|
||||
TYPED_TEST(TestExecutors, spin_some)
|
||||
TYPED_TEST(TestExecutors, spinSome)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
@@ -480,7 +480,7 @@ TYPED_TEST(TestExecutors, spin_some)
|
||||
|
||||
// The purpose of this test is to check that the ExecutorT.spin_some() method:
|
||||
// - does not continue executing after max_duration has elapsed
|
||||
TYPED_TEST(TestExecutors, spin_some_max_duration)
|
||||
TYPED_TEST(TestExecutors, spinSomeMaxDuration)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
@@ -638,7 +638,7 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
|
||||
// and b) refreshing the executor collections.
|
||||
// The inconsistent state would happen if the event was processed before the collections were
|
||||
// finished to be refreshed: the executor would pick up the event but be unable to process it.
|
||||
// This would leave the `entities_need_rebuild_` flag to true, preventing additional
|
||||
// This would leave the `notify_waitable_event_pushed_` flag to true, preventing additional
|
||||
// notify waitable events to be pushed.
|
||||
// The behavior is observable only under heavy load, so this test spawns several worker
|
||||
// threads. Due to the nature of the bug, this test may still succeed even if the
|
||||
@@ -647,13 +647,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
|
||||
TYPED_TEST(TestExecutors, testRaceConditionAddNode)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
// rmw_connextdds doesn't support events-executor
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
|
||||
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
// Spawn some threads to do some heavy work
|
||||
std::atomic<bool> should_cancel = false;
|
||||
@@ -701,67 +694,6 @@ TYPED_TEST(TestExecutors, testRaceConditionAddNode)
|
||||
}
|
||||
}
|
||||
|
||||
// Check that executors are correctly notified while they are spinning
|
||||
// we notify twice to ensure that the notify waitable is still working
|
||||
// after the first notification
|
||||
TYPED_TEST(TestExecutors, notifyTwiceWhileSpinning)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
// Create executor, add the node and start spinning
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
std::thread spinner([&]() {executor.spin();});
|
||||
|
||||
// Wait for executor to be spinning
|
||||
while (!executor.is_spinning()) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
|
||||
// Create the first subscription while the executor is already spinning
|
||||
std::atomic<size_t> sub1_msg_count {0};
|
||||
auto sub1 = this->node->template create_subscription<test_msgs::msg::Empty>(
|
||||
this->publisher->get_topic_name(),
|
||||
rclcpp::QoS(10),
|
||||
[&sub1_msg_count](test_msgs::msg::Empty::ConstSharedPtr) {
|
||||
sub1_msg_count++;
|
||||
});
|
||||
|
||||
// Publish a message and verify it's received
|
||||
this->publisher->publish(test_msgs::msg::Empty());
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while (sub1_msg_count == 0 && (std::chrono::steady_clock::now() - start) < 10s) {
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
EXPECT_EQ(sub1_msg_count, 1u);
|
||||
|
||||
// Create a second subscription while the executor is already spinning
|
||||
std::atomic<size_t> sub2_msg_count {0};
|
||||
auto sub2 = this->node->template create_subscription<test_msgs::msg::Empty>(
|
||||
this->publisher->get_topic_name(),
|
||||
rclcpp::QoS(10),
|
||||
[&sub2_msg_count](test_msgs::msg::Empty::ConstSharedPtr) {
|
||||
sub2_msg_count++;
|
||||
});
|
||||
|
||||
// Publish a message and verify it's received by both subscriptions
|
||||
this->publisher->publish(test_msgs::msg::Empty());
|
||||
start = std::chrono::steady_clock::now();
|
||||
while (
|
||||
sub1_msg_count == 1 &&
|
||||
sub2_msg_count == 0 &&
|
||||
(std::chrono::steady_clock::now() - start) < 10s)
|
||||
{
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
EXPECT_EQ(sub1_msg_count, 2u);
|
||||
EXPECT_EQ(sub2_msg_count, 1u);
|
||||
|
||||
// Cancel needs to be called before join, so that executor.spin() returns.
|
||||
executor.cancel();
|
||||
spinner.join();
|
||||
}
|
||||
|
||||
// Check spin_until_future_complete with node base pointer (instantiates its own executor)
|
||||
TEST(TestExecutors, testSpinUntilFutureCompleteNodeBasePtr)
|
||||
{
|
||||
@@ -833,7 +765,7 @@ TEST(TestExecutors, testSpinWithNonDefaultContext)
|
||||
rclcpp::shutdown(non_default_context);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutors, release_ownership_entity_after_spinning_cancel)
|
||||
TYPED_TEST(TestExecutors, releaseOwnershipEntityAfterSpinningCancel)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
@@ -856,3 +788,309 @@ TYPED_TEST(TestExecutors, release_ownership_entity_after_spinning_cancel)
|
||||
|
||||
EXPECT_EQ(server.use_count(), 1);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutors, testRaceDropCallbackGroupFromSecondThread)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
// Create an executor
|
||||
ExecutorType executor;
|
||||
executor.add_node(this->node);
|
||||
|
||||
// Start spinning
|
||||
auto executor_thread = std::thread(
|
||||
[&executor]() {
|
||||
executor.spin();
|
||||
});
|
||||
|
||||
// As the problem is a race, we do this multiple times,
|
||||
// to raise our chances of hitting the problem
|
||||
for (size_t i = 0; i < 10; i++) {
|
||||
auto cg = this->node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
auto timer = this->node->create_timer(1s, [] {}, cg);
|
||||
// sleep a bit, so that the spin thread can pick up the callback group
|
||||
// and add it to the executor
|
||||
std::this_thread::sleep_for(5ms);
|
||||
|
||||
// At this point the callbackgroup should be used within the waitset of the executor
|
||||
// as we leave the scope, the reference to cg will be dropped.
|
||||
// If the executor has a race, we will experience a segfault at this point.
|
||||
}
|
||||
|
||||
executor.cancel();
|
||||
executor_thread.join();
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutors, dropSomeTimer)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node");
|
||||
|
||||
bool timer1_works = false;
|
||||
bool timer2_works = false;
|
||||
|
||||
auto timer1 = node->create_timer(std::chrono::milliseconds(10), [&timer1_works]() {
|
||||
timer1_works = true;
|
||||
});
|
||||
auto timer2 = node->create_timer(std::chrono::milliseconds(10), [&timer2_works]() {
|
||||
timer2_works = true;
|
||||
});
|
||||
|
||||
executor.add_node(node);
|
||||
|
||||
// first let's make sure that both timers work
|
||||
auto max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
|
||||
while(!timer1_works || !timer2_works) {
|
||||
// let the executor pick up the node and the timers
|
||||
executor.spin_all(std::chrono::milliseconds(10));
|
||||
|
||||
const auto cur_time = std::chrono::steady_clock::now();
|
||||
ASSERT_LT(cur_time, max_end_time);
|
||||
}
|
||||
|
||||
// delete timer 2. Note, the executor uses an unordered map internally, to order
|
||||
// the entities added to the rcl waitset therefore the order is kind of undefined,
|
||||
// and this test may be flaky. In case it triggers, something is most likely
|
||||
// really broken.
|
||||
timer2.reset();
|
||||
|
||||
timer1_works = false;
|
||||
timer2_works = false;
|
||||
max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
|
||||
while(!timer1_works && !timer2_works) {
|
||||
// let the executor pick up the node and the timers
|
||||
executor.spin_all(std::chrono::milliseconds(10));
|
||||
|
||||
const auto cur_time = std::chrono::steady_clock::now();
|
||||
ASSERT_LT(cur_time, max_end_time);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(timer1_works || timer2_works);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutors, dropSomeNodeWithTimer)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
auto node1 = std::make_shared<rclcpp::Node>("test_node_1");
|
||||
auto node2 = std::make_shared<rclcpp::Node>("test_node_2");
|
||||
|
||||
bool timer1_works = false;
|
||||
bool timer2_works = false;
|
||||
|
||||
auto timer1 = node1->create_timer(std::chrono::milliseconds(10), [&timer1_works]() {
|
||||
timer1_works = true;
|
||||
});
|
||||
auto timer2 = node2->create_timer(std::chrono::milliseconds(10), [&timer2_works]() {
|
||||
timer2_works = true;
|
||||
});
|
||||
|
||||
executor.add_node(node1);
|
||||
executor.add_node(node2);
|
||||
|
||||
// first let's make sure that both timers work
|
||||
auto max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
|
||||
while(!timer1_works || !timer2_works) {
|
||||
// let the executor pick up the node and the timers
|
||||
executor.spin_all(std::chrono::milliseconds(10));
|
||||
|
||||
const auto cur_time = std::chrono::steady_clock::now();
|
||||
ASSERT_LT(cur_time, max_end_time);
|
||||
}
|
||||
|
||||
// delete node 1.
|
||||
node1 = nullptr;
|
||||
|
||||
timer2_works = false;
|
||||
max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
|
||||
while(!timer2_works) {
|
||||
// let the executor pick up the node and the timer
|
||||
executor.spin_all(std::chrono::milliseconds(10));
|
||||
|
||||
const auto cur_time = std::chrono::steady_clock::now();
|
||||
ASSERT_LT(cur_time, max_end_time);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(timer2_works);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutors, dropSomeSubscription)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node");
|
||||
|
||||
bool sub1_works = false;
|
||||
bool sub2_works = false;
|
||||
|
||||
auto sub1 = node->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
|
||||
[&sub1_works](const test_msgs::msg::Empty &) {
|
||||
sub1_works = true;
|
||||
});
|
||||
auto sub2 = node->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
|
||||
[&sub2_works](const test_msgs::msg::Empty &) {
|
||||
sub2_works = true;
|
||||
});
|
||||
|
||||
auto pub = node->create_publisher<test_msgs::msg::Empty>("/test_drop", 10);
|
||||
|
||||
executor.add_node(node);
|
||||
|
||||
// first let's make sure that both timers work
|
||||
auto max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
|
||||
while(!sub1_works || !sub2_works) {
|
||||
pub->publish(test_msgs::msg::Empty());
|
||||
|
||||
// let the executor pick up the node and the timers
|
||||
executor.spin_all(std::chrono::milliseconds(10));
|
||||
|
||||
const auto cur_time = std::chrono::steady_clock::now();
|
||||
ASSERT_LT(cur_time, max_end_time);
|
||||
}
|
||||
|
||||
// delete subscription 2. Note, the executor uses an unordered map internally, to order
|
||||
// the entities added to the rcl waitset therefore the order is kind of undefined,
|
||||
// and this test may be flaky. In case it triggers, something is most likely
|
||||
// really broken.
|
||||
sub2.reset();
|
||||
|
||||
sub1_works = false;
|
||||
sub2_works = false;
|
||||
max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
|
||||
while(!sub1_works && !sub2_works) {
|
||||
pub->publish(test_msgs::msg::Empty());
|
||||
|
||||
// let the executor pick up the node and the timers
|
||||
executor.spin_all(std::chrono::milliseconds(10));
|
||||
|
||||
const auto cur_time = std::chrono::steady_clock::now();
|
||||
ASSERT_LT(cur_time, max_end_time);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(sub1_works || sub2_works);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutors, dropSomeNodesWithSubscription)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node");
|
||||
auto node1 = std::make_shared<rclcpp::Node>("test_node_1");
|
||||
auto node2 = std::make_shared<rclcpp::Node>("test_node_2");
|
||||
|
||||
bool sub1_works = false;
|
||||
bool sub2_works = false;
|
||||
|
||||
auto sub1 = node1->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
|
||||
[&sub1_works](const test_msgs::msg::Empty &) {
|
||||
sub1_works = true;
|
||||
});
|
||||
auto sub2 = node2->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
|
||||
[&sub2_works](const test_msgs::msg::Empty &) {
|
||||
sub2_works = true;
|
||||
});
|
||||
|
||||
auto pub = node->create_publisher<test_msgs::msg::Empty>("/test_drop", 10);
|
||||
|
||||
executor.add_node(node);
|
||||
executor.add_node(node1);
|
||||
executor.add_node(node2);
|
||||
|
||||
// first let's make sure that both subscribers work
|
||||
auto max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
|
||||
while(!sub1_works || !sub2_works) {
|
||||
pub->publish(test_msgs::msg::Empty());
|
||||
|
||||
// let the executor pick up the node and the timers
|
||||
executor.spin_all(std::chrono::milliseconds(10));
|
||||
|
||||
const auto cur_time = std::chrono::steady_clock::now();
|
||||
ASSERT_LT(cur_time, max_end_time);
|
||||
}
|
||||
|
||||
// delete node 2.
|
||||
node2 = nullptr;
|
||||
|
||||
sub1_works = false;
|
||||
max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
|
||||
while(!sub1_works) {
|
||||
pub->publish(test_msgs::msg::Empty());
|
||||
|
||||
// let the executor pick up the node and the timers
|
||||
executor.spin_all(std::chrono::milliseconds(10));
|
||||
|
||||
const auto cur_time = std::chrono::steady_clock::now();
|
||||
ASSERT_LT(cur_time, max_end_time);
|
||||
}
|
||||
|
||||
ASSERT_TRUE(sub1_works);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutors, dropSubscriptionDuringCallback)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node");
|
||||
|
||||
bool sub1_works = false;
|
||||
bool sub2_works = false;
|
||||
|
||||
auto cbg = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive, true);
|
||||
rclcpp::SubscriptionOptions sub_ops;
|
||||
sub_ops.callback_group = cbg;
|
||||
|
||||
rclcpp::SubscriptionBase::SharedPtr sub1;
|
||||
rclcpp::SubscriptionBase::SharedPtr sub2;
|
||||
|
||||
// Note, the executor uses an unordered map internally, to order
|
||||
// the entities added to the rcl waitset therefore the order of the subscriptions
|
||||
// is kind of undefined. Therefore each sub deletes the other one.
|
||||
sub1 = node->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
|
||||
[&sub1_works, &sub2](const test_msgs::msg::Empty &) {
|
||||
sub1_works = true;
|
||||
// delete the other subscriber
|
||||
sub2.reset();
|
||||
}, sub_ops);
|
||||
sub2 = node->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
|
||||
[&sub2_works, &sub1](const test_msgs::msg::Empty &) {
|
||||
sub2_works = true;
|
||||
// delete the other subscriber
|
||||
sub1.reset();
|
||||
}, sub_ops);
|
||||
|
||||
auto pub = node->create_publisher<test_msgs::msg::Empty>("/test_drop", 10);
|
||||
|
||||
// wait for both subs to be connected
|
||||
auto max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(1500);
|
||||
while ((sub1->get_publisher_count() == 0) || (sub2->get_publisher_count() == 0)) {
|
||||
const auto cur_time = std::chrono::steady_clock::now();
|
||||
ASSERT_LT(cur_time, max_end_time);
|
||||
}
|
||||
|
||||
executor.add_node(node);
|
||||
|
||||
// publish some messages, until one subscriber fired. As both subscribers are
|
||||
// connected to the same topic, they should fire in the same wait.
|
||||
max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
|
||||
while (!sub1_works && !sub2_works) {
|
||||
pub->publish(test_msgs::msg::Empty());
|
||||
|
||||
// let the executor pick up the node and the timers
|
||||
executor.spin_all(std::chrono::milliseconds(10));
|
||||
|
||||
const auto cur_time = std::chrono::steady_clock::now();
|
||||
ASSERT_LT(cur_time, max_end_time);
|
||||
}
|
||||
|
||||
// only one subscriber must have worked, as the other
|
||||
// one was deleted during the callback
|
||||
ASSERT_TRUE(!sub1_works || !sub2_works);
|
||||
}
|
||||
|
||||
@@ -267,12 +267,15 @@ public:
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
executor.cancel();
|
||||
|
||||
// Clean up thread object
|
||||
if (standalone_thread.joinable()) {
|
||||
standalone_thread.join();
|
||||
}
|
||||
|
||||
node.reset();
|
||||
sim_clock_node.reset();
|
||||
}
|
||||
|
||||
std::shared_ptr<TimerNode> node;
|
||||
@@ -302,7 +305,6 @@ TYPED_TEST(TestTimerCancelBehavior, testTimer1CancelledWithExecutorSpin) {
|
||||
this->sim_clock_node->sleep_for(50ms);
|
||||
this->node->CancelTimer1();
|
||||
this->sim_clock_node->sleep_for(150ms);
|
||||
this->executor.cancel();
|
||||
|
||||
int t1_runs = this->node->GetTimer1Cnt();
|
||||
int t2_runs = this->node->GetTimer2Cnt();
|
||||
@@ -320,7 +322,6 @@ TYPED_TEST(TestTimerCancelBehavior, testTimer2CancelledWithExecutorSpin) {
|
||||
this->sim_clock_node->sleep_for(50ms);
|
||||
this->node->CancelTimer2();
|
||||
this->sim_clock_node->sleep_for(150ms);
|
||||
this->executor.cancel();
|
||||
|
||||
int t1_runs = this->node->GetTimer1Cnt();
|
||||
int t2_runs = this->node->GetTimer2Cnt();
|
||||
@@ -347,8 +348,6 @@ TYPED_TEST(TestTimerCancelBehavior, testHeadTimerCancelThenResetBehavior) {
|
||||
int t1_runs_final = this->node->GetTimer1Cnt();
|
||||
int t2_runs_final = this->node->GetTimer2Cnt();
|
||||
|
||||
this->executor.cancel();
|
||||
|
||||
// T1 should have been restarted, and execute about 15 additional times.
|
||||
// Check 10 greater than initial, to account for some timing jitter.
|
||||
EXPECT_LT(t1_runs_initial + 50, t1_runs_final);
|
||||
@@ -376,8 +375,6 @@ TYPED_TEST(TestTimerCancelBehavior, testBackTimerCancelThenResetBehavior) {
|
||||
int t1_runs_final = this->node->GetTimer1Cnt();
|
||||
int t2_runs_final = this->node->GetTimer2Cnt();
|
||||
|
||||
this->executor.cancel();
|
||||
|
||||
// T2 should have been restarted, and execute about 15 additional times.
|
||||
// Check 10 greater than initial, to account for some timing jitter.
|
||||
EXPECT_LT(t2_runs_initial + 50, t2_runs_final);
|
||||
@@ -411,8 +408,6 @@ TYPED_TEST(TestTimerCancelBehavior, testBothTimerCancelThenResetT1Behavior) {
|
||||
int t1_runs_final = this->node->GetTimer1Cnt();
|
||||
int t2_runs_final = this->node->GetTimer2Cnt();
|
||||
|
||||
this->executor.cancel();
|
||||
|
||||
// T1 and T2 should have the same initial count.
|
||||
EXPECT_LE(std::abs(t1_runs_initial - t2_runs_initial), 1);
|
||||
|
||||
@@ -450,8 +445,6 @@ TYPED_TEST(TestTimerCancelBehavior, testBothTimerCancelThenResetT2Behavior) {
|
||||
int t1_runs_final = this->node->GetTimer1Cnt();
|
||||
int t2_runs_final = this->node->GetTimer2Cnt();
|
||||
|
||||
this->executor.cancel();
|
||||
|
||||
// T1 and T2 should have the same initial count.
|
||||
EXPECT_LE(std::abs(t1_runs_initial - t2_runs_initial), 1);
|
||||
|
||||
|
||||
236
rclcpp/test/rclcpp/executors/test_executors_warmup.cpp
Normal file
236
rclcpp/test/rclcpp/executors/test_executors_warmup.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
// Copyright 2024 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.
|
||||
|
||||
/**
|
||||
* This test checks all implementations of rclcpp::executor to check they pass they basic API
|
||||
* tests. Anything specific to any executor in particular should go in a separate test file.
|
||||
*/
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
|
||||
#include "./executor_types.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
template<typename T>
|
||||
class TestExecutorsWarmup : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE(TestExecutorsWarmup, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
// This test verifies that spin_all is correctly collecting work multiple times
|
||||
// even when one of the items of work is a notifier waitable event and thus results in
|
||||
// rebuilding the entities collection.
|
||||
// When spin_all goes back to collect more work, it should see the ready items from
|
||||
// the new added entities
|
||||
TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
|
||||
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
|
||||
|
||||
// Add node to the executor before creating the entities
|
||||
executor.add_node(node);
|
||||
|
||||
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
|
||||
// the entities collection
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
|
||||
size_t callback_count = 0;
|
||||
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"test_topic", rclcpp::QoS(10), std::move(callback));
|
||||
|
||||
ASSERT_EQ(callback_count, 0u);
|
||||
|
||||
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
|
||||
publisher->publish(test_msgs::msg::Empty());
|
||||
|
||||
// We need to select a duration that is greater than
|
||||
// the time taken to refresh the entities collection and rebuild the waitset.
|
||||
// spin-all is expected to process the notifier waitable event, rebuild the collection,
|
||||
// and then collect more work, finding the subscription message event.
|
||||
// This duration has been selected empirically.
|
||||
executor.spin_all(std::chrono::milliseconds(500));
|
||||
|
||||
// Verify that the callback is called as part of the spin above
|
||||
EXPECT_EQ(callback_count, 1u);
|
||||
}
|
||||
|
||||
// Same test as `spin_all_doesnt_require_warmup`, but uses a callback group
|
||||
// This test reproduces the bug reported by https://github.com/ros2/rclcpp/issues/2589
|
||||
TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup_with_cbgroup)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
|
||||
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
|
||||
|
||||
auto callback_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive,
|
||||
false);
|
||||
|
||||
// Add callback group to the executor before creating the entities
|
||||
executor.add_callback_group(callback_group, node->get_node_base_interface());
|
||||
|
||||
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
|
||||
// the entities collection
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
|
||||
size_t callback_count = 0;
|
||||
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
sub_options.callback_group = callback_group;
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"test_topic", rclcpp::QoS(10), std::move(callback), sub_options);
|
||||
|
||||
ASSERT_EQ(callback_count, 0u);
|
||||
|
||||
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
|
||||
publisher->publish(test_msgs::msg::Empty());
|
||||
|
||||
// We need to select a duration that is greater than
|
||||
// the time taken to refresh the entities collection and rebuild the waitset.
|
||||
// spin-all is expected to process the notifier waitable event, rebuild the collection,
|
||||
// and then collect more work, finding the subscription message event.
|
||||
// This duration has been selected empirically.
|
||||
executor.spin_all(std::chrono::milliseconds(500));
|
||||
|
||||
// Verify that the callback is called as part of the spin above
|
||||
EXPECT_EQ(callback_count, 1u);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutorsWarmup, spin_some_doesnt_require_warmup)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
// TODO(alsora): currently only the events-executor passes this test.
|
||||
// Enable single-threaded and multi-threaded executors
|
||||
// when https://github.com/ros2/rclcpp/pull/2595 gets merged
|
||||
if (
|
||||
!std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>())
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
|
||||
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
|
||||
|
||||
// Add node to the executor before creating the entities
|
||||
executor.add_node(node);
|
||||
|
||||
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
|
||||
// the entities collection
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
|
||||
size_t callback_count = 0;
|
||||
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"test_topic", rclcpp::QoS(10), std::move(callback));
|
||||
|
||||
ASSERT_EQ(callback_count, 0u);
|
||||
|
||||
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
|
||||
publisher->publish(test_msgs::msg::Empty());
|
||||
|
||||
// NOTE: intra-process communication is enabled, so the subscription will immediately see
|
||||
// the new message, no risk of race conditions where spin_some gets called before the
|
||||
// message has been delivered.
|
||||
executor.spin_some();
|
||||
|
||||
// Verify that the callback is called as part of the spin above
|
||||
EXPECT_EQ(callback_count, 1u);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutorsWarmup, spin_some_doesnt_require_warmup_with_cbgroup)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
// TODO(alsora): currently only the events-executor passes this test.
|
||||
// Enable single-threaded and multi-threaded executors
|
||||
// when https://github.com/ros2/rclcpp/pull/2595 gets merged
|
||||
if (
|
||||
!std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>())
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
|
||||
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
|
||||
|
||||
auto callback_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive,
|
||||
false);
|
||||
|
||||
// Add callback group to the executor before creating the entities
|
||||
executor.add_callback_group(callback_group, node->get_node_base_interface());
|
||||
|
||||
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
|
||||
// the entities collection
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
|
||||
size_t callback_count = 0;
|
||||
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
sub_options.callback_group = callback_group;
|
||||
auto subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>(
|
||||
"test_topic", rclcpp::QoS(10), std::move(callback), sub_options);
|
||||
|
||||
ASSERT_EQ(callback_count, 0u);
|
||||
|
||||
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
|
||||
publisher->publish(test_msgs::msg::Empty());
|
||||
|
||||
// NOTE: intra-process communication is enabled, so the subscription will immediately see
|
||||
// the new message, no risk of race conditions where spin_some gets called before the
|
||||
// message has been delivered.
|
||||
executor.spin_some();
|
||||
|
||||
// Verify that the callback is called as part of the spin above
|
||||
EXPECT_EQ(callback_count, 1u);
|
||||
}
|
||||
@@ -56,7 +56,7 @@ TEST_F(TestStaticSingleThreadedExecutor, add_callback_group_trigger_guard_failed
|
||||
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
executor.add_callback_group(cb_group, node->get_node_base_interface(), true),
|
||||
std::runtime_error("Failed to handle entities update on callback group add: error not set"));
|
||||
std::runtime_error("Failed to trigger guard condition on callback group add: error not set"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ TEST_F(TestStaticSingleThreadedExecutor, add_node_trigger_guard_failed) {
|
||||
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
executor.add_node(node),
|
||||
std::runtime_error("Failed to handle entities update on node add: error not set"));
|
||||
std::runtime_error("Failed to trigger guard condition on node add: error not set"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_callback_group_trigger_guard_fai
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
executor.remove_callback_group(cb_group, true),
|
||||
std::runtime_error(
|
||||
"Failed to handle entities update on callback group remove: error not set"));
|
||||
"Failed to trigger guard condition on callback group remove: error not set"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,7 +115,7 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_node_trigger_guard_failed) {
|
||||
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
executor.remove_node(node, true),
|
||||
std::runtime_error("Failed to handle entities update on node remove: error not set"));
|
||||
std::runtime_error("Failed to trigger guard condition on node remove: error not set"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
@@ -192,7 +192,7 @@ TEST_F(TestNodeInterfaces, ni_init) {
|
||||
/*
|
||||
Testing macro'ed getters.
|
||||
*/
|
||||
TEST_F(TestNodeInterfaces, ni_all_init) {
|
||||
TEST_F(TestNodeInterfaces, ni_all_init_non_const) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
using rclcpp::node_interfaces::NodeInterfaces;
|
||||
@@ -252,3 +252,64 @@ TEST_F(TestNodeInterfaces, ni_all_init) {
|
||||
time_source = ni.get_node_time_source_interface();
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestNodeInterfaces, ni_all_init_const) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
|
||||
using rclcpp::node_interfaces::NodeInterfaces;
|
||||
using rclcpp::node_interfaces::NodeBaseInterface;
|
||||
using rclcpp::node_interfaces::NodeClockInterface;
|
||||
using rclcpp::node_interfaces::NodeGraphInterface;
|
||||
using rclcpp::node_interfaces::NodeLoggingInterface;
|
||||
using rclcpp::node_interfaces::NodeTimersInterface;
|
||||
using rclcpp::node_interfaces::NodeTopicsInterface;
|
||||
using rclcpp::node_interfaces::NodeServicesInterface;
|
||||
using rclcpp::node_interfaces::NodeWaitablesInterface;
|
||||
using rclcpp::node_interfaces::NodeParametersInterface;
|
||||
using rclcpp::node_interfaces::NodeTimeSourceInterface;
|
||||
|
||||
const auto ni = rclcpp::node_interfaces::NodeInterfaces<ALL_RCLCPP_NODE_INTERFACES>(*node);
|
||||
|
||||
{
|
||||
auto base = ni.get<NodeBaseInterface>();
|
||||
base = ni.get_node_base_interface();
|
||||
EXPECT_STREQ(base->get_name(), "my_node"); // Test for functionality
|
||||
}
|
||||
{
|
||||
auto clock = ni.get<NodeClockInterface>();
|
||||
clock = ni.get_node_clock_interface();
|
||||
clock->get_clock();
|
||||
}
|
||||
{
|
||||
auto graph = ni.get<NodeGraphInterface>();
|
||||
graph = ni.get_node_graph_interface();
|
||||
}
|
||||
{
|
||||
auto logging = ni.get<NodeLoggingInterface>();
|
||||
logging = ni.get_node_logging_interface();
|
||||
}
|
||||
{
|
||||
auto timers = ni.get<NodeTimersInterface>();
|
||||
timers = ni.get_node_timers_interface();
|
||||
}
|
||||
{
|
||||
auto topics = ni.get<NodeTopicsInterface>();
|
||||
topics = ni.get_node_topics_interface();
|
||||
}
|
||||
{
|
||||
auto services = ni.get<NodeServicesInterface>();
|
||||
services = ni.get_node_services_interface();
|
||||
}
|
||||
{
|
||||
auto waitables = ni.get<NodeWaitablesInterface>();
|
||||
waitables = ni.get_node_waitables_interface();
|
||||
}
|
||||
{
|
||||
auto parameters = ni.get<NodeParametersInterface>();
|
||||
parameters = ni.get_node_parameters_interface();
|
||||
}
|
||||
{
|
||||
auto time_source = ni.get<NodeTimeSourceInterface>();
|
||||
time_source = ni.get_node_time_source_interface();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -62,7 +61,7 @@ protected:
|
||||
std::shared_ptr<rclcpp::Node> node;
|
||||
rclcpp::node_interfaces::NodeParameters * node_parameters;
|
||||
|
||||
std::filesystem::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
rcpputils::fs::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
};
|
||||
|
||||
TEST_F(TestNodeParameters, construct_destruct_rcl_errors) {
|
||||
@@ -126,7 +125,7 @@ TEST_F(TestNodeParameters, list_parameters)
|
||||
list_result5.names.end());
|
||||
}
|
||||
|
||||
TEST_F(TestNodeParameters, parameter_overrides)
|
||||
TEST_F(TestNodeParameters, parameter_overrides_with_value)
|
||||
{
|
||||
rclcpp::NodeOptions node_options;
|
||||
node_options.automatically_declare_parameters_from_overrides(true);
|
||||
@@ -144,6 +143,24 @@ TEST_F(TestNodeParameters, parameter_overrides)
|
||||
EXPECT_EQ(2u, parameter_overrides.size());
|
||||
}
|
||||
|
||||
TEST_F(TestNodeParameters, parameter_overrides_with_parameter)
|
||||
{
|
||||
rclcpp::NodeOptions node_options;
|
||||
node_options.automatically_declare_parameters_from_overrides(true);
|
||||
node_options.append_parameter_override(rclcpp::Parameter("param1", true));
|
||||
node_options.append_parameter_override(rclcpp::Parameter("param2", 42));
|
||||
|
||||
std::shared_ptr<rclcpp::Node> node2 = std::make_shared<rclcpp::Node>("node2", "ns", node_options);
|
||||
|
||||
auto * node_parameters_interface =
|
||||
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
|
||||
node2->get_node_parameters_interface().get());
|
||||
ASSERT_NE(nullptr, node_parameters_interface);
|
||||
|
||||
const auto & parameter_overrides = node_parameters_interface->get_parameter_overrides();
|
||||
EXPECT_EQ(2u, parameter_overrides.size());
|
||||
}
|
||||
|
||||
TEST_F(TestNodeParameters, set_parameters) {
|
||||
rclcpp::NodeOptions node_options;
|
||||
node_options.allow_undeclared_parameters(true);
|
||||
|
||||
@@ -37,6 +37,38 @@ TEST_F(TestNodeTypeDescriptions, interface_created)
|
||||
ASSERT_NE(nullptr, node.get_node_type_descriptions_interface());
|
||||
}
|
||||
|
||||
TEST_F(TestNodeTypeDescriptions, automatically_declare_parameters_from_overrides)
|
||||
{
|
||||
rclcpp::NodeOptions node_options;
|
||||
node_options.automatically_declare_parameters_from_overrides(true);
|
||||
node_options.append_parameter_override("start_type_description_service", false);
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns", node_options);
|
||||
(void) node;
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(TestNodeTypeDescriptions, bad_parameter_type)
|
||||
{
|
||||
rclcpp::NodeOptions node_options;
|
||||
node_options.append_parameter_override("start_type_description_service", "unexpected_type");
|
||||
|
||||
ASSERT_THROW(
|
||||
{
|
||||
node_options.automatically_declare_parameters_from_overrides(false);
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns", node_options);
|
||||
(void) node;
|
||||
}, rclcpp::exceptions::InvalidParameterTypeException);
|
||||
|
||||
ASSERT_THROW(
|
||||
{
|
||||
node_options.automatically_declare_parameters_from_overrides(true);
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns", node_options);
|
||||
(void) node;
|
||||
}, rclcpp::exceptions::InvalidParameterTypeException);
|
||||
}
|
||||
|
||||
TEST_F(TestNodeTypeDescriptions, disabled_no_service)
|
||||
{
|
||||
rclcpp::NodeOptions node_options;
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
#include "test_msgs/msg/empty.h"
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
@@ -215,6 +216,7 @@ TYPED_TEST(TestAddCallbackGroupsToExecutor, add_callback_groups_after_add_node_t
|
||||
std::atomic_size_t timer_count {0};
|
||||
auto timer_callback = [&executor, &timer_count]() {
|
||||
auto cur_timer_count = timer_count++;
|
||||
printf("in timer_callback(%zu)\n", cur_timer_count);
|
||||
if (cur_timer_count > 0) {
|
||||
executor.cancel();
|
||||
}
|
||||
@@ -343,30 +345,32 @@ TYPED_TEST(TestAddCallbackGroupsToExecutorStable, subscriber_triggered_to_receiv
|
||||
received_message_promise.set_value(true);
|
||||
};
|
||||
|
||||
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
|
||||
rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr publisher;
|
||||
// to create a timer with a callback run on another executor
|
||||
rclcpp::TimerBase::SharedPtr timer = nullptr;
|
||||
std::promise<void> timer_promise;
|
||||
// create a subscription using the 'cb_grp' callback group
|
||||
rclcpp::QoS qos = rclcpp::QoS(1).reliable();
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
options.callback_group = cb_grp;
|
||||
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>("topic_name", qos, sub_callback, options);
|
||||
// create a publisher to send data
|
||||
rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr publisher =
|
||||
node->create_publisher<test_msgs::msg::Empty>("topic_name", qos);
|
||||
auto timer_callback =
|
||||
[&publisher, &timer_promise]() {
|
||||
if (publisher->get_subscription_count() == 0) {
|
||||
// If discovery hasn't happened yet, get out.
|
||||
return;
|
||||
[&subscription, &publisher, &timer, &cb_grp, &node, &sub_callback, &timer_promise]() {
|
||||
if (timer) {
|
||||
timer.reset();
|
||||
}
|
||||
|
||||
// create a subscription using the `cb_grp` callback group
|
||||
rclcpp::QoS qos = rclcpp::QoS(1).reliable();
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
options.callback_group = cb_grp;
|
||||
subscription =
|
||||
node->create_subscription<test_msgs::msg::Empty>("topic_name", qos, sub_callback, options);
|
||||
// create a publisher to send data
|
||||
publisher =
|
||||
node->create_publisher<test_msgs::msg::Empty>("topic_name", qos);
|
||||
publisher->publish(test_msgs::msg::Empty());
|
||||
timer_promise.set_value();
|
||||
};
|
||||
|
||||
// Another executor to run the timer with a callback
|
||||
ExecutorType timer_executor;
|
||||
|
||||
rclcpp::TimerBase::SharedPtr timer = node->create_wall_timer(100ms, timer_callback);
|
||||
timer = node->create_wall_timer(100ms, timer_callback);
|
||||
timer_executor.add_node(node);
|
||||
auto future = timer_promise.get_future();
|
||||
timer_executor.spin_until_future_complete(future);
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "rclcpp/any_service_callback.hpp"
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
#include "test_msgs/srv/empty.h"
|
||||
|
||||
class TestAnyServiceCallback : public ::testing::Test
|
||||
{
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
#include "test_msgs/msg/empty.h"
|
||||
|
||||
// Type adapter to be used in tests.
|
||||
struct MyEmpty {};
|
||||
|
||||
@@ -89,10 +89,31 @@ TEST_F(TestClient, construction_and_destruction) {
|
||||
{
|
||||
auto client = node->create_client<ListParameters>("service");
|
||||
}
|
||||
{
|
||||
// suppress deprecated function warning
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
auto client = node->create_client<ListParameters>(
|
||||
"service", rmw_qos_profile_services_default);
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
}
|
||||
{
|
||||
auto client = node->create_client<ListParameters>(
|
||||
"service", rclcpp::ServicesQoS());
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
@@ -102,6 +123,27 @@ TEST_F(TestClient, construction_and_destruction) {
|
||||
}
|
||||
|
||||
TEST_F(TestClient, construction_with_free_function) {
|
||||
{
|
||||
auto client = rclcpp::create_client<rcl_interfaces::srv::ListParameters>(
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
"service",
|
||||
rmw_qos_profile_services_default,
|
||||
nullptr);
|
||||
}
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto client = rclcpp::create_client<rcl_interfaces::srv::ListParameters>(
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
"invalid_?service",
|
||||
rmw_qos_profile_services_default,
|
||||
nullptr);
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
{
|
||||
auto client = rclcpp::create_client<rcl_interfaces::srv::ListParameters>(
|
||||
node->get_node_base_interface(),
|
||||
|
||||
@@ -431,7 +431,7 @@ TYPED_TEST(TestAllClientTypesWithServer, client_qos)
|
||||
|
||||
rclcpp::ServicesQoS qos_profile;
|
||||
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
|
||||
rclcpp::Duration duration(std::chrono::nanoseconds(1));
|
||||
rclcpp::Duration duration(std::chrono::milliseconds(1));
|
||||
qos_profile.deadline(duration);
|
||||
qos_profile.lifespan(duration);
|
||||
qos_profile.liveliness_lease_duration(duration);
|
||||
|
||||
@@ -178,7 +178,10 @@ TEST_F(TestClockWakeup, no_wakeup_on_sim_time) {
|
||||
TEST_F(TestClockWakeup, multiple_threads_wait_on_one_clock) {
|
||||
auto clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
|
||||
|
||||
std::vector<bool> thread_finished(10, false);
|
||||
std::vector<std::atomic_bool> thread_finished(10);
|
||||
for (std::atomic_bool & finished : thread_finished) {
|
||||
finished = false;
|
||||
}
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
@@ -196,7 +199,7 @@ TEST_F(TestClockWakeup, multiple_threads_wait_on_one_clock) {
|
||||
// wait a bit so all threads can execute the sleep_until
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
|
||||
for (const bool & finished : thread_finished) {
|
||||
for (const std::atomic_bool & finished : thread_finished) {
|
||||
EXPECT_FALSE(finished);
|
||||
}
|
||||
|
||||
@@ -207,7 +210,7 @@ TEST_F(TestClockWakeup, multiple_threads_wait_on_one_clock) {
|
||||
bool threads_finished = false;
|
||||
while (!threads_finished && start_time + std::chrono::seconds(1) > cur_time) {
|
||||
threads_finished = true;
|
||||
for (const bool finished : thread_finished) {
|
||||
for (const std::atomic_bool & finished : thread_finished) {
|
||||
if (!finished) {
|
||||
threads_finished = false;
|
||||
}
|
||||
@@ -217,7 +220,7 @@ TEST_F(TestClockWakeup, multiple_threads_wait_on_one_clock) {
|
||||
cur_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
for (const bool finished : thread_finished) {
|
||||
for (const std::atomic_bool & finished : thread_finished) {
|
||||
EXPECT_TRUE(finished);
|
||||
}
|
||||
|
||||
|
||||
246
rclcpp/test/rclcpp/test_clock_conditional.cpp
Normal file
246
rclcpp/test/rclcpp/test_clock_conditional.cpp
Normal file
@@ -0,0 +1,246 @@
|
||||
// Copyright 2025 Cellumation GmbH
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/time.h"
|
||||
#include "rclcpp/clock.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp/time_source.hpp"
|
||||
|
||||
#include "../utils/rclcpp_gtest_macros.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class TestClockWakeup : public ::testing::TestWithParam<rcl_clock_type_e>
|
||||
{
|
||||
public:
|
||||
void test_wakeup_before_sleep(const rclcpp::Clock::SharedPtr & clock)
|
||||
{
|
||||
std::atomic_bool thread_finished = false;
|
||||
rclcpp::ClockConditionalVariable cond(clock);
|
||||
|
||||
bool stopSleeping = false;
|
||||
|
||||
std::thread wait_thread = std::thread(
|
||||
[&cond, &clock, &stopSleeping, &thread_finished]()
|
||||
{
|
||||
// make sure the thread starts sleeping late
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
std::unique_lock lk(cond.mutex());
|
||||
cond.wait_until(lk, clock->now() + std::chrono::seconds(3),
|
||||
[&stopSleeping] () {return stopSleeping;});
|
||||
thread_finished = true;
|
||||
});
|
||||
|
||||
{
|
||||
std::lock_guard lk(cond.mutex());
|
||||
// stop sleeping after next notification
|
||||
stopSleeping = true;
|
||||
}
|
||||
// notify the conditional, to recheck it pred
|
||||
cond.notify_one();
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
auto cur_time = start_time;
|
||||
while (!thread_finished && start_time + std::chrono::seconds(1) > cur_time) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
cur_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
wait_thread.join();
|
||||
|
||||
EXPECT_TRUE(thread_finished);
|
||||
EXPECT_LT(cur_time, start_time + std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
void test_wakeup_after_sleep(const rclcpp::Clock::SharedPtr & clock)
|
||||
{
|
||||
std::atomic_bool thread_finished = false;
|
||||
rclcpp::ClockConditionalVariable cond(clock);
|
||||
|
||||
bool stopSleeping = false;
|
||||
|
||||
std::thread wait_thread = std::thread(
|
||||
[&cond, &clock, &stopSleeping, &thread_finished]()
|
||||
{
|
||||
std::unique_lock lk(cond.mutex());
|
||||
cond.wait_until(lk, clock->now() + std::chrono::seconds(3),
|
||||
[&stopSleeping] () {return stopSleeping;});
|
||||
thread_finished = true;
|
||||
});
|
||||
|
||||
// make sure the thread is already sleeping before we send the cancel
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
|
||||
{
|
||||
std::lock_guard lk(cond.mutex());
|
||||
// stop sleeping after next notification
|
||||
stopSleeping = true;
|
||||
}
|
||||
// notify the conditional, to recheck it pred
|
||||
cond.notify_one();
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
auto cur_time = start_time;
|
||||
while (!thread_finished && start_time + std::chrono::seconds(1) > cur_time) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
cur_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
wait_thread.join();
|
||||
|
||||
EXPECT_TRUE(thread_finished);
|
||||
EXPECT_LT(cur_time, start_time + std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("my_node");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_SUITE_P(
|
||||
ClockConditionalVariable,
|
||||
TestClockWakeup,
|
||||
::testing::Values(
|
||||
RCL_SYSTEM_TIME, RCL_ROS_TIME, RCL_STEADY_TIME
|
||||
));
|
||||
|
||||
TEST_P(TestClockWakeup, wakeup_sleep) {
|
||||
auto clock = std::make_shared<rclcpp::Clock>(GetParam());
|
||||
test_wakeup_after_sleep(clock);
|
||||
test_wakeup_before_sleep(clock);
|
||||
}
|
||||
|
||||
TEST_F(TestClockWakeup, wakeup_sleep_ros_time_active) {
|
||||
node->set_parameter({"use_sim_time", true});
|
||||
auto clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
|
||||
rclcpp::TimeSource time_source(node);
|
||||
time_source.attachClock(clock);
|
||||
|
||||
EXPECT_TRUE(clock->ros_time_is_active());
|
||||
|
||||
test_wakeup_after_sleep(clock);
|
||||
test_wakeup_before_sleep(clock);
|
||||
}
|
||||
|
||||
TEST_F(TestClockWakeup, no_wakeup_on_sim_time) {
|
||||
node->set_parameter({"use_sim_time", true});
|
||||
auto clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
|
||||
EXPECT_FALSE(clock->ros_time_is_active());
|
||||
|
||||
rclcpp::TimeSource time_source(node);
|
||||
time_source.attachClock(clock);
|
||||
EXPECT_TRUE(clock->ros_time_is_active());
|
||||
|
||||
std::atomic_bool thread_finished = false;
|
||||
rclcpp::ClockConditionalVariable cond(clock);
|
||||
|
||||
bool stopSleeping = false;
|
||||
|
||||
std::thread wait_thread = std::thread(
|
||||
[&cond, &clock, &stopSleeping, &thread_finished]()
|
||||
{
|
||||
std::unique_lock lk(cond.mutex());
|
||||
// only sleep for an short period
|
||||
cond.wait_until(lk, clock->now() + std::chrono::milliseconds(10),
|
||||
[&stopSleeping] () {return stopSleeping;});
|
||||
thread_finished = true;
|
||||
});
|
||||
|
||||
// make sure, that the sim time clock does not wakeup, as no clock is provided
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
EXPECT_FALSE(thread_finished);
|
||||
|
||||
{
|
||||
std::lock_guard lk(cond.mutex());
|
||||
// stop sleeping after next notification
|
||||
stopSleeping = true;
|
||||
}
|
||||
// notify the conditional, to recheck it pred
|
||||
cond.notify_one();
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
auto cur_time = start_time;
|
||||
while (!thread_finished && start_time + std::chrono::seconds(1) > cur_time) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
cur_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
wait_thread.join();
|
||||
|
||||
EXPECT_TRUE(thread_finished);
|
||||
EXPECT_LT(cur_time, start_time + std::chrono::seconds(1));
|
||||
}
|
||||
|
||||
TEST_F(TestClockWakeup, wakeup_on_ros_shutdown) {
|
||||
auto clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
|
||||
|
||||
std::atomic_bool thread_finished = false;
|
||||
rclcpp::ClockConditionalVariable cond(clock);
|
||||
|
||||
bool stopSleeping = false;
|
||||
|
||||
std::thread wait_thread = std::thread(
|
||||
[&cond, &clock, &stopSleeping, &thread_finished]()
|
||||
{
|
||||
std::unique_lock lk(cond.mutex());
|
||||
// only sleep for an short period
|
||||
cond.wait_until(lk, clock->now() + std::chrono::seconds(10),
|
||||
[&stopSleeping] () {return stopSleeping;});
|
||||
thread_finished = true;
|
||||
});
|
||||
|
||||
// wait a bit to be sure the thread is sleeping
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(500));
|
||||
|
||||
EXPECT_FALSE(thread_finished);
|
||||
|
||||
rclcpp::shutdown();
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
auto cur_time = start_time;
|
||||
while (!thread_finished && start_time + std::chrono::seconds(1) > cur_time) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
cur_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
EXPECT_TRUE(thread_finished);
|
||||
|
||||
wait_thread.join();
|
||||
|
||||
EXPECT_LT(cur_time, start_time + std::chrono::seconds(1));
|
||||
}
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
#include "test_msgs/msg/empty.h"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -92,3 +93,19 @@ TEST_F(TestCreateSubscription, create_with_statistics) {
|
||||
ASSERT_NE(nullptr, subscription);
|
||||
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
|
||||
}
|
||||
|
||||
TEST_F(TestCreateSubscription, create_with_intra_process_com) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
|
||||
|
||||
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
|
||||
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
subscription = rclcpp::create_subscription<test_msgs::msg::Empty>(
|
||||
node, "topic_name", rclcpp::SystemDefaultsQoS(), callback, options);
|
||||
});
|
||||
ASSERT_NE(nullptr, subscription);
|
||||
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
|
||||
}
|
||||
|
||||
@@ -138,7 +138,7 @@ TEST_F(TestExecutor, add_callback_group_failed_trigger_guard_condition) {
|
||||
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
dummy.add_callback_group(cb_group, node->get_node_base_interface(), true),
|
||||
std::runtime_error("Failed to handle entities update on callback group add: error not set"));
|
||||
std::runtime_error("Failed to trigger guard condition on callback group add: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestExecutor, remove_callback_group_null_node) {
|
||||
@@ -175,7 +175,7 @@ TEST_F(TestExecutor, remove_callback_group_failed_trigger_guard_condition) {
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
dummy.remove_callback_group(cb_group, true),
|
||||
std::runtime_error(
|
||||
"Failed to handle entities update on callback group remove: error not set"));
|
||||
"Failed to trigger guard condition on callback group remove: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestExecutor, remove_node_not_associated) {
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -517,7 +517,7 @@ TEST(TestIntraProcessManager, add_pub_sub) {
|
||||
* - Remove the first subscription from ipm and add a new one.
|
||||
* - Publishes a unique_ptr message with a subscription not requesting ownership.
|
||||
* - The received message is expected to be the same, the first subscription do not receive it.
|
||||
* - Publishes a shared_ptr message with a subscription not requesting ownership.
|
||||
* - Publishes a unique_ptr message with a subscription not requesting ownership.
|
||||
* - The received message is expected to be the same.
|
||||
*/
|
||||
TEST(TestIntraProcessManager, single_subscription) {
|
||||
@@ -569,9 +569,9 @@ TEST(TestIntraProcessManager, single_subscription) {
|
||||
* - One is expected to receive the published message, while the other will receive a copy.
|
||||
* - Publishes a unique_ptr message with 2 subscriptions not requesting ownership.
|
||||
* - Both received messages are expected to be the same as the published one.
|
||||
* - Publishes a shared_ptr message with 2 subscriptions requesting ownership.
|
||||
* - Publishes a unique_ptr message with 2 subscriptions requesting ownership.
|
||||
* - Both received messages are expected to be a copy of the published one.
|
||||
* - Publishes a shared_ptr message with 2 subscriptions not requesting ownership.
|
||||
* - Publishes a unique_ptr message with 2 subscriptions not requesting ownership.
|
||||
* - Both received messages are expected to be the same as the published one.
|
||||
*/
|
||||
TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
@@ -676,9 +676,9 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
* - The 2 subscriptions not requesting ownership are expected to both receive the same copy
|
||||
* of the message, one of the subscription requesting ownership is expected to receive a
|
||||
* different copy, while the last is expected to receive the published message.
|
||||
* - Publishes a shared_ptr message with 1 subscription requesting ownership and 1 not.
|
||||
* - The subscription requesting ownership is expected to receive a copy of the message, while
|
||||
* the other is expected to receive the published message
|
||||
* - Publishes a unique_ptr message with 1 subscription requesting ownership and 1 not.
|
||||
* - The subscription requesting ownership is expected to receive the published message, while
|
||||
* the other is expected to receive a copy of the message
|
||||
*/
|
||||
TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
|
||||
|
||||
@@ -40,6 +40,53 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestLoanedMessage, initialize) {
|
||||
auto node = std::make_shared<rclcpp::Node>("loaned_message_test_node");
|
||||
auto pub = node->create_publisher<MessageT>("loaned_message_test_topic", 1);
|
||||
|
||||
// suppress deprecated function warning
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
auto pub_allocator = pub->get_allocator();
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
// suppress deprecated function warning
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
auto loaned_msg = rclcpp::LoanedMessage<MessageT>(pub.get(), pub_allocator);
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
ASSERT_TRUE(loaned_msg.is_valid());
|
||||
loaned_msg.get().float32_value = 42.0f;
|
||||
ASSERT_EQ(42.0f, loaned_msg.get().float32_value);
|
||||
|
||||
SUCCEED();
|
||||
}
|
||||
|
||||
TEST_F(TestLoanedMessage, loan_from_pub) {
|
||||
auto node = std::make_shared<rclcpp::Node>("loaned_message_test_node");
|
||||
auto pub = node->create_publisher<MessageT>("loaned_message_test_topic", 1);
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -211,7 +210,15 @@ TEST(TestLogger, get_logging_directory) {
|
||||
ASSERT_EQ(true, rcutils_set_env("ROS_LOG_DIR", nullptr));
|
||||
ASSERT_EQ(true, rcutils_set_env("ROS_HOME", nullptr));
|
||||
|
||||
auto path = rclcpp::get_log_directory();
|
||||
auto expected_path = std::filesystem::path{"/fake_home_dir"} / ".ros" / "log";
|
||||
EXPECT_EQ(path, expected_path);
|
||||
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());
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -253,9 +253,19 @@ bool log_function_const_ref(const rclcpp::Logger & logger)
|
||||
return true;
|
||||
}
|
||||
|
||||
class DerivedLogger : public rclcpp::Logger
|
||||
{
|
||||
public:
|
||||
explicit DerivedLogger(const rclcpp::Logger & logger)
|
||||
: rclcpp::Logger(logger) {}
|
||||
};
|
||||
|
||||
TEST_F(TestLoggingMacros, test_log_from_node) {
|
||||
auto logger = rclcpp::get_logger("test_logging_logger");
|
||||
EXPECT_TRUE(log_function(logger));
|
||||
EXPECT_TRUE(log_function_const(logger));
|
||||
EXPECT_TRUE(log_function_const_ref(logger));
|
||||
|
||||
DerivedLogger derived_logger(logger);
|
||||
RCLCPP_INFO(derived_logger, "successful log from derived logger");
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
@@ -57,7 +56,7 @@ protected:
|
||||
test_resources_path /= "test_node";
|
||||
}
|
||||
|
||||
std::filesystem::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
rcpputils::fs::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -200,6 +200,57 @@ TEST(TestNodeOptions, copy) {
|
||||
rcl_arguments_get_count_unparsed(&other_rcl_options->arguments),
|
||||
rcl_arguments_get_count_unparsed(&rcl_options->arguments));
|
||||
}
|
||||
|
||||
{
|
||||
// The following scope test is missing:
|
||||
// "arguments" because it is already tested in the above scopes
|
||||
// "parameter_event_publisher_options" because it can not be directly compared with EXPECT_EQ
|
||||
// "allocator" because it can not be directly compared with EXPECT_EQ
|
||||
|
||||
// We separate attribute modification from variable initialisation (copy assignment operator)
|
||||
// to be sure the "non_default_options"'s properties are correctly set before testing the
|
||||
// assignment operator.
|
||||
auto non_default_options = rclcpp::NodeOptions();
|
||||
non_default_options
|
||||
.parameter_overrides({rclcpp::Parameter("foo", 0), rclcpp::Parameter("bar", "1")})
|
||||
.use_global_arguments(false)
|
||||
.enable_rosout(false)
|
||||
.use_intra_process_comms(true)
|
||||
.enable_topic_statistics(true)
|
||||
.start_parameter_services(false)
|
||||
.enable_logger_service(true)
|
||||
.start_parameter_event_publisher(false)
|
||||
.clock_type(RCL_SYSTEM_TIME)
|
||||
.clock_qos(rclcpp::SensorDataQoS())
|
||||
.use_clock_thread(false)
|
||||
.parameter_event_qos(rclcpp::ClockQoS())
|
||||
.rosout_qos(rclcpp::ParameterEventsQoS())
|
||||
.allow_undeclared_parameters(true)
|
||||
.automatically_declare_parameters_from_overrides(true);
|
||||
|
||||
auto copied_options = non_default_options;
|
||||
EXPECT_EQ(non_default_options.parameter_overrides(), copied_options.parameter_overrides());
|
||||
EXPECT_EQ(non_default_options.use_global_arguments(), copied_options.use_global_arguments());
|
||||
EXPECT_EQ(non_default_options.enable_rosout(), copied_options.enable_rosout());
|
||||
EXPECT_EQ(non_default_options.use_intra_process_comms(),
|
||||
copied_options.use_intra_process_comms());
|
||||
EXPECT_EQ(non_default_options.enable_topic_statistics(),
|
||||
copied_options.enable_topic_statistics());
|
||||
EXPECT_EQ(non_default_options.start_parameter_services(),
|
||||
copied_options.start_parameter_services());
|
||||
EXPECT_EQ(non_default_options.enable_logger_service(), copied_options.enable_logger_service());
|
||||
EXPECT_EQ(non_default_options.start_parameter_event_publisher(),
|
||||
copied_options.start_parameter_event_publisher());
|
||||
EXPECT_EQ(non_default_options.clock_type(), copied_options.clock_type());
|
||||
EXPECT_EQ(non_default_options.clock_qos(), copied_options.clock_qos());
|
||||
EXPECT_EQ(non_default_options.use_clock_thread(), copied_options.use_clock_thread());
|
||||
EXPECT_EQ(non_default_options.parameter_event_qos(), copied_options.parameter_event_qos());
|
||||
EXPECT_EQ(non_default_options.rosout_qos(), copied_options.rosout_qos());
|
||||
EXPECT_EQ(non_default_options.allow_undeclared_parameters(),
|
||||
copied_options.allow_undeclared_parameters());
|
||||
EXPECT_EQ(non_default_options.automatically_declare_parameters_from_overrides(),
|
||||
copied_options.automatically_declare_parameters_from_overrides());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestNodeOptions, append_parameter_override) {
|
||||
@@ -207,8 +258,10 @@ TEST(TestNodeOptions, append_parameter_override) {
|
||||
auto options = rclcpp::NodeOptions().arguments(expected_args).use_global_arguments(false);
|
||||
rclcpp::Parameter parameter("some_parameter", 10);
|
||||
options.append_parameter_override("some_parameter", 10);
|
||||
EXPECT_EQ(1u, options.parameter_overrides().size());
|
||||
options.append_parameter_override(parameter);
|
||||
EXPECT_EQ(2u, options.parameter_overrides().size());
|
||||
EXPECT_EQ(std::string("some_parameter"), options.parameter_overrides()[0].get_name());
|
||||
EXPECT_EQ(std::string("some_parameter"), options.parameter_overrides()[1].get_name());
|
||||
}
|
||||
|
||||
TEST(TestNodeOptions, rcl_node_options_fini_error) {
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <filesystem>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
@@ -936,7 +935,7 @@ TEST_F(TestParameterClient, async_parameter_load_parameters) {
|
||||
auto asynchronous_client =
|
||||
std::make_shared<rclcpp::AsyncParametersClient>(load_node, "/namespace/load_node");
|
||||
// load parameters
|
||||
std::filesystem::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
rcpputils::fs::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
const std::string parameters_filepath = (
|
||||
test_resources_path / "test_node" / "load_parameters.yaml").string();
|
||||
auto load_future = asynchronous_client->load_parameters(parameters_filepath);
|
||||
@@ -962,7 +961,7 @@ TEST_F(TestParameterClient, sync_parameter_load_parameters) {
|
||||
auto synchronous_client =
|
||||
std::make_shared<rclcpp::SyncParametersClient>(load_node);
|
||||
// load parameters
|
||||
std::filesystem::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
rcpputils::fs::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
const std::string parameters_filepath = (
|
||||
test_resources_path / "test_node" / "load_parameters.yaml").string();
|
||||
auto load_future = synchronous_client->load_parameters(parameters_filepath);
|
||||
@@ -984,7 +983,7 @@ TEST_F(TestParameterClient, async_parameter_load_parameters_complicated_regex) {
|
||||
auto asynchronous_client =
|
||||
std::make_shared<rclcpp::AsyncParametersClient>(load_node, "/namespace/load_node");
|
||||
// load parameters
|
||||
std::filesystem::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
rcpputils::fs::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
const std::string parameters_filepath = (
|
||||
test_resources_path / "test_node" / "load_complicated_parameters.yaml").string();
|
||||
auto load_future = asynchronous_client->load_parameters(parameters_filepath);
|
||||
@@ -1014,7 +1013,7 @@ TEST_F(TestParameterClient, async_parameter_load_no_valid_parameter) {
|
||||
auto asynchronous_client =
|
||||
std::make_shared<rclcpp::AsyncParametersClient>(load_node, "/namespace/load_node");
|
||||
// load parameters
|
||||
std::filesystem::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
rcpputils::fs::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
const std::string parameters_filepath = (
|
||||
test_resources_path / "test_node" / "no_valid_parameters.yaml").string();
|
||||
EXPECT_THROW(
|
||||
|
||||
@@ -72,6 +72,13 @@ protected:
|
||||
/*
|
||||
Testing filters.
|
||||
*/
|
||||
TEST_F(TestParameterEventFilter, invalide_arguments) {
|
||||
EXPECT_THROW(
|
||||
rclcpp::ParameterEventsFilter(nullptr, {"new"}, {nt}),
|
||||
std::invalid_argument
|
||||
);
|
||||
}
|
||||
|
||||
TEST_F(TestParameterEventFilter, full_by_type) {
|
||||
auto res = rclcpp::ParameterEventsFilter(
|
||||
full,
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <future>
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -178,6 +179,21 @@ TEST_F(TestPublisher, various_creation_signatures) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing publisher with intraprocess enabled and SystemDefaultQoS
|
||||
*/
|
||||
TEST_F(TestPublisher, test_publisher_with_system_default_qos) {
|
||||
initialize(rclcpp::NodeOptions().use_intra_process_comms(false));
|
||||
// explicitly enable intra-process comm with publisher option
|
||||
auto options = rclcpp::PublisherOptions();
|
||||
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
|
||||
using test_msgs::msg::Empty;
|
||||
ASSERT_NO_THROW(
|
||||
{
|
||||
auto publisher = node->create_publisher<Empty>("topic", rclcpp::SystemDefaultsQoS(), options);
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
Testing publisher with intraprocess enabled and invalid QoS
|
||||
*/
|
||||
@@ -423,11 +439,10 @@ TEST_F(TestPublisher, intra_process_publish_failures) {
|
||||
publisher->get_publisher_handle().get(), msg.get()));
|
||||
}
|
||||
}
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
node->create_publisher<test_msgs::msg::Empty>(
|
||||
"topic", rclcpp::QoS(0), options),
|
||||
std::invalid_argument(
|
||||
"intraprocess communication is not allowed with a zero qos history depth value"));
|
||||
// a zero depth with KEEP_LAST doesn't make sense,
|
||||
// this will be interpreted as SystemDefaultQoS by rclcpp.
|
||||
EXPECT_NO_THROW(
|
||||
node->create_publisher<test_msgs::msg::Empty>("topic", rclcpp::QoS(0), options));
|
||||
}
|
||||
|
||||
TEST_F(TestPublisher, inter_process_publish_failures) {
|
||||
@@ -702,11 +717,7 @@ TEST_F(TestPublisher, intra_process_transient_local) {
|
||||
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
|
||||
struct IntraProcessCallback
|
||||
{
|
||||
void callback_fun(size_t s)
|
||||
{
|
||||
(void) s;
|
||||
called = true;
|
||||
}
|
||||
void callback_fun(size_t) {called = true;}
|
||||
bool called = false;
|
||||
};
|
||||
rclcpp::SubscriptionOptions sub_options_ipm_disabled;
|
||||
@@ -763,3 +774,44 @@ TEST_F(TestPublisher, intra_process_transient_local) {
|
||||
EXPECT_FALSE(callback3.called);
|
||||
EXPECT_FALSE(callback4.called);
|
||||
}
|
||||
|
||||
TEST_F(TestPublisher, intra_process_inter_process_mix_transient_local) {
|
||||
constexpr auto history_depth = 10u;
|
||||
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||
|
||||
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> pub_options_ipm_enabled;
|
||||
pub_options_ipm_enabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
|
||||
|
||||
auto pub_ipm_enabled_transient_local_enabled = node->create_publisher<test_msgs::msg::Empty>(
|
||||
"topic1",
|
||||
rclcpp::QoS(rclcpp::KeepLast(history_depth)).transient_local(), pub_options_ipm_enabled);
|
||||
|
||||
test_msgs::msg::Empty msg;
|
||||
pub_ipm_enabled_transient_local_enabled->publish(msg);
|
||||
|
||||
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
|
||||
struct SubscriptionCallback
|
||||
{
|
||||
void callback_fun(size_t) {called.set_value();}
|
||||
std::promise<void> called;
|
||||
};
|
||||
rclcpp::SubscriptionOptions sub_options_ipm_disabled;
|
||||
sub_options_ipm_disabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
|
||||
SubscriptionCallback intra_callback, inter_callback;
|
||||
std::future<void> intra_callback_future = intra_callback.called.get_future(),
|
||||
inter_callback_future = inter_callback.called.get_future();
|
||||
auto sub_ipm_disabled_transient_local_enabled = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic1",
|
||||
rclcpp::QoS(rclcpp::KeepLast(history_depth)).transient_local(),
|
||||
do_nothing, sub_options_ipm_disabled);
|
||||
sub_ipm_disabled_transient_local_enabled->set_on_new_intra_process_message_callback(
|
||||
std::bind(&SubscriptionCallback::callback_fun, &intra_callback, std::placeholders::_1));
|
||||
sub_ipm_disabled_transient_local_enabled->set_on_new_message_callback(
|
||||
std::bind(&SubscriptionCallback::callback_fun, &inter_callback, std::placeholders::_1));
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node);
|
||||
EXPECT_EQ(executor.spin_until_future_complete(inter_callback_future,
|
||||
std::chrono::milliseconds(100)), rclcpp::FutureReturnCode::SUCCESS);
|
||||
EXPECT_EQ(executor.spin_until_future_complete(intra_callback_future,
|
||||
std::chrono::milliseconds(100)), rclcpp::FutureReturnCode::TIMEOUT);
|
||||
}
|
||||
|
||||
@@ -259,3 +259,15 @@ TEST(TestQoS, qos_check_compatible)
|
||||
EXPECT_FALSE(ret.reason.empty());
|
||||
}
|
||||
}
|
||||
|
||||
TEST(TestQoS, from_rmw_validity)
|
||||
{
|
||||
rmw_qos_profile_t invalid_qos;
|
||||
memset(&invalid_qos, 0, sizeof(invalid_qos));
|
||||
unsigned int n = 999;
|
||||
memcpy(&invalid_qos.history, &n, sizeof(n));
|
||||
|
||||
EXPECT_THROW({
|
||||
rclcpp::QoSInitialization::from_rmw(invalid_qos);
|
||||
}, std::invalid_argument);
|
||||
}
|
||||
|
||||
@@ -70,27 +70,31 @@ TEST_F(TestQosEvent, test_publisher_constructor)
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>(
|
||||
topic_name, 10, options);
|
||||
|
||||
// options arg with one of the callbacks
|
||||
options.event_callbacks.deadline_callback =
|
||||
[node = node.get()](rclcpp::QOSDeadlineOfferedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Offered deadline missed - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
publisher = node->create_publisher<test_msgs::msg::Empty>(
|
||||
topic_name, 10, options);
|
||||
if (rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED) &&
|
||||
rmw_event_type_is_supported(RMW_EVENT_LIVELINESS_LOST))
|
||||
{
|
||||
// options arg with one of the callbacks
|
||||
options.event_callbacks.deadline_callback =
|
||||
[node = node.get()](rclcpp::QOSDeadlineOfferedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Offered deadline missed - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
publisher = node->create_publisher<test_msgs::msg::Empty>(
|
||||
topic_name, 10, options);
|
||||
|
||||
// options arg with two of the callbacks
|
||||
options.event_callbacks.liveliness_callback =
|
||||
[node = node.get()](rclcpp::QOSLivelinessLostInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Liveliness lost - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
publisher = node->create_publisher<test_msgs::msg::Empty>(
|
||||
topic_name, 10, options);
|
||||
// options arg with two of the callbacks
|
||||
options.event_callbacks.liveliness_callback =
|
||||
[node = node.get()](rclcpp::QOSLivelinessLostInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Liveliness lost - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
publisher = node->create_publisher<test_msgs::msg::Empty>(
|
||||
topic_name, 10, options);
|
||||
}
|
||||
|
||||
// options arg with three of the callbacks
|
||||
options.event_callbacks.incompatible_qos_callback =
|
||||
@@ -115,28 +119,32 @@ TEST_F(TestQosEvent, test_subscription_constructor)
|
||||
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, options);
|
||||
|
||||
// options arg with one of the callbacks
|
||||
options.event_callbacks.deadline_callback =
|
||||
[node = node.get()](rclcpp::QOSDeadlineRequestedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Requested deadline missed - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, options);
|
||||
if (rmw_event_type_is_supported(RMW_EVENT_REQUESTED_DEADLINE_MISSED) &&
|
||||
rmw_event_type_is_supported(RMW_EVENT_LIVELINESS_CHANGED))
|
||||
{
|
||||
// options arg with one of the callbacks
|
||||
options.event_callbacks.deadline_callback =
|
||||
[node = node.get()](rclcpp::QOSDeadlineRequestedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Requested deadline missed - total %d (delta %d)",
|
||||
event.total_count, event.total_count_change);
|
||||
};
|
||||
subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, options);
|
||||
|
||||
// options arg with two of the callbacks
|
||||
options.event_callbacks.liveliness_callback =
|
||||
[node = node.get()](rclcpp::QOSLivelinessChangedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Liveliness changed - alive %d (delta %d), not alive %d (delta %d)",
|
||||
event.alive_count, event.alive_count_change,
|
||||
event.not_alive_count, event.not_alive_count_change);
|
||||
};
|
||||
subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, options);
|
||||
// options arg with two of the callbacks
|
||||
options.event_callbacks.liveliness_callback =
|
||||
[node = node.get()](rclcpp::QOSLivelinessChangedInfo & event) {
|
||||
RCLCPP_INFO(
|
||||
node->get_logger(),
|
||||
"Liveliness changed - alive %d (delta %d), not alive %d (delta %d)",
|
||||
event.alive_count, event.alive_count_change,
|
||||
event.not_alive_count, event.not_alive_count_change);
|
||||
};
|
||||
subscription = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, options);
|
||||
}
|
||||
|
||||
// options arg with three of the callbacks
|
||||
options.event_callbacks.incompatible_qos_callback =
|
||||
@@ -204,14 +212,18 @@ TEST_F(TestQosEvent, test_default_incompatible_qos_callbacks)
|
||||
const auto timeout = std::chrono::seconds(10);
|
||||
ex.spin_until_future_complete(log_msgs_future, timeout);
|
||||
|
||||
EXPECT_EQ(
|
||||
"New subscription discovered on topic '/ns/test_topic', requesting incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
pub_log_msg);
|
||||
EXPECT_EQ(
|
||||
"New publisher discovered on topic '/ns/test_topic', offering incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
sub_log_msg);
|
||||
if (qos_check_compatible(qos_profile_publisher,
|
||||
qos_profile_subscription).compatibility != rclcpp::QoSCompatibility::Ok)
|
||||
{
|
||||
EXPECT_EQ(
|
||||
"New subscription discovered on topic '/ns/test_topic', requesting incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
pub_log_msg);
|
||||
EXPECT_EQ(
|
||||
"New publisher discovered on topic '/ns/test_topic', offering incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
sub_log_msg);
|
||||
}
|
||||
|
||||
rcutils_logging_set_output_handler(original_output_handler);
|
||||
}
|
||||
@@ -223,7 +235,9 @@ TEST_F(TestQosEvent, construct_destruct_rcl_error) {
|
||||
|
||||
// This callback requires some type of parameter, but it could be anything
|
||||
auto callback = [](int) {};
|
||||
const rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
|
||||
const rcl_publisher_event_type_t event_type =
|
||||
!rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED) ?
|
||||
RCL_PUBLISHER_MATCHED : RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
|
||||
|
||||
{
|
||||
// Logs error and returns
|
||||
@@ -260,6 +274,10 @@ TEST_F(TestQosEvent, construct_destruct_rcl_error) {
|
||||
}
|
||||
|
||||
TEST_F(TestQosEvent, execute) {
|
||||
if (!rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED)) {
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
|
||||
auto rcl_handle = publisher->get_publisher_handle();
|
||||
|
||||
@@ -292,7 +310,10 @@ TEST_F(TestQosEvent, add_to_wait_set) {
|
||||
// This callback requires some type of parameter, but it could be anything
|
||||
auto callback = [](int) {};
|
||||
|
||||
rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
|
||||
const rcl_publisher_event_type_t event_type =
|
||||
!rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED) ?
|
||||
RCL_PUBLISHER_MATCHED : RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
|
||||
|
||||
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
|
||||
callback, rcl_publisher_event_init, rcl_handle, event_type);
|
||||
|
||||
@@ -314,6 +335,12 @@ TEST_F(TestQosEvent, add_to_wait_set) {
|
||||
|
||||
TEST_F(TestQosEvent, test_on_new_event_callback)
|
||||
{
|
||||
if (!rmw_event_type_is_supported(RMW_EVENT_REQUESTED_DEADLINE_MISSED) ||
|
||||
!rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED))
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
auto offered_deadline = rclcpp::Duration(std::chrono::milliseconds(1));
|
||||
auto requested_deadline = rclcpp::Duration(std::chrono::milliseconds(2));
|
||||
|
||||
@@ -359,17 +386,21 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
|
||||
auto sub = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
|
||||
auto dummy_cb = [](size_t count_events) {(void)count_events;};
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
|
||||
if (rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED) &&
|
||||
rmw_event_type_is_supported(RMW_EVENT_LIVELINESS_LOST))
|
||||
{
|
||||
EXPECT_NO_THROW(
|
||||
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
|
||||
EXPECT_NO_THROW(
|
||||
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_LIVELINESS_LOST));
|
||||
EXPECT_NO_THROW(
|
||||
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_LIVELINESS_LOST));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_LIVELINESS_LOST));
|
||||
EXPECT_NO_THROW(
|
||||
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_LIVELINESS_LOST));
|
||||
}
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS));
|
||||
@@ -383,11 +414,15 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
|
||||
EXPECT_NO_THROW(
|
||||
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_MATCHED));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
|
||||
if (rmw_event_type_is_supported(RMW_EVENT_REQUESTED_DEADLINE_MISSED) &&
|
||||
rmw_event_type_is_supported(RMW_EVENT_LIVELINESS_CHANGED))
|
||||
{
|
||||
EXPECT_NO_THROW(
|
||||
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
|
||||
EXPECT_NO_THROW(
|
||||
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
|
||||
}
|
||||
|
||||
EXPECT_NO_THROW(
|
||||
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
|
||||
@@ -407,24 +442,28 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
|
||||
EXPECT_NO_THROW(
|
||||
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_MATCHED));
|
||||
|
||||
std::function<void(size_t)> invalid_cb;
|
||||
if (rmw_event_type_is_supported(RMW_EVENT_REQUESTED_DEADLINE_MISSED) &&
|
||||
rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED))
|
||||
{
|
||||
std::function<void(size_t)> invalid_cb;
|
||||
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
sub_options.event_callbacks.deadline_callback = [](auto) {};
|
||||
sub = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, sub_options);
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
sub_options.event_callbacks.deadline_callback = [](auto) {};
|
||||
sub = node->create_subscription<test_msgs::msg::Empty>(
|
||||
topic_name, 10, message_callback, sub_options);
|
||||
|
||||
EXPECT_THROW(
|
||||
sub->set_on_new_qos_event_callback(invalid_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED),
|
||||
std::invalid_argument);
|
||||
EXPECT_THROW(
|
||||
sub->set_on_new_qos_event_callback(invalid_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED),
|
||||
std::invalid_argument);
|
||||
|
||||
rclcpp::PublisherOptions pub_options;
|
||||
pub_options.event_callbacks.deadline_callback = [](auto) {};
|
||||
pub = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10, pub_options);
|
||||
rclcpp::PublisherOptions pub_options;
|
||||
pub_options.event_callbacks.deadline_callback = [](auto) {};
|
||||
pub = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10, pub_options);
|
||||
|
||||
EXPECT_THROW(
|
||||
pub->set_on_new_qos_event_callback(invalid_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED),
|
||||
std::invalid_argument);
|
||||
EXPECT_THROW(
|
||||
pub->set_on_new_qos_event_callback(invalid_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED),
|
||||
std::invalid_argument);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestQosEvent, test_pub_matched_event_by_set_event_callback)
|
||||
|
||||
@@ -47,6 +47,21 @@ TEST_F(TestRate, rate_basics) {
|
||||
auto start = std::chrono::system_clock::now();
|
||||
rclcpp::Rate r(period);
|
||||
EXPECT_EQ(rclcpp::Duration(period), r.period());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_FALSE(r.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
ASSERT_EQ(RCL_SYSTEM_TIME, r.get_type());
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto one = std::chrono::system_clock::now();
|
||||
@@ -87,6 +102,23 @@ TEST_F(TestRate, wall_rate_basics) {
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
rclcpp::WallRate r(period);
|
||||
EXPECT_EQ(rclcpp::Duration(period), r.period());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_TRUE(r.is_steady());
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
ASSERT_EQ(RCL_STEADY_TIME, r.get_type());
|
||||
ASSERT_TRUE(r.sleep());
|
||||
auto one = std::chrono::steady_clock::now();
|
||||
@@ -118,6 +150,124 @@ TEST_F(TestRate, wall_rate_basics) {
|
||||
EXPECT_GT(epsilon, delta);
|
||||
}
|
||||
|
||||
/*
|
||||
Basic test for the deprecated GenericRate class.
|
||||
*/
|
||||
TEST_F(TestRate, generic_rate) {
|
||||
auto period = std::chrono::milliseconds(100);
|
||||
auto offset = std::chrono::milliseconds(50);
|
||||
auto epsilon = std::chrono::milliseconds(1);
|
||||
double overrun_ratio = 1.5;
|
||||
|
||||
{
|
||||
auto start = std::chrono::system_clock::now();
|
||||
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
rclcpp::GenericRate<std::chrono::system_clock> gr(period);
|
||||
ASSERT_FALSE(gr.is_steady());
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
ASSERT_EQ(gr.get_type(), RCL_SYSTEM_TIME);
|
||||
EXPECT_EQ(period, gr.period());
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto one = std::chrono::system_clock::now();
|
||||
auto delta = one - start;
|
||||
EXPECT_LT(period, delta + epsilon);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto two = std::chrono::system_clock::now();
|
||||
delta = two - start;
|
||||
EXPECT_LT(2 * period, delta);
|
||||
EXPECT_GT(2 * period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
auto two_offset = std::chrono::system_clock::now();
|
||||
gr.reset();
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto three = std::chrono::system_clock::now();
|
||||
delta = three - two_offset;
|
||||
EXPECT_LT(period, delta + epsilon);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset + period);
|
||||
auto four = std::chrono::system_clock::now();
|
||||
ASSERT_FALSE(gr.sleep());
|
||||
auto five = std::chrono::system_clock::now();
|
||||
delta = five - four;
|
||||
ASSERT_TRUE(epsilon > delta);
|
||||
}
|
||||
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
|
||||
rclcpp::GenericRate<std::chrono::steady_clock> gr(period);
|
||||
ASSERT_TRUE(gr.is_steady());
|
||||
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
ASSERT_EQ(gr.get_type(), RCL_STEADY_TIME);
|
||||
EXPECT_EQ(period, gr.period());
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto one = std::chrono::steady_clock::now();
|
||||
auto delta = one - start;
|
||||
EXPECT_LT(period, delta);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto two = std::chrono::steady_clock::now();
|
||||
delta = two - start;
|
||||
EXPECT_LT(2 * period, delta + epsilon);
|
||||
EXPECT_GT(2 * period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
auto two_offset = std::chrono::steady_clock::now();
|
||||
gr.reset();
|
||||
ASSERT_TRUE(gr.sleep());
|
||||
auto three = std::chrono::steady_clock::now();
|
||||
delta = three - two_offset;
|
||||
EXPECT_LT(period, delta);
|
||||
EXPECT_GT(period * overrun_ratio, delta);
|
||||
|
||||
rclcpp::sleep_for(offset + period);
|
||||
auto four = std::chrono::steady_clock::now();
|
||||
ASSERT_FALSE(gr.sleep());
|
||||
auto five = std::chrono::steady_clock::now();
|
||||
delta = five - four;
|
||||
EXPECT_GT(epsilon, delta);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestRate, from_double) {
|
||||
{
|
||||
rclcpp::Rate rate(1.0);
|
||||
@@ -140,14 +290,59 @@ TEST_F(TestRate, from_double) {
|
||||
TEST_F(TestRate, clock_types) {
|
||||
{
|
||||
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_SYSTEM_TIME));
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
EXPECT_FALSE(rate.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
EXPECT_EQ(RCL_SYSTEM_TIME, rate.get_type());
|
||||
}
|
||||
{
|
||||
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_STEADY_TIME));
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
EXPECT_TRUE(rate.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
EXPECT_EQ(RCL_STEADY_TIME, rate.get_type());
|
||||
}
|
||||
{
|
||||
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_ROS_TIME));
|
||||
// suppress deprecated warnings
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
EXPECT_FALSE(rate.is_steady());
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
EXPECT_EQ(RCL_ROS_TIME, rate.get_type());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -139,3 +139,15 @@ TEST(TestRingBufferImplementation, basic_usage_unique_ptr) {
|
||||
EXPECT_EQ(false, rb.has_data());
|
||||
EXPECT_EQ(false, rb.is_full());
|
||||
}
|
||||
|
||||
TEST(TestRingBufferImplementation, handle_nullptr_deletion) {
|
||||
rclcpp::experimental::buffers::RingBufferImplementation<std::unique_ptr<int>> rb(3);
|
||||
rb.enqueue(std::make_unique<int>(42));
|
||||
rb.enqueue(nullptr); // intentionally enqueuing nullptr
|
||||
rb.enqueue(std::make_unique<int>(84));
|
||||
auto all_data = rb.get_all_data();
|
||||
EXPECT_EQ(3u, all_data.size());
|
||||
EXPECT_EQ(42, *(all_data[0]));
|
||||
EXPECT_EQ(nullptr, all_data[1]);
|
||||
EXPECT_EQ(84, *(all_data[2]));
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
@@ -123,7 +124,7 @@ TEST_F(TestServiceSub, construction_and_destruction) {
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto service = node->create_service<rcl_interfaces::srv::ListParameters>(
|
||||
auto service = subnode->create_service<rcl_interfaces::srv::ListParameters>(
|
||||
"invalid_service?", callback);
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
@@ -335,7 +336,7 @@ TEST_F(TestService, rcl_service_request_subscription_get_actual_qos_error) {
|
||||
TEST_F(TestService, server_qos) {
|
||||
rclcpp::ServicesQoS qos_profile;
|
||||
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
|
||||
rclcpp::Duration duration(std::chrono::nanoseconds(1));
|
||||
rclcpp::Duration duration(std::chrono::milliseconds(1));
|
||||
qos_profile.deadline(duration);
|
||||
qos_profile.lifespan(duration);
|
||||
qos_profile.liveliness_lease_duration(duration);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include <rcl/service_introspection.h>
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
@@ -92,9 +93,16 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
|
||||
request->set__int64_value(42);
|
||||
|
||||
client->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
service->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
|
||||
// Wait for the introspection to attach to our subscription
|
||||
size_t tries = 1000;
|
||||
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
ASSERT_EQ(sub->get_publisher_count(), 2u);
|
||||
|
||||
auto future = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
@@ -149,9 +157,11 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
|
||||
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
|
||||
{
|
||||
client->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
|
||||
service->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
|
||||
|
||||
ASSERT_EQ(sub->get_publisher_count(), 0);
|
||||
|
||||
auto request = std::make_shared<BasicTypes::Request>();
|
||||
request->set__bool_value(true);
|
||||
@@ -169,9 +179,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
|
||||
events.clear();
|
||||
|
||||
client->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
service->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
|
||||
|
||||
// Wait for the introspection to attach to our subscription
|
||||
size_t tries = 1000;
|
||||
while (this->sub->get_publisher_count() < 1 && tries-- > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
ASSERT_EQ(sub->get_publisher_count(), 1u);
|
||||
|
||||
future = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
@@ -186,9 +203,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
|
||||
events.clear();
|
||||
|
||||
client->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
|
||||
service->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
|
||||
// Wait for the introspection to attach to our subscription
|
||||
tries = 1000;
|
||||
while (this->sub->get_publisher_count() < 1 && tries-- > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
ASSERT_EQ(sub->get_publisher_count(), 1u);
|
||||
|
||||
future = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
@@ -203,9 +227,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
|
||||
events.clear();
|
||||
|
||||
client->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
service->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
|
||||
// Wait for the introspection to attach to our subscription
|
||||
tries = 1000;
|
||||
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
ASSERT_EQ(sub->get_publisher_count(), 2u);
|
||||
|
||||
future = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
@@ -221,9 +252,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
|
||||
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_content)
|
||||
{
|
||||
client->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
service->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
|
||||
// Wait for the introspection to attach to our subscription
|
||||
size_t tries = 1000;
|
||||
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
ASSERT_EQ(sub->get_publisher_count(), 2u);
|
||||
|
||||
auto request = std::make_shared<BasicTypes::Request>();
|
||||
request->set__bool_value(true);
|
||||
@@ -245,9 +283,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
|
||||
events.clear();
|
||||
|
||||
client->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
service->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
|
||||
// Wait for the introspection to attach to our subscription
|
||||
tries = 1000;
|
||||
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
ASSERT_EQ(sub->get_publisher_count(), 2u);
|
||||
|
||||
future = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
@@ -278,9 +323,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
|
||||
events.clear();
|
||||
|
||||
client->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
|
||||
service->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
|
||||
// Wait for the introspection to attach to our subscription
|
||||
tries = 1000;
|
||||
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
ASSERT_EQ(sub->get_publisher_count(), 2u);
|
||||
|
||||
future = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
@@ -311,9 +363,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
|
||||
events.clear();
|
||||
|
||||
client->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
service->configure_introspection(
|
||||
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
|
||||
|
||||
// Wait for the introspection to attach to our subscription
|
||||
tries = 1000;
|
||||
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
}
|
||||
ASSERT_EQ(sub->get_publisher_count(), 2u);
|
||||
|
||||
future = client->async_send_request(request);
|
||||
ASSERT_EQ(
|
||||
|
||||
@@ -30,7 +30,14 @@
|
||||
|
||||
#include "test_msgs/msg/basic_types.hpp"
|
||||
|
||||
class TestContentFilterSubscription : public ::testing::Test
|
||||
#ifdef RMW_IMPLEMENTATION
|
||||
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
|
||||
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
|
||||
#else
|
||||
# define CLASSNAME(NAME, SUFFIX) NAME
|
||||
#endif
|
||||
|
||||
class CLASSNAME (TestContentFilterSubscription, RMW_IMPLEMENTATION) : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
@@ -106,8 +113,7 @@ bool operator==(const test_msgs::msg::BasicTypes & m1, const test_msgs::msg::Bas
|
||||
m1.uint64_value == m2.uint64_value;
|
||||
}
|
||||
|
||||
TEST_F(TestContentFilterSubscription, is_cft_enabled)
|
||||
{
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), is_cft_enabled) {
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_subscription_is_cft_enabled, false);
|
||||
@@ -121,8 +127,7 @@ TEST_F(TestContentFilterSubscription, is_cft_enabled)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestContentFilterSubscription, get_content_filter_error)
|
||||
{
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content_filter_error) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_subscription_get_content_filter, RCL_RET_ERROR);
|
||||
|
||||
@@ -132,8 +137,7 @@ TEST_F(TestContentFilterSubscription, get_content_filter_error)
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
TEST_F(TestContentFilterSubscription, set_content_filter_error)
|
||||
{
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content_filter_error) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_subscription_set_content_filter, RCL_RET_ERROR);
|
||||
|
||||
@@ -144,8 +148,7 @@ TEST_F(TestContentFilterSubscription, set_content_filter_error)
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
|
||||
TEST_F(TestContentFilterSubscription, get_content_filter)
|
||||
{
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content_filter) {
|
||||
rclcpp::ContentFilterOptions options;
|
||||
|
||||
if (sub->is_cft_enabled()) {
|
||||
@@ -161,8 +164,7 @@ TEST_F(TestContentFilterSubscription, get_content_filter)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestContentFilterSubscription, set_content_filter)
|
||||
{
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content_filter) {
|
||||
if (sub->is_cft_enabled()) {
|
||||
EXPECT_NO_THROW(
|
||||
sub->set_content_filter(filter_expression_init, expression_parameters_2));
|
||||
@@ -173,8 +175,7 @@ TEST_F(TestContentFilterSubscription, set_content_filter)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestContentFilterSubscription, content_filter_get_begin)
|
||||
{
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_get_begin) {
|
||||
using namespace std::chrono_literals;
|
||||
{
|
||||
test_msgs::msg::BasicTypes msg;
|
||||
@@ -216,8 +217,7 @@ TEST_F(TestContentFilterSubscription, content_filter_get_begin)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestContentFilterSubscription, content_filter_get_later)
|
||||
{
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_get_later) {
|
||||
using namespace std::chrono_literals;
|
||||
{
|
||||
test_msgs::msg::BasicTypes msg;
|
||||
@@ -264,8 +264,7 @@ TEST_F(TestContentFilterSubscription, content_filter_get_later)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestContentFilterSubscription, content_filter_reset)
|
||||
{
|
||||
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_reset) {
|
||||
using namespace std::chrono_literals;
|
||||
{
|
||||
test_msgs::msg::BasicTypes msg;
|
||||
@@ -312,8 +311,11 @@ TEST_F(TestContentFilterSubscription, content_filter_reset)
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestContentFilterSubscription, create_two_content_filters_with_same_topic_name_and_destroy)
|
||||
{
|
||||
TEST_F(
|
||||
CLASSNAME(
|
||||
TestContentFilterSubscription,
|
||||
RMW_IMPLEMENTATION), create_two_content_filters_with_same_topic_name_and_destroy) {
|
||||
|
||||
// Create another content filter
|
||||
auto options = rclcpp::SubscriptionOptions();
|
||||
|
||||
|
||||
@@ -60,7 +60,7 @@ TEST_F(TestSubscriptionOptions, topic_statistics_options_default_and_set) {
|
||||
EXPECT_EQ(options.topic_stats_options.state, rclcpp::TopicStatisticsState::NodeDefault);
|
||||
EXPECT_EQ(options.topic_stats_options.publish_topic, defaultPublishTopic);
|
||||
EXPECT_EQ(options.topic_stats_options.publish_period, 1s);
|
||||
EXPECT_EQ(options.topic_stats_options.qos, rclcpp::SystemDefaultsQoS());
|
||||
EXPECT_EQ(options.topic_stats_options.qos, rclcpp::SystemDefaultsQoS().keep_last(10));
|
||||
|
||||
options.topic_stats_options.state = rclcpp::TopicStatisticsState::Enable;
|
||||
options.topic_stats_options.publish_topic = "topic_statistics";
|
||||
|
||||
@@ -138,8 +138,6 @@ TEST_F(TestTime, conversions) {
|
||||
|
||||
EXPECT_ANY_THROW(rclcpp::Time(-1, 1));
|
||||
|
||||
EXPECT_ANY_THROW(rclcpp::Time(-1));
|
||||
|
||||
EXPECT_ANY_THROW(
|
||||
{
|
||||
rclcpp::Time assignment(1, 2);
|
||||
@@ -170,6 +168,48 @@ TEST_F(TestTime, conversions) {
|
||||
EXPECT_EQ(time_msg.nanosec, HALF_SEC_IN_NS);
|
||||
EXPECT_EQ(rclcpp::Time(time_msg).nanoseconds(), ONE_AND_HALF_SEC_IN_NS);
|
||||
}
|
||||
|
||||
{
|
||||
// Can rclcpp::Time be negative or not? The following constructor works:
|
||||
rclcpp::Time time(-HALF_SEC_IN_NS);
|
||||
auto time_msg = static_cast<builtin_interfaces::msg::Time>(time);
|
||||
EXPECT_EQ(time_msg.sec, -1);
|
||||
EXPECT_EQ(time_msg.nanosec, HALF_SEC_IN_NS);
|
||||
|
||||
// The opposite conversion throws...
|
||||
EXPECT_ANY_THROW(
|
||||
{
|
||||
rclcpp::Time negative_time(time_msg);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
// Can rclcpp::Time be negative or not? The following constructor works:
|
||||
rclcpp::Time time(-ONE_SEC_IN_NS);
|
||||
auto time_msg = static_cast<builtin_interfaces::msg::Time>(time);
|
||||
EXPECT_EQ(time_msg.sec, -1);
|
||||
EXPECT_EQ(time_msg.nanosec, 0u);
|
||||
|
||||
// The opposite conversion throws...
|
||||
EXPECT_ANY_THROW(
|
||||
{
|
||||
rclcpp::Time negative_time(time_msg);
|
||||
});
|
||||
}
|
||||
|
||||
{
|
||||
// Can rclcpp::Time be negative or not? The following constructor works:
|
||||
rclcpp::Time time(-ONE_AND_HALF_SEC_IN_NS);
|
||||
auto time_msg = static_cast<builtin_interfaces::msg::Time>(time);
|
||||
EXPECT_EQ(time_msg.sec, -2);
|
||||
EXPECT_EQ(time_msg.nanosec, HALF_SEC_IN_NS);
|
||||
|
||||
// The opposite conversion throws...
|
||||
EXPECT_ANY_THROW(
|
||||
{
|
||||
rclcpp::Time negative_time(time_msg);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestTime, operators) {
|
||||
@@ -286,18 +326,31 @@ TEST_F(TestTime, overflow_detectors) {
|
||||
|
||||
TEST_F(TestTime, overflows) {
|
||||
rclcpp::Time max_time(std::numeric_limits<rcl_time_point_value_t>::max());
|
||||
rclcpp::Time min_time(std::numeric_limits<rcl_time_point_value_t>::min());
|
||||
rclcpp::Duration one(1ns);
|
||||
rclcpp::Duration two(2ns);
|
||||
|
||||
// Cross max
|
||||
// Cross min/max
|
||||
EXPECT_THROW(max_time + one, std::overflow_error);
|
||||
EXPECT_THROW(min_time - one, std::underflow_error);
|
||||
EXPECT_THROW(max_time - min_time, std::overflow_error);
|
||||
EXPECT_THROW(min_time - max_time, std::underflow_error);
|
||||
EXPECT_THROW(rclcpp::Time(max_time) += one, std::overflow_error);
|
||||
EXPECT_THROW(rclcpp::Time(min_time) -= one, std::underflow_error);
|
||||
EXPECT_NO_THROW(max_time - max_time);
|
||||
EXPECT_NO_THROW(min_time - min_time);
|
||||
|
||||
// Cross zero
|
||||
// Cross zero in both directions
|
||||
rclcpp::Time one_time(1);
|
||||
EXPECT_THROW(one_time - two, std::runtime_error);
|
||||
EXPECT_THROW(rclcpp::Time(one_time) -= two, std::runtime_error);
|
||||
EXPECT_NO_THROW(one_time - two);
|
||||
EXPECT_NO_THROW(rclcpp::Time(one_time) -= two);
|
||||
|
||||
rclcpp::Time minus_one_time(-1);
|
||||
EXPECT_NO_THROW(minus_one_time + two);
|
||||
EXPECT_NO_THROW(rclcpp::Time(minus_one_time) += two);
|
||||
|
||||
EXPECT_NO_THROW(one_time - minus_one_time);
|
||||
EXPECT_NO_THROW(minus_one_time - one_time);
|
||||
|
||||
rclcpp::Time two_time(2);
|
||||
EXPECT_NO_THROW(one_time - two_time);
|
||||
@@ -379,24 +432,41 @@ TEST_F(TestTime, test_overflow_underflow_throws) {
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Time(INT64_MAX) + rclcpp::Duration(1ns),
|
||||
std::overflow_error("addition leads to int64_t overflow"));
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Time(INT64_MIN) + rclcpp::Duration(-1ns),
|
||||
std::underflow_error("addition leads to int64_t underflow"));
|
||||
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Time(INT64_MAX) - rclcpp::Duration(-1ns),
|
||||
std::overflow_error("time subtraction leads to int64_t overflow"));
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Time(INT64_MIN) - rclcpp::Duration(1ns),
|
||||
std::underflow_error("time subtraction leads to int64_t underflow"));
|
||||
|
||||
test_time = rclcpp::Time(INT64_MAX);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time += rclcpp::Duration(1ns),
|
||||
std::overflow_error("addition leads to int64_t overflow"));
|
||||
test_time = rclcpp::Time(INT64_MIN);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time += rclcpp::Duration(-1ns),
|
||||
std::underflow_error("addition leads to int64_t underflow"));
|
||||
|
||||
test_time = rclcpp::Time(INT64_MAX);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time -= rclcpp::Duration(-1ns),
|
||||
std::overflow_error("time subtraction leads to int64_t overflow"));
|
||||
test_time = rclcpp::Time(INT64_MIN);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time -= rclcpp::Duration(1ns),
|
||||
std::underflow_error("time subtraction leads to int64_t underflow"));
|
||||
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Duration::from_nanoseconds(INT64_MAX) + rclcpp::Time(1),
|
||||
std::overflow_error("addition leads to int64_t overflow"));
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
test_time = rclcpp::Duration::from_nanoseconds(INT64_MIN) + rclcpp::Time(-1),
|
||||
std::underflow_error("addition leads to int64_t underflow"));
|
||||
}
|
||||
|
||||
class TestClockSleep : public ::testing::Test
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user