Compare commits
78 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
83c282a161 | ||
|
|
226044022a | ||
|
|
7e9ff5f4c7 | ||
|
|
64dd644469 | ||
|
|
322b5f5d79 | ||
|
|
e854bb29cd | ||
|
|
88ebea94e9 | ||
|
|
1e6767ac13 | ||
|
|
37e3688026 | ||
|
|
9b654942f9 | ||
|
|
f12e3c69dc | ||
|
|
bcc14755f9 | ||
|
|
4567b6ec80 | ||
|
|
0be8aa013e | ||
|
|
41d3a27437 | ||
|
|
c50f0c9c3d | ||
|
|
f8aea8cc51 | ||
|
|
16290fb352 | ||
|
|
e133cc65f6 | ||
|
|
50a1e50133 | ||
|
|
1a0092a65b | ||
|
|
2813045a96 | ||
|
|
51dfdc3708 | ||
|
|
1f408e3b19 | ||
|
|
63105cd8a6 | ||
|
|
a78d0cbd33 | ||
|
|
918363d081 | ||
|
|
97c386ce40 | ||
|
|
1a3dfaf45c | ||
|
|
f7056c0d86 | ||
|
|
2f71d6e249 | ||
|
|
e846f56224 | ||
|
|
ee94bc63e4 | ||
|
|
b7c789328a | ||
|
|
4b1c125cac | ||
|
|
dfaaf4739a | ||
|
|
e6b6faf152 | ||
|
|
f961ca7855 | ||
|
|
6da8363582 | ||
|
|
ab7cf878c2 | ||
|
|
496c45549b | ||
|
|
2739327ee9 | ||
|
|
4e4f0cf43b | ||
|
|
c46896731c | ||
|
|
45adf6565f | ||
|
|
6f5c6f45d7 | ||
|
|
9b1e6c9d52 | ||
|
|
a4d7210b9c | ||
|
|
fcc0261c49 | ||
|
|
b1fdb18f1e | ||
|
|
647bd65e28 | ||
|
|
54b8f9cc97 | ||
|
|
bdf1f8f78a | ||
|
|
004db2b393 | ||
|
|
304b51c3a1 | ||
|
|
069a001893 | ||
|
|
c743c173e6 | ||
|
|
8de4b90512 | ||
|
|
8230d15ef7 | ||
|
|
7d68b9096f | ||
|
|
eeaa5222a1 | ||
|
|
a13dc0f157 | ||
|
|
9f1de85079 | ||
|
|
5149a095c1 | ||
|
|
16795dd8bf | ||
|
|
f4923c6f43 | ||
|
|
7c096888ca | ||
|
|
d8d83a0ee6 | ||
|
|
3bc364a10b | ||
|
|
22df1d593a | ||
|
|
343b29b617 | ||
|
|
42b0b5775b | ||
|
|
f7185dc129 | ||
|
|
5f912eb58e | ||
|
|
2cd8900dd2 | ||
|
|
cf6141330a | ||
|
|
de666d2bf4 | ||
|
|
55939613a0 |
13
.github/workflows/mirror-rolling-to-master.yaml
vendored
Normal file
13
.github/workflows/mirror-rolling-to-master.yaml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
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
|
||||
2
CODEOWNERS
Normal file
2
CODEOWNERS
Normal file
@@ -0,0 +1,2 @@
|
||||
# This file was generated by https://github.com/audrow/update-ros2-repos
|
||||
* @ivanpauno @hidmic @wjwwood
|
||||
@@ -2,144 +2,92 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
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)
|
||||
29.2.0 (2024-11-25)
|
||||
-------------------
|
||||
* 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]
|
||||
* accept custom allocator for LoanedMessage. (`#2672 <https://github.com/ros2/rclcpp/issues/2672>`_)
|
||||
* Contributors: Tomoya Fujita
|
||||
|
||||
28.1.8 (2025-04-02)
|
||||
29.1.0 (2024-11-20)
|
||||
-------------------
|
||||
* a couple of typo fixes in doc section for LoanedMessage. (`#2676 <https://github.com/ros2/rclcpp/issues/2676>`_)
|
||||
* Make sure callback_end tracepoint is triggered in AnyServiceCallback (`#2670 <https://github.com/ros2/rclcpp/issues/2670>`_)
|
||||
* Correct the incorrect comments in generic_client.hpp (`#2662 <https://github.com/ros2/rclcpp/issues/2662>`_)
|
||||
* Fix NodeOptions assignment operator (`#2656 <https://github.com/ros2/rclcpp/issues/2656>`_)
|
||||
* set QoS History KEEP_ALL explicitly for statistics publisher. (`#2650 <https://github.com/ros2/rclcpp/issues/2650>`_)
|
||||
* Fix test_intra_process_manager.cpp with rmw_zenoh_cpp (`#2653 <https://github.com/ros2/rclcpp/issues/2653>`_)
|
||||
* Fixed test_events_executors in zenoh (`#2643 <https://github.com/ros2/rclcpp/issues/2643>`_)
|
||||
* rmw_fastrtps supports service event gid uniqueness test. (`#2638 <https://github.com/ros2/rclcpp/issues/2638>`_)
|
||||
* print warning if event callback is not supported instead of passing exception. (`#2648 <https://github.com/ros2/rclcpp/issues/2648>`_)
|
||||
* Implement callback support of async_send_request for service generic client (`#2614 <https://github.com/ros2/rclcpp/issues/2614>`_)
|
||||
* Contributors: Alejandro Hernández Cordero, Barry Xu, Chris Lalancette, Christophe Bedard, Romain DESILLE, Tomoya Fujita
|
||||
|
||||
28.1.7 (2025-03-26)
|
||||
29.0.0 (2024-10-03)
|
||||
-------------------
|
||||
* 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
|
||||
* Fixed test qos rmw zenoh (`#2639 <https://github.com/ros2/rclcpp/issues/2639>`_)
|
||||
* verify client gid uniqueness for a single service event. (`#2636 <https://github.com/ros2/rclcpp/issues/2636>`_)
|
||||
* Skip some tests in test_qos_event and run others with event types supported by rmw_zenoh (`#2626 <https://github.com/ros2/rclcpp/issues/2626>`_)
|
||||
* Shutdown the context before context's destructor is invoked in tests (`#2633 <https://github.com/ros2/rclcpp/issues/2633>`_)
|
||||
* Skip rmw zenoh content filtering tests (`#2627 <https://github.com/ros2/rclcpp/issues/2627>`_)
|
||||
* Use InvalidServiceTypeError for unavailable service type in GenericClient (`#2629 <https://github.com/ros2/rclcpp/issues/2629>`_)
|
||||
* Implement generic service (`#2617 <https://github.com/ros2/rclcpp/issues/2617>`_)
|
||||
* fix events-executor warm-up bug and add unit-tests (`#2591 <https://github.com/ros2/rclcpp/issues/2591>`_)
|
||||
* remove unnecessary gtest-skip in test_executors (`#2600 <https://github.com/ros2/rclcpp/issues/2600>`_)
|
||||
* Correct node name in service test code (`#2615 <https://github.com/ros2/rclcpp/issues/2615>`_)
|
||||
* Minor naming fixes for ParameterValue to_string() function (`#2609 <https://github.com/ros2/rclcpp/issues/2609>`_)
|
||||
* Removed clang warnings (`#2605 <https://github.com/ros2/rclcpp/issues/2605>`_)
|
||||
* Fix a couple of issues in the documentation. (`#2608 <https://github.com/ros2/rclcpp/issues/2608>`_)
|
||||
* deprecate the static single threaded executor (`#2598 <https://github.com/ros2/rclcpp/issues/2598>`_)
|
||||
* Fix name of ParameterEventHandler class in doc (`#2604 <https://github.com/ros2/rclcpp/issues/2604>`_)
|
||||
* subscriber_statistics_collectors\_ should be protected by mutex. (`#2592 <https://github.com/ros2/rclcpp/issues/2592>`_)
|
||||
* Fix bug in timers lifecycle for events executor (`#2586 <https://github.com/ros2/rclcpp/issues/2586>`_)
|
||||
* fix rclcpp/test/rclcpp/CMakeLists.txt to check for the correct targets existance (`#2596 <https://github.com/ros2/rclcpp/issues/2596>`_)
|
||||
* Shut down context during init if logging config fails (`#2594 <https://github.com/ros2/rclcpp/issues/2594>`_)
|
||||
* Make more of the Waitable API abstract (`#2593 <https://github.com/ros2/rclcpp/issues/2593>`_)
|
||||
* Contributors: Alberto Soragna, Alejandro Hernández Cordero, Alexis Pojomovsky, Barry Xu, Chris Lalancette, Christophe Bedard, Kang, Hsin-Yi, Tomoya Fujita
|
||||
|
||||
28.1.6 (2024-12-18)
|
||||
28.3.3 (2024-07-29)
|
||||
-------------------
|
||||
* 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.
|
||||
* Only compile the tests once. (`#2590 <https://github.com/ros2/rclcpp/issues/2590>`_)
|
||||
* Contributors: Chris Lalancette
|
||||
|
||||
28.3.2 (2024-07-24)
|
||||
-------------------
|
||||
* 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
|
||||
---------
|
||||
(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
|
||||
* 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 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>`_)
|
||||
* Split test_executors.cpp even further. (`#2572 <https://github.com/ros2/rclcpp/issues/2572>`_)
|
||||
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
|
||||
@@ -147,70 +95,32 @@ 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.
|
||||
(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
|
||||
* 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 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]
|
||||
* Contributors: Alberto Soragna, Alejandro Hernández Cordero, Barry Xu, Chris Lalancette
|
||||
|
||||
28.1.3 (2024-06-27)
|
||||
28.3.1 (2024-06-25)
|
||||
-------------------
|
||||
* 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]
|
||||
* 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
|
||||
|
||||
28.1.2 (2024-05-13)
|
||||
28.3.0 (2024-06-17)
|
||||
-------------------
|
||||
* 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]
|
||||
* 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
|
||||
|
||||
28.1.1 (2024-04-24)
|
||||
28.2.0 (2024-04-26)
|
||||
-------------------
|
||||
* 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]
|
||||
* 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
|
||||
|
||||
28.1.0 (2024-04-16)
|
||||
-------------------
|
||||
|
||||
@@ -77,6 +77,7 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/future_return_code.cpp
|
||||
src/rclcpp/generic_client.cpp
|
||||
src/rclcpp/generic_publisher.cpp
|
||||
src/rclcpp/generic_service.cpp
|
||||
src/rclcpp/generic_subscription.cpp
|
||||
src/rclcpp/graph_listener.cpp
|
||||
src/rclcpp/guard_condition.cpp
|
||||
|
||||
@@ -165,11 +165,13 @@ public:
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
|
||||
cb(request_header, std::move(request));
|
||||
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
return nullptr;
|
||||
}
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
|
||||
cb(service_handle, request_header, std::move(request));
|
||||
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
return nullptr;
|
||||
}
|
||||
// auto response = allocate_shared<typename ServiceT::Response, Allocator>();
|
||||
|
||||
@@ -59,46 +59,6 @@ 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,14 +70,6 @@ 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().
|
||||
@@ -436,15 +428,6 @@ 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().
|
||||
@@ -490,7 +473,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 subscription.
|
||||
* \param[in] client_options options for the client.
|
||||
*/
|
||||
Client(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
|
||||
@@ -60,13 +60,6 @@ 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.
|
||||
*/
|
||||
@@ -224,13 +217,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 then the time jump;
|
||||
* callbacks will be executed whose threshold is greater than the time jump;
|
||||
* The logic will first call selected pre_callbacks and then all selected
|
||||
* post_callbacks.
|
||||
*
|
||||
@@ -239,7 +232,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
|
||||
* then the threshold.
|
||||
* than 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
|
||||
@@ -267,117 +260,6 @@ 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,28 +47,9 @@ 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_profile;
|
||||
options.qos = qos.get_rmw_qos_profile();
|
||||
|
||||
auto cli = rclcpp::Client<ServiceT>::make_shared(
|
||||
node_base.get(),
|
||||
@@ -80,7 +61,6 @@ create_client(
|
||||
node_services->add_client(cli_base_ptr, group);
|
||||
return cli;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CREATE_CLIENT_HPP_
|
||||
|
||||
102
rclcpp/include/rclcpp/create_generic_service.hpp
Normal file
102
rclcpp/include/rclcpp/create_generic_service.hpp
Normal file
@@ -0,0 +1,102 @@
|
||||
// Copyright 2024 Sony Group Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__CREATE_GENERIC_SERVICE_HPP_
|
||||
#define RCLCPP__CREATE_GENERIC_SERVICE_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/generic_service.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/get_node_services_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_services_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
/// Create a generic service with a given type.
|
||||
/**
|
||||
* \param[in] node_base NodeBaseInterface implementation of the node on which
|
||||
* to create the generic service.
|
||||
* \param[in] node_services NodeServicesInterface implementation of the node on
|
||||
* which to create the service.
|
||||
* \param[in] service_name The name on which the service is accessible.
|
||||
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
|
||||
* \param[in] callback The callback to call when the service gets a request.
|
||||
* \param[in] qos Quality of service profile for the service.
|
||||
* \param[in] group Callback group to handle the reply to service calls.
|
||||
* \return Shared pointer to the created service.
|
||||
*/
|
||||
template<typename CallbackT>
|
||||
typename rclcpp::GenericService::SharedPtr
|
||||
create_generic_service(
|
||||
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
|
||||
std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
CallbackT && callback,
|
||||
const rclcpp::QoS & qos,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
rclcpp::GenericServiceCallback any_service_callback;
|
||||
any_service_callback.set(std::forward<CallbackT>(callback));
|
||||
|
||||
rcl_service_options_t service_options = rcl_service_get_default_options();
|
||||
service_options.qos = qos.get_rmw_qos_profile();
|
||||
|
||||
auto serv = GenericService::make_shared(
|
||||
node_base->get_shared_rcl_node_handle(),
|
||||
service_name, service_type, any_service_callback, service_options);
|
||||
auto serv_base_ptr = std::dynamic_pointer_cast<ServiceBase>(serv);
|
||||
node_services->add_service(serv_base_ptr, group);
|
||||
return serv;
|
||||
}
|
||||
|
||||
/// Create a generic service with a given type.
|
||||
/**
|
||||
* The NodeT type needs to have NodeBaseInterface implementation and NodeServicesInterface
|
||||
* implementation of the node which to create the generic service.
|
||||
*
|
||||
* \param[in] node The node on which to create the generic service.
|
||||
* \param[in] service_name The name on which the service is accessible.
|
||||
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
|
||||
* \param[in] callback The callback to call when the service gets a request.
|
||||
* \param[in] qos Quality of service profile for the service.
|
||||
* \param[in] group Callback group to handle the reply to service calls.
|
||||
* \return Shared pointer to the created service.
|
||||
*/
|
||||
template<typename NodeT, typename CallbackT>
|
||||
typename rclcpp::GenericService::SharedPtr
|
||||
create_generic_service(
|
||||
NodeT node,
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
CallbackT && callback,
|
||||
const rclcpp::QoS & qos,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
return create_generic_service<CallbackT>(
|
||||
rclcpp::node_interfaces::get_node_base_interface(node),
|
||||
rclcpp::node_interfaces::get_node_services_interface(node),
|
||||
service_name,
|
||||
service_type,
|
||||
std::forward<CallbackT>(callback), qos.get_rmw_qos_profile(), group);
|
||||
}
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CREATE_GENERIC_SERVICE_HPP_
|
||||
@@ -233,8 +233,6 @@ protected:
|
||||
size_t wait_set_event_index_;
|
||||
};
|
||||
|
||||
using QOSEventHandlerBase [[deprecated("Use rclcpp::EventHandlerBase")]] = EventHandlerBase;
|
||||
|
||||
template<typename EventCallbackT, typename ParentHandleT>
|
||||
class EventHandler : public EventHandlerBase
|
||||
{
|
||||
@@ -311,11 +309,6 @@ 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_
|
||||
|
||||
@@ -100,6 +100,15 @@ public:
|
||||
{}
|
||||
};
|
||||
|
||||
class InvalidServiceTypeError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
InvalidServiceTypeError()
|
||||
: std::runtime_error("Service type is invalid.") {}
|
||||
explicit InvalidServiceTypeError(const std::string & msg)
|
||||
: std::runtime_error(msg) {}
|
||||
};
|
||||
|
||||
class UnimplementedError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -541,8 +541,9 @@ protected:
|
||||
*
|
||||
* \param[in] notify if true will execute a trigger that will wake up a waiting executor
|
||||
*/
|
||||
void
|
||||
trigger_entity_recollect(bool notify);
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
handle_updated_entities(bool notify);
|
||||
|
||||
/// Spinning state, used to prevent multi threaded calls to spin and to cancel blocking spins.
|
||||
std::atomic_bool spinning;
|
||||
|
||||
@@ -122,6 +122,14 @@ 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.
|
||||
@@ -142,7 +150,10 @@ 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_;
|
||||
|
||||
|
||||
@@ -31,9 +31,15 @@ namespace executors
|
||||
/// Static executor implementation
|
||||
/**
|
||||
* This executor is a static version of the original single threaded executor.
|
||||
* It's static because it doesn't reconstruct the executable list for every iteration.
|
||||
* It contains some performance optimization to avoid unnecessary reconstructions of
|
||||
* the executable list for every iteration.
|
||||
* All nodes, callbackgroups, timers, subscriptions etc. are created before
|
||||
* spin() is called, and modified only when an entity is added/removed to/from a node.
|
||||
* This executor is deprecated because these performance improvements have now been
|
||||
* applied to all other executors.
|
||||
* This executor is also considered unstable due to known bugs.
|
||||
* See the unit-tests that are only applied to `StandardExecutors` for information
|
||||
* on the known limitations.
|
||||
*
|
||||
* To run this executor instead of SingleThreadedExecutor replace:
|
||||
* rclcpp::executors::SingleThreadedExecutor exec;
|
||||
@@ -44,7 +50,8 @@ namespace executors
|
||||
* exec.spin();
|
||||
* exec.remove_node(node);
|
||||
*/
|
||||
class StaticSingleThreadedExecutor : public rclcpp::Executor
|
||||
class [[deprecated("Use rclcpp::executors::SingleThreadedExecutor")]] StaticSingleThreadedExecutor
|
||||
: public rclcpp::Executor
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(StaticSingleThreadedExecutor)
|
||||
|
||||
@@ -255,13 +255,9 @@ private:
|
||||
std::vector<BufferT> result_vtr;
|
||||
result_vtr.reserve(size_);
|
||||
for (size_t id = 0; id < size_; ++id) {
|
||||
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);
|
||||
}
|
||||
result_vtr.emplace_back(
|
||||
new typename is_std_unique_ptr<T>::Ptr_type(
|
||||
*(ring_buffer_[(read_index_ + id) % capacity_])));
|
||||
}
|
||||
return result_vtr;
|
||||
}
|
||||
|
||||
@@ -59,8 +59,6 @@ namespace executors
|
||||
*/
|
||||
class EventsExecutor : public rclcpp::Executor
|
||||
{
|
||||
friend class EventsExecutorEntitiesCollector;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(EventsExecutor)
|
||||
|
||||
@@ -72,7 +70,7 @@ public:
|
||||
* \param[in] options Options used to configure the executor.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit EventsExecutor(
|
||||
EventsExecutor(
|
||||
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue = std::make_unique<
|
||||
rclcpp::experimental::executors::SimpleEventsQueue>(),
|
||||
bool execute_timers_separate_thread = false,
|
||||
@@ -128,87 +126,6 @@ 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
|
||||
@@ -220,6 +137,11 @@ 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)
|
||||
|
||||
@@ -227,9 +149,9 @@ private:
|
||||
void
|
||||
execute_event(const ExecutorEvent & event);
|
||||
|
||||
/// Collect entities from callback groups and refresh the current collection with them
|
||||
/// Rebuilds the executor's notify waitable, as we can't use the one built in the base class
|
||||
void
|
||||
refresh_current_collection_from_callback_groups();
|
||||
setup_notify_waitable();
|
||||
|
||||
/// Refresh the current collection using the provided new_collection
|
||||
void
|
||||
@@ -253,6 +175,11 @@ 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()) {
|
||||
@@ -273,16 +200,6 @@ 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_;
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
#include <memory>
|
||||
#include <future>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
@@ -35,8 +36,8 @@ namespace rclcpp
|
||||
class GenericClient : public ClientBase
|
||||
{
|
||||
public:
|
||||
using Request = void *; // Serialized data pointer of request message
|
||||
using Response = void *; // Serialized data pointer of response message
|
||||
using Request = void *; // Deserialized data pointer of request message
|
||||
using Response = void *; // Deserialized data pointer of response message
|
||||
|
||||
using SharedResponse = std::shared_ptr<void>;
|
||||
|
||||
@@ -46,6 +47,8 @@ public:
|
||||
using Future = std::future<SharedResponse>;
|
||||
using SharedFuture = std::shared_future<SharedResponse>;
|
||||
|
||||
using CallbackType = std::function<void (SharedFuture)>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericClient)
|
||||
|
||||
/// A convenient GenericClient::Future and request id pair.
|
||||
@@ -76,6 +79,20 @@ public:
|
||||
~FutureAndRequestId() = default;
|
||||
};
|
||||
|
||||
/// A convenient GenericClient::SharedFuture and request id pair.
|
||||
/**
|
||||
* Public members:
|
||||
* - future: a std::shared_future<SharedResponse>.
|
||||
* - request_id: the request id associated with the future.
|
||||
*
|
||||
* All the other methods are equivalent to the ones std::shared_future provides.
|
||||
*/
|
||||
struct SharedFutureAndRequestId
|
||||
: detail::FutureAndRequestId<std::shared_future<SharedResponse>>
|
||||
{
|
||||
using detail::FutureAndRequestId<std::shared_future<SharedResponse>>::FutureAndRequestId;
|
||||
};
|
||||
|
||||
GenericClient(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
|
||||
@@ -106,16 +123,16 @@ public:
|
||||
* If the future never completes,
|
||||
* e.g. the call to Executor::spin_until_future_complete() times out,
|
||||
* GenericClient::remove_pending_request() must be called to clean the client internal state.
|
||||
* Not doing so will make the `Client` instance to use more memory each time a response is not
|
||||
* received from the service server.
|
||||
* Not doing so will make the `GenericClient` instance to use more memory each time a response is
|
||||
* not received from the service server.
|
||||
*
|
||||
* ```cpp
|
||||
* auto future = client->async_send_request(my_request);
|
||||
* auto future = generic_client->async_send_request(my_request);
|
||||
* if (
|
||||
* rclcpp::FutureReturnCode::TIMEOUT ==
|
||||
* executor->spin_until_future_complete(future, timeout))
|
||||
* {
|
||||
* client->remove_pending_request(future);
|
||||
* generic_client->remove_pending_request(future);
|
||||
* // handle timeout
|
||||
* } else {
|
||||
* handle_response(future.get());
|
||||
@@ -129,6 +146,45 @@ public:
|
||||
FutureAndRequestId
|
||||
async_send_request(const Request request);
|
||||
|
||||
/// Send a request to the service server and schedule a callback in the executor.
|
||||
/**
|
||||
* Similar to the previous overload, but a callback will automatically be called when a response
|
||||
* is received.
|
||||
*
|
||||
* If the callback is never called, because we never got a reply for the service server,
|
||||
* remove_pending_request() has to be called with the returned request id or
|
||||
* prune_pending_requests().
|
||||
* Not doing so will make the `GenericClient` instance use more memory each time a response is not
|
||||
* received from the service server.
|
||||
* In this case, it's convenient to setup a timer to cleanup the pending requests.
|
||||
*
|
||||
* \param[in] request request to be send.
|
||||
* \param[in] cb callback that will be called when we get a response for this request.
|
||||
* \return the request id representing the request just sent.
|
||||
*/
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
CallbackType
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
SharedFutureAndRequestId
|
||||
async_send_request(const Request request, CallbackT && cb)
|
||||
{
|
||||
Promise promise;
|
||||
auto shared_future = promise.get_future().share();
|
||||
auto req_id = async_send_request_impl(
|
||||
request,
|
||||
std::make_tuple(
|
||||
CallbackType{std::forward<CallbackT>(cb)},
|
||||
shared_future,
|
||||
std::move(promise)));
|
||||
return SharedFutureAndRequestId{std::move(shared_future), req_id};
|
||||
}
|
||||
|
||||
/// Clean all pending requests older than a time_point.
|
||||
/**
|
||||
* \param[in] time_point Requests that were sent before this point are going to be removed.
|
||||
@@ -149,15 +205,52 @@ public:
|
||||
pruned_requests);
|
||||
}
|
||||
|
||||
/// Clean all pending requests.
|
||||
/**
|
||||
* \return number of pending requests that were removed.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
prune_pending_requests();
|
||||
|
||||
/// Cleanup a pending request.
|
||||
/**
|
||||
* This notifies the client that we have waited long enough for a response from the server
|
||||
* to come, we have given up and we are not waiting for a response anymore.
|
||||
*
|
||||
* Not calling this will make the client start using more memory for each request
|
||||
* that never got a reply from the server.
|
||||
*
|
||||
* \param[in] request_id request id returned by async_send_request().
|
||||
* \return true when a pending request was removed, false if not (e.g. a response was received).
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
remove_pending_request(
|
||||
int64_t request_id);
|
||||
|
||||
/// Cleanup a pending request.
|
||||
/**
|
||||
* Convenient overload, same as:
|
||||
*
|
||||
* `GenericClient::remove_pending_request(this, future.request_id)`.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
remove_pending_request(
|
||||
const FutureAndRequestId & future);
|
||||
|
||||
/// Cleanup a pending request.
|
||||
/**
|
||||
* Convenient overload, same as:
|
||||
*
|
||||
* `GenericClient::remove_pending_request(this, future.request_id)`.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
remove_pending_request(
|
||||
const SharedFutureAndRequestId & future);
|
||||
|
||||
/// Take the next response for this client.
|
||||
/**
|
||||
* \sa ClientBase::take_type_erased_response().
|
||||
@@ -179,9 +272,12 @@ public:
|
||||
}
|
||||
|
||||
protected:
|
||||
using CallbackTypeValueVariant = std::tuple<CallbackType, SharedFuture, Promise>;
|
||||
using CallbackInfoVariant = std::variant<
|
||||
std::promise<SharedResponse>>; // Use variant for extension
|
||||
std::promise<SharedResponse>,
|
||||
CallbackTypeValueVariant>; // Use variant for extension
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
int64_t
|
||||
async_send_request_impl(
|
||||
const Request request,
|
||||
|
||||
308
rclcpp/include/rclcpp/generic_service.hpp
Normal file
308
rclcpp/include/rclcpp/generic_service.hpp
Normal file
@@ -0,0 +1,308 @@
|
||||
// Copyright 2024 Sony Group Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__GENERIC_SERVICE_HPP_
|
||||
#define RCLCPP__GENERIC_SERVICE_HPP_
|
||||
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <variant>
|
||||
|
||||
#include "rclcpp/typesupport_helpers.hpp"
|
||||
|
||||
#include "rosidl_runtime_c/service_type_support_struct.h"
|
||||
#include "rosidl_typesupport_introspection_cpp/identifier.hpp"
|
||||
#include "rosidl_typesupport_introspection_cpp/service_introspection.hpp"
|
||||
|
||||
#include "service.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
class GenericService;
|
||||
|
||||
class GenericServiceCallback
|
||||
{
|
||||
public:
|
||||
using SharedRequest = std::shared_ptr<void>;
|
||||
using SharedResponse = std::shared_ptr<void>;
|
||||
|
||||
GenericServiceCallback()
|
||||
: callback_(std::monostate{})
|
||||
{}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if_t<!detail::can_be_nullptr<CallbackT>::value, int> = 0>
|
||||
void
|
||||
set(CallbackT && callback)
|
||||
{
|
||||
// Workaround Windows issue with std::bind
|
||||
if constexpr (
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrCallback>(callback);
|
||||
} else if constexpr ( // NOLINT, can't satisfy both cpplint and uncrustify
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrWithRequestHeaderCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallbackWithServiceHandle
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
|
||||
} else {
|
||||
// the else clause is not needed, but anyways we should only be doing this instead
|
||||
// of all the above workaround ...
|
||||
callback_ = std::forward<CallbackT>(callback);
|
||||
}
|
||||
}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if_t<detail::can_be_nullptr<CallbackT>::value, int> = 0>
|
||||
void
|
||||
set(CallbackT && callback)
|
||||
{
|
||||
if (!callback) {
|
||||
throw std::invalid_argument("AnyServiceCallback::set(): callback cannot be nullptr");
|
||||
}
|
||||
// Workaround Windows issue with std::bind
|
||||
if constexpr (
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrWithRequestHeaderCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallback
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
|
||||
} else if constexpr ( // NOLINT
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrDeferResponseCallbackWithServiceHandle
|
||||
>::value)
|
||||
{
|
||||
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
|
||||
} else {
|
||||
// the else clause is not needed, but anyways we should only be doing this instead
|
||||
// of all the above workaround ...
|
||||
callback_ = std::forward<CallbackT>(callback);
|
||||
}
|
||||
}
|
||||
|
||||
SharedResponse
|
||||
dispatch(
|
||||
const std::shared_ptr<rclcpp::GenericService> & service_handle,
|
||||
const std::shared_ptr<rmw_request_id_t> & request_header,
|
||||
SharedRequest request,
|
||||
SharedRequest response)
|
||||
{
|
||||
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
|
||||
if (std::holds_alternative<std::monostate>(callback_)) {
|
||||
// TODO(ivanpauno): Remove the set method, and force the users of this class
|
||||
// to pass a callback at construnciton.
|
||||
throw std::runtime_error{"unexpected request without any callback set"};
|
||||
}
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
|
||||
cb(request_header, std::move(request));
|
||||
return nullptr;
|
||||
}
|
||||
if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
|
||||
cb(service_handle, request_header, std::move(request));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (std::holds_alternative<SharedPtrCallback>(callback_)) {
|
||||
(void)request_header;
|
||||
const auto & cb = std::get<SharedPtrCallback>(callback_);
|
||||
cb(std::move(request), std::move(response));
|
||||
} else if (std::holds_alternative<SharedPtrWithRequestHeaderCallback>(callback_)) {
|
||||
const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
|
||||
cb(request_header, std::move(request), std::move(response));
|
||||
}
|
||||
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
|
||||
return response;
|
||||
}
|
||||
|
||||
void register_callback_for_tracing()
|
||||
{
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
std::visit(
|
||||
[this](auto && arg) {
|
||||
if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
|
||||
char * symbol = tracetools::get_symbol(arg);
|
||||
TRACETOOLS_DO_TRACEPOINT(
|
||||
rclcpp_callback_register,
|
||||
static_cast<const void *>(this),
|
||||
symbol);
|
||||
std::free(symbol);
|
||||
}
|
||||
}, callback_);
|
||||
#endif // TRACETOOLS_DISABLED
|
||||
}
|
||||
|
||||
private:
|
||||
using SharedPtrCallback = std::function<void (SharedRequest, SharedResponse)>;
|
||||
using SharedPtrWithRequestHeaderCallback = std::function<
|
||||
void (
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
SharedRequest,
|
||||
SharedResponse
|
||||
)>;
|
||||
using SharedPtrDeferResponseCallback = std::function<
|
||||
void (
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
SharedRequest
|
||||
)>;
|
||||
using SharedPtrDeferResponseCallbackWithServiceHandle = std::function<
|
||||
void (
|
||||
std::shared_ptr<rclcpp::GenericService>,
|
||||
std::shared_ptr<rmw_request_id_t>,
|
||||
SharedRequest
|
||||
)>;
|
||||
|
||||
std::variant<
|
||||
std::monostate,
|
||||
SharedPtrCallback,
|
||||
SharedPtrWithRequestHeaderCallback,
|
||||
SharedPtrDeferResponseCallback,
|
||||
SharedPtrDeferResponseCallbackWithServiceHandle> callback_;
|
||||
};
|
||||
|
||||
class GenericService
|
||||
: public ServiceBase,
|
||||
public std::enable_shared_from_this<GenericService>
|
||||
{
|
||||
public:
|
||||
using Request = void *; // Serialized/Deserialized data pointer of request message
|
||||
using Response = void *; // Serialized/Deserialized data pointer of response message
|
||||
using SharedRequest = std::shared_ptr<void>;
|
||||
using SharedResponse = std::shared_ptr<void>;
|
||||
using CallbackType = std::function<void (const SharedRequest, SharedResponse)>;
|
||||
|
||||
using CallbackWithHeaderType =
|
||||
std::function<void (const std::shared_ptr<rmw_request_id_t>,
|
||||
const SharedRequest,
|
||||
SharedResponse)>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericService)
|
||||
|
||||
/// Default constructor.
|
||||
/**
|
||||
* The constructor for a Service is almost never called directly.
|
||||
* Instead, services should be instantiated through the function
|
||||
* rclcpp::create_service().
|
||||
*
|
||||
* \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] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
|
||||
* \param[in] any_callback User defined callback to call when a client request is received.
|
||||
* \param[in] service_options options for the service.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
GenericService(
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
GenericServiceCallback any_callback,
|
||||
rcl_service_options_t & service_options);
|
||||
|
||||
GenericService() = delete;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~GenericService() {}
|
||||
|
||||
/// Take the next request from the service.
|
||||
/**
|
||||
* \sa ServiceBase::take_type_erased_request().
|
||||
*
|
||||
* \param[out] request_out The reference to a service deserialized request object
|
||||
* into which the middleware will copy the taken request.
|
||||
* \param[out] request_id_out The output id for the request which can be used
|
||||
* to associate response with this request in the future.
|
||||
* \returns true if the request was taken, otherwise false.
|
||||
* \throws rclcpp::exceptions::RCLError based exceptions if the underlying
|
||||
* rcl calls fail.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
take_request(SharedRequest request_out, rmw_request_id_t & request_id_out);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void>
|
||||
create_request() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<void>
|
||||
create_response();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rmw_request_id_t>
|
||||
create_request_header() override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
handle_request(
|
||||
std::shared_ptr<rmw_request_id_t> request_header,
|
||||
std::shared_ptr<void> request) override;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
send_response(rmw_request_id_t & req_id, SharedResponse & response);
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(GenericService)
|
||||
|
||||
GenericServiceCallback any_callback_;
|
||||
|
||||
std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;
|
||||
const rosidl_typesupport_introspection_cpp::MessageMembers * request_members_;
|
||||
const rosidl_typesupport_introspection_cpp::MessageMembers * response_members_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
#endif // RCLCPP__GENERIC_SERVICE_HPP_
|
||||
@@ -31,21 +31,20 @@ namespace rclcpp
|
||||
template<typename MessageT, typename AllocatorT = std::allocator<void>>
|
||||
class LoanedMessage
|
||||
{
|
||||
public:
|
||||
using MessageAllocatorTraits = rclcpp::allocator::AllocRebind<MessageT, AllocatorT>;
|
||||
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
|
||||
|
||||
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
|
||||
* The underlying middleware is queried to determine whether it is able to allocate the
|
||||
* appropriate memory for this message type or not.
|
||||
* In the case that the middleware cannot loan messages, the passed in allocator instance
|
||||
* is used to allocate the message within the scope of this class.
|
||||
* Otherwise, the allocator is ignored and the allocation is solely performed
|
||||
* 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.
|
||||
@@ -53,12 +52,12 @@ public:
|
||||
* 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
|
||||
* \param[in] allocator Allocator instance in case middleware cannot allocate messages
|
||||
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
|
||||
*/
|
||||
LoanedMessage(
|
||||
const rclcpp::PublisherBase & pub,
|
||||
std::allocator<MessageT> allocator)
|
||||
MessageAllocator allocator)
|
||||
: pub_(pub),
|
||||
message_(nullptr),
|
||||
message_allocator_(std::move(allocator))
|
||||
@@ -82,36 +81,6 @@ 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,6 +15,7 @@
|
||||
#ifndef RCLCPP__LOGGER_HPP_
|
||||
#define RCLCPP__LOGGER_HPP_
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -77,6 +78,34 @@ 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,
|
||||
* see rcl_logging_get_logging_directory().
|
||||
*
|
||||
* \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,
|
||||
@@ -86,8 +115,8 @@ get_node_logger(const rcl_node_t * node);
|
||||
* \throws rclcpp::exceptions::RCLError if an unexpected error occurs.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rcpputils::fs::path
|
||||
get_logging_directory();
|
||||
std::filesystem::path
|
||||
get_log_directory();
|
||||
|
||||
class Logger
|
||||
{
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
#include "rclcpp/event.hpp"
|
||||
#include "rclcpp/generic_client.hpp"
|
||||
#include "rclcpp/generic_publisher.hpp"
|
||||
#include "rclcpp/generic_service.hpp"
|
||||
#include "rclcpp/generic_subscription.hpp"
|
||||
#include "rclcpp/logger.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
@@ -257,22 +258,6 @@ 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.
|
||||
@@ -287,24 +272,6 @@ 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.
|
||||
@@ -337,6 +304,24 @@ public:
|
||||
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Create and return a GenericService.
|
||||
/**
|
||||
* \param[in] service_name The topic to service on.
|
||||
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool"
|
||||
* \param[in] callback User-defined callback function.
|
||||
* \param[in] qos Quality of service profile for the service.
|
||||
* \param[in] group Callback group to call the service.
|
||||
* \return Shared pointer to the created service.
|
||||
*/
|
||||
template<typename CallbackT>
|
||||
typename rclcpp::GenericService::SharedPtr
|
||||
create_generic_service(
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
CallbackT && callback,
|
||||
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
|
||||
rclcpp::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Create and return a GenericPublisher.
|
||||
/**
|
||||
* The returned pointer will never be empty, but this function can throw various exceptions, for
|
||||
@@ -1015,8 +1000,6 @@ 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;
|
||||
|
||||
@@ -40,6 +40,7 @@
|
||||
#include "rclcpp/create_generic_subscription.hpp"
|
||||
#include "rclcpp/create_publisher.hpp"
|
||||
#include "rclcpp/create_service.hpp"
|
||||
#include "rclcpp/create_generic_service.hpp"
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/create_timer.hpp"
|
||||
#include "rclcpp/detail/resolve_enable_topic_statistics.hpp"
|
||||
@@ -154,22 +155,6 @@ 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(
|
||||
@@ -187,20 +172,22 @@ Node::create_service(
|
||||
group);
|
||||
}
|
||||
|
||||
template<typename ServiceT, typename CallbackT>
|
||||
typename rclcpp::Service<ServiceT>::SharedPtr
|
||||
Node::create_service(
|
||||
template<typename CallbackT>
|
||||
typename rclcpp::GenericService::SharedPtr
|
||||
Node::create_generic_service(
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
CallbackT && callback,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
const rclcpp::QoS & qos,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
return rclcpp::create_service<ServiceT, CallbackT>(
|
||||
return rclcpp::create_generic_service<CallbackT>(
|
||||
node_base_,
|
||||
node_services_,
|
||||
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
|
||||
service_type,
|
||||
std::forward<CallbackT>(callback),
|
||||
qos_profile,
|
||||
qos,
|
||||
group);
|
||||
}
|
||||
|
||||
|
||||
@@ -52,8 +52,6 @@ struct OnSetParametersCallbackHandle
|
||||
std::function<
|
||||
rcl_interfaces::msg::SetParametersResult(
|
||||
const std::vector<rclcpp::Parameter> &)>;
|
||||
using OnParametersSetCallbackType [[deprecated("use OnSetParametersCallbackType instead")]] =
|
||||
OnSetParametersCallbackType;
|
||||
|
||||
OnSetParametersCallbackType callback;
|
||||
};
|
||||
|
||||
@@ -52,37 +52,6 @@ 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.
|
||||
@@ -103,31 +72,6 @@ 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
|
||||
@@ -150,31 +94,6 @@ 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
|
||||
@@ -383,19 +302,6 @@ 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,
|
||||
@@ -408,23 +314,6 @@ 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,
|
||||
@@ -441,19 +330,6 @@ 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,
|
||||
@@ -466,23 +342,6 @@ 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,
|
||||
@@ -499,28 +358,6 @@ 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,
|
||||
|
||||
@@ -67,19 +67,23 @@ struct ParameterEventCallbackHandle
|
||||
* The first step is to instantiate a ParameterEventHandler, providing a ROS node to use
|
||||
* to create any required subscriptions:
|
||||
*
|
||||
* auto param_handler = std::make_shared<rclcpp::ParameterEventHandler>(node);
|
||||
* ```cpp
|
||||
* auto param_handler = std::make_shared<rclcpp::ParameterEventHandler>(node);
|
||||
* ```
|
||||
*
|
||||
* Next, you can supply a callback to the add_parameter_callback method, as follows:
|
||||
*
|
||||
* auto cb1 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb1: Received an update to parameter \"%s\" of type %s: \"%ld\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_int());
|
||||
* };
|
||||
* auto handle1 = param_handler->add_parameter_callback("an_int_param", cb1);
|
||||
* ```cpp
|
||||
* auto cb1 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb1: Received an update to parameter \"%s\" of type %s: \"%ld\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_int());
|
||||
* };
|
||||
* auto handle1 = param_handler->add_parameter_callback("an_int_param", cb1);
|
||||
* ```
|
||||
*
|
||||
* In this case, we didn't supply a node name (the third, optional, parameter) so the
|
||||
* default will be to monitor for changes to the "an_int_param" parameter associated with
|
||||
@@ -92,16 +96,18 @@ struct ParameterEventCallbackHandle
|
||||
* You may also monitor for changes to parameters in other nodes by supplying the node
|
||||
* name to add_parameter_callback:
|
||||
*
|
||||
* auto cb2 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb2: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* };
|
||||
* auto handle2 = param_handler->add_parameter_callback(
|
||||
* "some_remote_param_name", cb2, "some_remote_node_name");
|
||||
* ```cpp
|
||||
* auto cb2 = [&node](const rclcpp::Parameter & p) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb2: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* };
|
||||
* auto handle2 = param_handler->add_parameter_callback(
|
||||
* "some_remote_param_name", cb2, "some_remote_node_name");
|
||||
* ```
|
||||
*
|
||||
* In this case, the callback will be invoked whenever "some_remote_param_name" changes
|
||||
* on remote node "some_remote_node_name".
|
||||
@@ -109,7 +115,9 @@ struct ParameterEventCallbackHandle
|
||||
* To remove a parameter callback, reset the callback handle smart pointer or call
|
||||
* remove_parameter_callback, passing the handle returned from add_parameter_callback:
|
||||
*
|
||||
* param_handler->remove_parameter_callback(handle2);
|
||||
* ```cpp
|
||||
* param_handler->remove_parameter_callback(handle2);
|
||||
* ```
|
||||
*
|
||||
* You can also monitor for *all* parameter changes, using add_parameter_event_callback.
|
||||
* In this case, the callback will be invoked whenever any parameter changes in the system.
|
||||
@@ -117,40 +125,42 @@ struct ParameterEventCallbackHandle
|
||||
* is convenient to use a regular expression on the node names or namespaces of interest.
|
||||
* For example:
|
||||
*
|
||||
* auto cb3 =
|
||||
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent & event) {
|
||||
* // Look for any updates to parameters in "/a_namespace" as well as any parameter changes
|
||||
* // to our own node ("this_node")
|
||||
* std::regex re("(/a_namespace/.*)|(/this_node)");
|
||||
* if (regex_match(event.node, re)) {
|
||||
* // Now that we know the event matches the regular expression we scanned for, we can
|
||||
* // use 'get_parameter_from_event' to get a specific parameter name that we're looking for
|
||||
* rclcpp::Parameter p;
|
||||
* if (rclcpp::ParameterEventsSubscriber::get_parameter_from_event(
|
||||
* event, p, remote_param_name, fqn))
|
||||
* {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* }
|
||||
*
|
||||
* // You can also use 'get_parameter*s*_from_event' to enumerate all changes that came
|
||||
* // in on this event
|
||||
* auto params = rclcpp::ParameterEventsSubscriber::get_parameters_from_event(event);
|
||||
* for (auto & p : params) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.value_to_string().c_str());
|
||||
* }
|
||||
* ```cpp
|
||||
* auto cb3 =
|
||||
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent & event) {
|
||||
* // Look for any updates to parameters in "/a_namespace" as well as any parameter changes
|
||||
* // to our own node ("this_node")
|
||||
* std::regex re("(/a_namespace/.*)|(/this_node)");
|
||||
* if (regex_match(event.node, re)) {
|
||||
* // Now that we know the event matches the regular expression we scanned for, we can
|
||||
* // use 'get_parameter_from_event' to get a specific parameter name that we're looking for
|
||||
* rclcpp::Parameter p;
|
||||
* if (rclcpp::ParameterEventHandler::get_parameter_from_event(
|
||||
* event, p, remote_param_name, fqn))
|
||||
* {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.as_string().c_str());
|
||||
* }
|
||||
* };
|
||||
* auto handle3 = param_handler->add_parameter_event_callback(cb3);
|
||||
*
|
||||
* // You can also use 'get_parameter*s*_from_event' to enumerate all changes that came
|
||||
* // in on this event
|
||||
* auto params = rclcpp::ParameterEventHandler::get_parameters_from_event(event);
|
||||
* for (auto & p : params) {
|
||||
* RCLCPP_INFO(
|
||||
* node->get_logger(),
|
||||
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
|
||||
* p.get_name().c_str(),
|
||||
* p.get_type_name().c_str(),
|
||||
* p.value_to_string().c_str());
|
||||
* }
|
||||
* }
|
||||
* };
|
||||
* auto handle3 = param_handler->add_parameter_event_callback(cb3);
|
||||
* ```
|
||||
*
|
||||
* For both parameter callbacks and parameter event callbacks, when multiple callbacks are added,
|
||||
* the callbacks are invoked last-in, first-called order (LIFO).
|
||||
@@ -160,7 +170,9 @@ struct ParameterEventCallbackHandle
|
||||
*
|
||||
* To remove a parameter event callback, reset the callback smart pointer or use:
|
||||
*
|
||||
* param_handler->remove_event_parameter_callback(handle3);
|
||||
* ```cpp
|
||||
* param_handler->remove_event_parameter_callback(handle3);
|
||||
* ```
|
||||
*/
|
||||
class ParameterEventHandler
|
||||
{
|
||||
|
||||
@@ -45,7 +45,6 @@ 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,20 +40,6 @@ 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,
|
||||
|
||||
@@ -359,7 +359,7 @@ private:
|
||||
/// Return the value of a parameter as a string
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
to_string(const ParameterValue & type);
|
||||
to_string(const ParameterValue & value);
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -96,22 +96,6 @@ 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,
|
||||
@@ -128,8 +112,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 Subcription.
|
||||
* \param[in] options Options for the subscription.
|
||||
* \param[in] qos QoS profile for the publisher.
|
||||
* \param[in] options Options for the publisher.
|
||||
*/
|
||||
Publisher(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
@@ -162,7 +146,8 @@ public:
|
||||
const rclcpp::QoS & qos,
|
||||
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
|
||||
{
|
||||
(void)qos;
|
||||
// Topic is unused for now.
|
||||
(void)topic;
|
||||
(void)options;
|
||||
|
||||
// If needed, setup intra process communication.
|
||||
@@ -170,26 +155,22 @@ 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>();
|
||||
// Check if the QoS is compatible with intra-process.
|
||||
auto qos_profile = get_actual_qos();
|
||||
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
// Register the publisher with the intra process manager.
|
||||
if (qos.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication on topic '" + topic +
|
||||
"' allowed only with keep last history qos policy");
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos_profile.depth() == 0) {
|
||||
if (qos.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication on topic '" + topic +
|
||||
"' is not allowed with a zero qos history depth value");
|
||||
"intraprocess communication is not allowed with a zero qos history depth value");
|
||||
}
|
||||
if (qos_profile.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
|
||||
if (qos.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_profile,
|
||||
qos,
|
||||
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,
|
||||
@@ -206,7 +187,7 @@ public:
|
||||
* the loaned message will be directly allocated in the middleware.
|
||||
* If not, the message allocator of this rclcpp::Publisher instance is being used.
|
||||
*
|
||||
* With a call to \sa `publish` the LoanedMessage instance is being returned to the middleware
|
||||
* With a call to `publish` the LoanedMessage instance is being returned to the middleware
|
||||
* or free'd accordingly to the allocator.
|
||||
* If the message is not being published but processed differently, the destructor of this
|
||||
* class will either return the message to the middleware or deallocate it via the internal
|
||||
@@ -251,12 +232,8 @@ 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() || buffer_;
|
||||
get_subscription_count() > get_intra_process_subscription_count();
|
||||
|
||||
if (inter_process_publish_needed) {
|
||||
auto shared_msg =
|
||||
@@ -333,11 +310,8 @@ 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() || buffer_;
|
||||
get_subscription_count() > get_intra_process_subscription_count();
|
||||
|
||||
if (inter_process_publish_needed) {
|
||||
auto ros_msg_ptr = std::make_shared<ROSMessageType>();
|
||||
@@ -433,13 +407,6 @@ 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,10 +39,6 @@ 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;
|
||||
|
||||
@@ -54,82 +50,6 @@ 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:
|
||||
@@ -149,11 +69,6 @@ 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 subscription.
|
||||
* \param[in] service_options options for the service.
|
||||
*/
|
||||
Service(
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
|
||||
@@ -90,18 +90,6 @@ 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>;
|
||||
@@ -159,13 +147,11 @@ public:
|
||||
auto qos_profile = get_actual_qos();
|
||||
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication on topic '" + topic_name +
|
||||
"' allowed only with keep last history qos policy");
|
||||
"intraprocess communication allowed only with keep last history qos policy");
|
||||
}
|
||||
if (qos_profile.depth() == 0) {
|
||||
throw std::invalid_argument(
|
||||
"intraprocess communication on topic '" + topic_name +
|
||||
"' is not allowed with 0 depth qos policy");
|
||||
"intraprocess communication is not allowed with 0 depth qos policy");
|
||||
}
|
||||
|
||||
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
|
||||
|
||||
@@ -49,6 +49,7 @@ 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);
|
||||
|
||||
@@ -172,12 +172,11 @@ private:
|
||||
{
|
||||
auto received_message_age = std::make_unique<ReceivedMessageAge>();
|
||||
received_message_age->Start();
|
||||
subscriber_statistics_collectors_.emplace_back(std::move(received_message_age));
|
||||
|
||||
auto received_message_period = std::make_unique<ReceivedMessagePeriod>();
|
||||
received_message_period->Start();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
subscriber_statistics_collectors_.emplace_back(std::move(received_message_age));
|
||||
subscriber_statistics_collectors_.emplace_back(std::move(received_message_period));
|
||||
}
|
||||
|
||||
|
||||
@@ -38,25 +38,6 @@ RCLCPP_PUBLIC
|
||||
std::shared_ptr<rcpputils::SharedLibrary>
|
||||
get_typesupport_library(const std::string & type, const std::string & typesupport_identifier);
|
||||
|
||||
/// Extract the type support handle from the library.
|
||||
/**
|
||||
* The library needs to match the topic type. The shared library must stay loaded for the lifetime of the result.
|
||||
*
|
||||
* \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);
|
||||
|
||||
/// 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.
|
||||
@@ -76,7 +57,7 @@ get_message_typesupport_handle(
|
||||
|
||||
/// Extract the service type support handle from the library.
|
||||
/**
|
||||
* The library needs to match the service type. The shared library must stay loaded for the lifetime of the result.
|
||||
* 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"
|
||||
|
||||
@@ -176,9 +176,7 @@ public:
|
||||
for (; ii < wait_set.size_of_timers(); ++ii) {
|
||||
if (rcl_wait_set.timers[ii] != nullptr) {
|
||||
ret = wait_set.timers(ii);
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -219,9 +217,7 @@ public:
|
||||
if (rcl_wait_set.subscriptions[ii] != nullptr) {
|
||||
ret = wait_set.subscriptions(ii);
|
||||
rcl_wait_set.subscriptions[ii] = nullptr;
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -241,9 +237,7 @@ public:
|
||||
if (rcl_wait_set.services[ii] != nullptr) {
|
||||
ret = wait_set.services(ii);
|
||||
rcl_wait_set.services[ii] = nullptr;
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -263,9 +257,7 @@ public:
|
||||
if (rcl_wait_set.clients[ii] != nullptr) {
|
||||
ret = wait_set.clients(ii);
|
||||
rcl_wait_set.clients[ii] = nullptr;
|
||||
if (ret) {
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -216,11 +216,6 @@ public:
|
||||
shared_waitables_
|
||||
);
|
||||
|
||||
if (this->needs_pruning_) {
|
||||
this->storage_prune_deleted_entities();
|
||||
this->needs_pruning_ = false;
|
||||
}
|
||||
|
||||
this->storage_release_ownerships();
|
||||
}
|
||||
|
||||
@@ -460,60 +455,59 @@ public:
|
||||
|
||||
size_t size_of_subscriptions() const
|
||||
{
|
||||
return subscriptions_.size();
|
||||
return shared_subscriptions_.size();
|
||||
}
|
||||
|
||||
size_t size_of_timers() const
|
||||
{
|
||||
return timers_.size();
|
||||
return shared_timers_.size();
|
||||
}
|
||||
|
||||
size_t size_of_clients() const
|
||||
{
|
||||
return clients_.size();
|
||||
return shared_clients_.size();
|
||||
}
|
||||
|
||||
size_t size_of_services() const
|
||||
{
|
||||
return services_.size();
|
||||
return shared_services_.size();
|
||||
}
|
||||
|
||||
size_t size_of_waitables() const
|
||||
{
|
||||
return waitables_.size();
|
||||
return shared_waitables_.size();
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::SubscriptionBase>
|
||||
subscriptions(size_t ii) const
|
||||
{
|
||||
return subscriptions_[ii].lock();
|
||||
return shared_subscriptions_[ii].subscription;
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::TimerBase>
|
||||
timers(size_t ii) const
|
||||
{
|
||||
return timers_[ii].lock();
|
||||
return shared_timers_[ii];
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::ClientBase>
|
||||
clients(size_t ii) const
|
||||
{
|
||||
return clients_[ii].lock();
|
||||
return shared_clients_[ii];
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::ServiceBase>
|
||||
services(size_t ii) const
|
||||
{
|
||||
return services_[ii].lock();
|
||||
return shared_services_[ii];
|
||||
}
|
||||
|
||||
std::shared_ptr<rclcpp::Waitable>
|
||||
waitables(size_t ii) const
|
||||
{
|
||||
return waitables_[ii].lock();
|
||||
return shared_waitables_[ii].waitable;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t ownership_reference_counter_ = 0;
|
||||
|
||||
SequenceOfWeakSubscriptions subscriptions_;
|
||||
|
||||
@@ -160,15 +160,6 @@ 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,6 +734,8 @@ 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.
|
||||
@@ -750,6 +752,8 @@ 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,23 +101,6 @@ 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.
|
||||
@@ -126,24 +109,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
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
|
||||
add_to_wait_set(rcl_wait_set_t & wait_set) = 0;
|
||||
|
||||
/// Check if the Waitable is ready.
|
||||
/**
|
||||
@@ -158,7 +124,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
is_ready(const rcl_wait_set_t & wait_set);
|
||||
is_ready(const rcl_wait_set_t & wait_set) = 0;
|
||||
|
||||
/// Take the data so that it can be consumed with `execute`.
|
||||
/**
|
||||
@@ -210,24 +176,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
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
|
||||
take_data_by_entity_id(size_t id) = 0;
|
||||
|
||||
/// Execute data that is passed in.
|
||||
/**
|
||||
@@ -254,7 +203,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
execute(const std::shared_ptr<void> & data);
|
||||
execute(const std::shared_ptr<void> & data) = 0;
|
||||
|
||||
/// Exchange the "in use by wait set" state for this timer.
|
||||
/**
|
||||
@@ -297,7 +246,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
set_on_ready_callback(std::function<void(size_t, int)> callback);
|
||||
set_on_ready_callback(std::function<void(size_t, int)> callback) = 0;
|
||||
|
||||
/// Unset any callback registered via set_on_ready_callback.
|
||||
/**
|
||||
@@ -307,7 +256,7 @@ public:
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
clear_on_ready_callback();
|
||||
clear_on_ready_callback() = 0;
|
||||
|
||||
private:
|
||||
std::atomic<bool> in_use_by_wait_set_{false};
|
||||
|
||||
@@ -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.1.10</version>
|
||||
<version>29.2.0</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
|
||||
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
|
||||
|
||||
@@ -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,245 +367,4 @@ 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
|
||||
|
||||
@@ -212,28 +212,27 @@ Context::init(
|
||||
}
|
||||
rcl_context_.reset(context, __delete_context);
|
||||
|
||||
if (init_options.auto_initialize_logging()) {
|
||||
logging_mutex_ = get_global_logging_mutex();
|
||||
std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
|
||||
size_t & count = get_logging_reference_count();
|
||||
if (0u == count) {
|
||||
ret = rcl_logging_configure_with_output_handler(
|
||||
&rcl_context_->global_arguments,
|
||||
rcl_init_options_get_allocator(init_options.get_rcl_init_options()),
|
||||
rclcpp_logging_output_handler);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rcl_context_.reset();
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure logging");
|
||||
}
|
||||
} else {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"logging was initialized more than once");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
|
||||
try {
|
||||
if (init_options.auto_initialize_logging()) {
|
||||
logging_mutex_ = get_global_logging_mutex();
|
||||
std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
|
||||
size_t & count = get_logging_reference_count();
|
||||
if (0u == count) {
|
||||
ret = rcl_logging_configure_with_output_handler(
|
||||
&rcl_context_->global_arguments,
|
||||
rcl_init_options_get_allocator(init_options.get_rcl_init_options()),
|
||||
rclcpp_logging_output_handler);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure logging");
|
||||
}
|
||||
} else {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"logging was initialized more than once");
|
||||
}
|
||||
++count;
|
||||
}
|
||||
|
||||
std::vector<std::string> unparsed_ros_arguments = detail::get_unparsed_ros_arguments(
|
||||
argc, argv, &(rcl_context_->global_arguments), rcl_get_default_allocator());
|
||||
if (!unparsed_ros_arguments.empty()) {
|
||||
|
||||
49
rclcpp/src/rclcpp/create_generic_service.cpp
Normal file
49
rclcpp/src/rclcpp/create_generic_service.cpp
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2024 Sony Group Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/create_generic_service.hpp"
|
||||
#include "rclcpp/generic_service.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
rclcpp::GenericService::SharedPtr
|
||||
create_generic_service(
|
||||
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 std::string & service_type,
|
||||
GenericServiceCallback any_callback,
|
||||
const rclcpp::QoS & qos,
|
||||
rclcpp::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
rcl_service_options_t options = rcl_service_get_default_options();
|
||||
options.qos = qos.get_rmw_qos_profile();
|
||||
|
||||
auto srv = rclcpp::GenericService::make_shared(
|
||||
node_base.get(),
|
||||
node_graph,
|
||||
service_name,
|
||||
service_type,
|
||||
any_callback,
|
||||
options);
|
||||
|
||||
auto srv_base_ptr = std::dynamic_pointer_cast<rclcpp::ServiceBase>(srv);
|
||||
node_services->add_service(srv_base_ptr, group);
|
||||
return srv;
|
||||
}
|
||||
} // namespace rclcpp
|
||||
@@ -84,9 +84,7 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
|
||||
notify_waitable_->add_guard_condition(interrupt_guard_condition_);
|
||||
notify_waitable_->add_guard_condition(shutdown_guard_condition_);
|
||||
|
||||
// we need to initially rebuild the collection,
|
||||
// so that the notify_waitable_ is added
|
||||
collect_entities();
|
||||
wait_set_.add_waitable(notify_waitable_);
|
||||
}
|
||||
|
||||
Executor::~Executor()
|
||||
@@ -131,7 +129,7 @@ Executor::~Executor()
|
||||
}
|
||||
|
||||
void
|
||||
Executor::trigger_entity_recollect(bool notify)
|
||||
Executor::handle_updated_entities(bool notify)
|
||||
{
|
||||
this->entities_need_rebuild_.store(true);
|
||||
|
||||
@@ -176,11 +174,11 @@ Executor::add_callback_group(
|
||||
this->collector_.add_callback_group(group_ptr);
|
||||
|
||||
try {
|
||||
this->trigger_entity_recollect(notify);
|
||||
this->handle_updated_entities(notify);
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to trigger guard condition on callback group add: ") + ex.what());
|
||||
"Failed to handle entities update on callback group add: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,11 +188,11 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
|
||||
this->collector_.add_node(node_ptr);
|
||||
|
||||
try {
|
||||
this->trigger_entity_recollect(notify);
|
||||
this->handle_updated_entities(notify);
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to trigger guard condition on node add: ") + ex.what());
|
||||
"Failed to handle entities update on node add: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,11 +204,11 @@ Executor::remove_callback_group(
|
||||
this->collector_.remove_callback_group(group_ptr);
|
||||
|
||||
try {
|
||||
this->trigger_entity_recollect(notify);
|
||||
this->handle_updated_entities(notify);
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to trigger guard condition on callback group remove: ") + ex.what());
|
||||
"Failed to handle entities update on callback group remove: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,11 +224,11 @@ Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node
|
||||
this->collector_.remove_node(node_ptr);
|
||||
|
||||
try {
|
||||
this->trigger_entity_recollect(notify);
|
||||
this->handle_updated_entities(notify);
|
||||
} catch (const rclcpp::exceptions::RCLError & ex) {
|
||||
throw std::runtime_error(
|
||||
std::string(
|
||||
"Failed to trigger guard condition on node remove: ") + ex.what());
|
||||
"Failed to handle entities update on node remove: ") + ex.what());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,14 +371,7 @@ 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 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;
|
||||
}
|
||||
bool just_waited = true;
|
||||
|
||||
// The logic of this while loop is as follows:
|
||||
//
|
||||
@@ -402,14 +393,12 @@ 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);
|
||||
// during the execution some entity might got ready therefore we need
|
||||
// to repoll the states of all entities
|
||||
entity_states_fully_polled = false;
|
||||
just_waited = false;
|
||||
} else {
|
||||
// if nothing is ready, reset the result to clear it
|
||||
wait_result_.reset();
|
||||
|
||||
if (entity_states_fully_polled) {
|
||||
if (just_waited) {
|
||||
// there was no work after just waiting, always exit in this case
|
||||
// before the exhaustive condition can be checked
|
||||
break;
|
||||
@@ -419,13 +408,7 @@ 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));
|
||||
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;
|
||||
}
|
||||
just_waited = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
@@ -746,33 +729,13 @@ 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,8 +12,6 @@
|
||||
// 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"
|
||||
|
||||
@@ -91,9 +89,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*/)
|
||||
{
|
||||
(void) data;
|
||||
std::lock_guard<std::mutex> lock(execute_mutex_);
|
||||
this->execute_callback_();
|
||||
}
|
||||
|
||||
@@ -149,6 +147,14 @@ 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,29 +110,10 @@ 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,29 +52,36 @@ EventsExecutor::EventsExecutor(
|
||||
timers_manager_ =
|
||||
std::make_shared<rclcpp::experimental::TimersManager>(context_, timer_on_ready_cb);
|
||||
|
||||
this->current_entities_collection_ =
|
||||
std::make_shared<rclcpp::executors::ExecutorEntitiesCollection>();
|
||||
entities_need_rebuild_ = false;
|
||||
|
||||
notify_waitable_ = std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(
|
||||
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(
|
||||
[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
|
||||
this->refresh_current_collection_from_callback_groups();
|
||||
this->handle_updated_entities(false);
|
||||
});
|
||||
|
||||
// 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) {
|
||||
@@ -84,7 +91,7 @@ EventsExecutor::EventsExecutor(
|
||||
// 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 (notify_waitable_event_pushed_.exchange(true)) {
|
||||
if (entities_need_rebuild_.exchange(true)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -92,9 +99,6 @@ EventsExecutor::EventsExecutor(
|
||||
{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,7 +172,7 @@ EventsExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhau
|
||||
// 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();
|
||||
this->handle_updated_entities(false);
|
||||
}
|
||||
|
||||
// Get the number of events and timers ready at start
|
||||
@@ -236,46 +240,6 @@ 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)
|
||||
@@ -285,10 +249,9 @@ 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_entities_collection_->clients);
|
||||
current_collection_.clients);
|
||||
}
|
||||
if (client) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
@@ -302,10 +265,9 @@ 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_entities_collection_->subscriptions);
|
||||
current_collection_.subscriptions);
|
||||
}
|
||||
if (subscription) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
@@ -318,10 +280,9 @@ 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_entities_collection_->services);
|
||||
current_collection_.services);
|
||||
}
|
||||
if (service) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
@@ -341,10 +302,9 @@ 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_entities_collection_->waitables);
|
||||
current_collection_.waitables);
|
||||
}
|
||||
if (waitable) {
|
||||
for (size_t i = 0; i < event.num_events; i++) {
|
||||
@@ -358,71 +318,23 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
|
||||
}
|
||||
|
||||
void
|
||||
EventsExecutor::add_callback_group(
|
||||
rclcpp::CallbackGroup::SharedPtr group_ptr,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
bool notify)
|
||||
EventsExecutor::handle_updated_entities(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()) {
|
||||
const bool notify_waitable_triggered = entities_need_rebuild_.exchange(false);
|
||||
if (!notify_waitable_triggered && !this->collector_.has_pending()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Build the new collection
|
||||
this->entities_collector_->update_collections();
|
||||
auto callback_groups = this->entities_collector_->get_all_callback_groups();
|
||||
this->collector_.update_collections();
|
||||
auto callback_groups = this->collector_.get_all_callback_groups();
|
||||
rclcpp::executors::ExecutorEntitiesCollection new_collection;
|
||||
rclcpp::executors::build_entities_collection(callback_groups, new_collection);
|
||||
|
||||
@@ -432,14 +344,11 @@ EventsExecutor::refresh_current_collection_from_callback_groups()
|
||||
// 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 both the new and
|
||||
// current collections such that it's neither added or removed.
|
||||
// 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)
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -448,14 +357,19 @@ EventsExecutor::refresh_current_collection(
|
||||
const rclcpp::executors::ExecutorEntitiesCollection & new_collection)
|
||||
{
|
||||
// Acquire lock before modifying the current collection
|
||||
std::lock_guard<std::recursive_mutex> lock(collection_mutex_);
|
||||
std::lock_guard<std::mutex> guard(mutex_);
|
||||
|
||||
current_entities_collection_->timers.update(
|
||||
// Remove expired entities to ensure re-initialized objects
|
||||
// are updated. This fixes issues with stale state entities.
|
||||
// See: https://github.com/ros2/rclcpp/pull/2586
|
||||
current_collection_.remove_expired_entities();
|
||||
|
||||
current_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_entities_collection_->subscriptions.update(
|
||||
current_collection_.subscriptions.update(
|
||||
new_collection.subscriptions,
|
||||
[this](auto subscription) {
|
||||
subscription->set_on_new_message_callback(
|
||||
@@ -464,7 +378,7 @@ EventsExecutor::refresh_current_collection(
|
||||
},
|
||||
[](auto subscription) {subscription->clear_on_new_message_callback();});
|
||||
|
||||
current_entities_collection_->clients.update(
|
||||
current_collection_.clients.update(
|
||||
new_collection.clients,
|
||||
[this](auto client) {
|
||||
client->set_on_new_response_callback(
|
||||
@@ -473,7 +387,7 @@ EventsExecutor::refresh_current_collection(
|
||||
},
|
||||
[](auto client) {client->clear_on_new_response_callback();});
|
||||
|
||||
current_entities_collection_->services.update(
|
||||
current_collection_.services.update(
|
||||
new_collection.services,
|
||||
[this](auto service) {
|
||||
service->set_on_new_request_callback(
|
||||
@@ -484,12 +398,12 @@ EventsExecutor::refresh_current_collection(
|
||||
|
||||
// DO WE NEED THIS? WE ARE NOT DOING ANYTHING WITH GUARD CONDITIONS
|
||||
/*
|
||||
current_entities_collection_->guard_conditions.update(new_collection.guard_conditions,
|
||||
current_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_entities_collection_->waitables.update(
|
||||
current_collection_.waitables.update(
|
||||
new_collection.waitables,
|
||||
[this](auto waitable) {
|
||||
waitable->set_on_ready_callback(
|
||||
|
||||
@@ -31,22 +31,31 @@ GenericClient::GenericClient(
|
||||
rcl_client_options_t & client_options)
|
||||
: ClientBase(node_base, node_graph)
|
||||
{
|
||||
ts_lib_ = get_typesupport_library(
|
||||
service_type, "rosidl_typesupport_cpp");
|
||||
const rosidl_service_type_support_t * service_ts;
|
||||
try {
|
||||
ts_lib_ = get_typesupport_library(
|
||||
service_type, "rosidl_typesupport_cpp");
|
||||
|
||||
auto service_ts_ = get_service_typesupport_handle(
|
||||
service_type, "rosidl_typesupport_cpp", *ts_lib_);
|
||||
service_ts = get_service_typesupport_handle(
|
||||
service_type, "rosidl_typesupport_cpp", *ts_lib_);
|
||||
|
||||
auto response_type_support_intro = get_message_typesupport_handle(
|
||||
service_ts_->response_typesupport,
|
||||
rosidl_typesupport_introspection_cpp::typesupport_identifier);
|
||||
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
|
||||
response_type_support_intro->data);
|
||||
auto response_type_support_intro = get_message_typesupport_handle(
|
||||
service_ts->response_typesupport,
|
||||
rosidl_typesupport_introspection_cpp::typesupport_identifier);
|
||||
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
|
||||
response_type_support_intro->data);
|
||||
} catch (std::runtime_error & err) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_node_logger(node_handle_.get()).get_child("rclcpp"),
|
||||
"Invalid service type: %s",
|
||||
err.what());
|
||||
throw rclcpp::exceptions::InvalidServiceTypeError(err.what());
|
||||
}
|
||||
|
||||
rcl_ret_t ret = rcl_client_init(
|
||||
this->get_client_handle().get(),
|
||||
this->get_rcl_node_handle(),
|
||||
service_ts_,
|
||||
service_ts,
|
||||
service_name.c_str(),
|
||||
&client_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
@@ -100,6 +109,13 @@ GenericClient::handle_response(
|
||||
if (std::holds_alternative<Promise>(value)) {
|
||||
auto & promise = std::get<Promise>(value);
|
||||
promise.set_value(std::move(response));
|
||||
} else if (std::holds_alternative<CallbackTypeValueVariant>(value)) {
|
||||
auto & inner = std::get<CallbackTypeValueVariant>(value);
|
||||
const auto & callback = std::get<CallbackType>(inner);
|
||||
auto & promise = std::get<Promise>(inner);
|
||||
auto & future = std::get<SharedFuture>(inner);
|
||||
promise.set_value(std::move(response));
|
||||
callback(std::move(future));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,6 +135,18 @@ GenericClient::remove_pending_request(int64_t request_id)
|
||||
return pending_requests_.erase(request_id) != 0u;
|
||||
}
|
||||
|
||||
bool
|
||||
GenericClient::remove_pending_request(const FutureAndRequestId & future)
|
||||
{
|
||||
return this->remove_pending_request(future.request_id);
|
||||
}
|
||||
|
||||
bool
|
||||
GenericClient::remove_pending_request(const SharedFutureAndRequestId & future)
|
||||
{
|
||||
return this->remove_pending_request(future.request_id);
|
||||
}
|
||||
|
||||
std::optional<GenericClient::CallbackInfoVariant>
|
||||
GenericClient::get_and_erase_pending_request(int64_t request_number)
|
||||
{
|
||||
|
||||
172
rclcpp/src/rclcpp/generic_service.cpp
Normal file
172
rclcpp/src/rclcpp/generic_service.cpp
Normal file
@@ -0,0 +1,172 @@
|
||||
// Copyright 2024 Sony Group Corporation.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/generic_service.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
GenericService::GenericService(
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const std::string & service_name,
|
||||
const std::string & service_type,
|
||||
GenericServiceCallback any_callback,
|
||||
rcl_service_options_t & service_options)
|
||||
: ServiceBase(node_handle),
|
||||
any_callback_(any_callback)
|
||||
{
|
||||
const rosidl_service_type_support_t * service_ts;
|
||||
try {
|
||||
ts_lib_ = get_typesupport_library(
|
||||
service_type, "rosidl_typesupport_cpp");
|
||||
|
||||
service_ts = get_service_typesupport_handle(
|
||||
service_type, "rosidl_typesupport_cpp", *ts_lib_);
|
||||
|
||||
auto request_type_support_intro = get_message_typesupport_handle(
|
||||
service_ts->request_typesupport,
|
||||
rosidl_typesupport_introspection_cpp::typesupport_identifier);
|
||||
request_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
|
||||
request_type_support_intro->data);
|
||||
|
||||
auto response_type_support_intro = get_message_typesupport_handle(
|
||||
service_ts->response_typesupport,
|
||||
rosidl_typesupport_introspection_cpp::typesupport_identifier);
|
||||
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
|
||||
response_type_support_intro->data);
|
||||
} catch (std::runtime_error & err) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_node_logger(node_handle_.get()).get_child("rclcpp"),
|
||||
"Invalid service type: %s",
|
||||
err.what());
|
||||
throw rclcpp::exceptions::InvalidServiceTypeError(err.what());
|
||||
}
|
||||
|
||||
// rcl does the static memory allocation here
|
||||
service_handle_ = std::shared_ptr<rcl_service_t>(
|
||||
new rcl_service_t, [handle = node_handle_, service_name](rcl_service_t * service)
|
||||
{
|
||||
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
|
||||
"Error in destruction of rcl service handle: %s",
|
||||
rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
delete service;
|
||||
});
|
||||
*service_handle_.get() = rcl_get_zero_initialized_service();
|
||||
|
||||
rcl_ret_t ret = rcl_service_init(
|
||||
service_handle_.get(),
|
||||
node_handle.get(),
|
||||
service_ts,
|
||||
service_name.c_str(),
|
||||
&service_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
if (ret == RCL_RET_SERVICE_NAME_INVALID) {
|
||||
auto rcl_node_handle = get_rcl_node_handle();
|
||||
// this will throw on any validation problem
|
||||
rcl_reset_error();
|
||||
expand_topic_or_service_name(
|
||||
service_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle),
|
||||
true);
|
||||
}
|
||||
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
|
||||
}
|
||||
TRACETOOLS_TRACEPOINT(
|
||||
rclcpp_service_callback_added,
|
||||
static_cast<const void *>(get_service_handle().get()),
|
||||
static_cast<const void *>(&any_callback_));
|
||||
#ifndef TRACETOOLS_DISABLED
|
||||
any_callback_.register_callback_for_tracing();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool
|
||||
GenericService::take_request(
|
||||
SharedRequest request_out,
|
||||
rmw_request_id_t & request_id_out)
|
||||
{
|
||||
request_out = create_request();
|
||||
return this->take_type_erased_request(request_out.get(), request_id_out);
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
GenericService::create_request()
|
||||
{
|
||||
Request request = new uint8_t[request_members_->size_of_];
|
||||
request_members_->init_function(request, rosidl_runtime_cpp::MessageInitialization::ZERO);
|
||||
return std::shared_ptr<void>(
|
||||
request,
|
||||
[this](void * p)
|
||||
{
|
||||
request_members_->fini_function(p);
|
||||
delete[] reinterpret_cast<uint8_t *>(p);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
GenericService::create_response()
|
||||
{
|
||||
Response response = new uint8_t[response_members_->size_of_];
|
||||
response_members_->init_function(response, rosidl_runtime_cpp::MessageInitialization::ZERO);
|
||||
return std::shared_ptr<void>(
|
||||
response,
|
||||
[this](void * p)
|
||||
{
|
||||
response_members_->fini_function(p);
|
||||
delete[] reinterpret_cast<uint8_t *>(p);
|
||||
});
|
||||
}
|
||||
|
||||
std::shared_ptr<rmw_request_id_t>
|
||||
GenericService::create_request_header()
|
||||
{
|
||||
return std::make_shared<rmw_request_id_t>();
|
||||
}
|
||||
|
||||
void
|
||||
GenericService::handle_request(
|
||||
std::shared_ptr<rmw_request_id_t> request_header,
|
||||
std::shared_ptr<void> request)
|
||||
{
|
||||
auto response = any_callback_.dispatch(
|
||||
this->shared_from_this(), request_header, request, create_response());
|
||||
if (response) {
|
||||
send_response(*request_header, response);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GenericService::send_response(rmw_request_id_t & req_id, SharedResponse & response)
|
||||
{
|
||||
rcl_ret_t ret = rcl_send_response(get_service_handle().get(), &req_id, response.get());
|
||||
|
||||
if (ret == RCL_RET_TIMEOUT) {
|
||||
RCLCPP_WARN(
|
||||
node_logger_.get_child("rclcpp"),
|
||||
"failed to send response to %s (timeout): %s",
|
||||
this->get_service_name(), rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
return;
|
||||
}
|
||||
if (ret != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send response");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -12,6 +12,7 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -54,6 +55,14 @@ 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()
|
||||
{
|
||||
@@ -67,6 +76,26 @@ 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)
|
||||
|
||||
@@ -28,9 +28,6 @@ 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()) {
|
||||
|
||||
@@ -135,15 +135,28 @@ void
|
||||
PublisherBase::bind_event_callbacks(
|
||||
const PublisherEventCallbacks & event_callbacks, bool use_default_callbacks)
|
||||
{
|
||||
if (event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.deadline_callback,
|
||||
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
|
||||
try {
|
||||
if (event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.deadline_callback,
|
||||
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for deadline; not supported");
|
||||
}
|
||||
if (event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.liveliness_callback,
|
||||
RCL_PUBLISHER_LIVELINESS_LOST);
|
||||
|
||||
try {
|
||||
if (event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.liveliness_callback,
|
||||
RCL_PUBLISHER_LIVELINESS_LOST);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for liveliness; not supported");
|
||||
}
|
||||
|
||||
QOSOfferedIncompatibleQoSCallbackType incompatible_qos_cb;
|
||||
@@ -160,9 +173,9 @@ PublisherBase::bind_event_callbacks(
|
||||
this->add_event_handler(incompatible_qos_cb, RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_DEBUG(
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for incompatible qos; wrong callback type");
|
||||
"Failed to add event handler for incompatible qos; not supported");
|
||||
}
|
||||
|
||||
IncompatibleTypeCallbackType incompatible_type_cb;
|
||||
@@ -179,14 +192,21 @@ PublisherBase::bind_event_callbacks(
|
||||
this->add_event_handler(incompatible_type_cb, RCL_PUBLISHER_INCOMPATIBLE_TYPE);
|
||||
}
|
||||
} catch (UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_DEBUG(
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for incompatible type; wrong callback type");
|
||||
"Failed to add event handler for incompatible type; not supported");
|
||||
}
|
||||
if (event_callbacks.matched_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.matched_callback,
|
||||
RCL_PUBLISHER_MATCHED);
|
||||
|
||||
try {
|
||||
if (event_callbacks.matched_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.matched_callback,
|
||||
RCL_PUBLISHER_MATCHED);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for matched; not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,10 +69,8 @@ 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:
|
||||
return KeepLast(rmw_qos.depth);
|
||||
default:
|
||||
throw std::invalid_argument(
|
||||
"Invalid history policy enum value passed to QoSInitialization::from_rmw");
|
||||
return KeepLast(rmw_qos.depth);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -71,12 +71,6 @@ 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,6 +36,7 @@ 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.");
|
||||
|
||||
@@ -51,6 +52,7 @@ 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,13 +26,8 @@ namespace rclcpp
|
||||
|
||||
inline void copy_rcl_message(const rcl_serialized_message_t & from, rcl_serialized_message_t & to)
|
||||
{
|
||||
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);
|
||||
}
|
||||
|
||||
const auto ret = rmw_serialized_message_init(
|
||||
&to, from.buffer_capacity, &from.allocator);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
@@ -83,6 +78,7 @@ 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_);
|
||||
}
|
||||
|
||||
@@ -92,6 +88,7 @@ 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_);
|
||||
}
|
||||
|
||||
@@ -101,14 +98,6 @@ 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());
|
||||
}
|
||||
@@ -119,14 +108,6 @@ 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());
|
||||
}
|
||||
|
||||
@@ -112,16 +112,28 @@ void
|
||||
SubscriptionBase::bind_event_callbacks(
|
||||
const SubscriptionEventCallbacks & event_callbacks, bool use_default_callbacks)
|
||||
{
|
||||
if (event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.deadline_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
|
||||
try {
|
||||
if (event_callbacks.deadline_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.deadline_callback,
|
||||
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for deadline; not supported");
|
||||
}
|
||||
|
||||
if (event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.liveliness_callback,
|
||||
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
|
||||
try {
|
||||
if (event_callbacks.liveliness_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.liveliness_callback,
|
||||
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for liveliness; not supported");
|
||||
}
|
||||
|
||||
QOSRequestedIncompatibleQoSCallbackType incompatible_qos_cb;
|
||||
@@ -139,7 +151,9 @@ SubscriptionBase::bind_event_callbacks(
|
||||
this->add_event_handler(incompatible_qos_cb, RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
// pass
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for incompatible qos; not supported");
|
||||
}
|
||||
|
||||
IncompatibleTypeCallbackType incompatible_type_cb;
|
||||
@@ -156,18 +170,33 @@ SubscriptionBase::bind_event_callbacks(
|
||||
this->add_event_handler(incompatible_type_cb, RCL_SUBSCRIPTION_INCOMPATIBLE_TYPE);
|
||||
}
|
||||
} catch (UnsupportedEventTypeException & /*exc*/) {
|
||||
// pass
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for incompatible type; not supported");
|
||||
}
|
||||
|
||||
if (event_callbacks.message_lost_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.message_lost_callback,
|
||||
RCL_SUBSCRIPTION_MESSAGE_LOST);
|
||||
try {
|
||||
if (event_callbacks.message_lost_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.message_lost_callback,
|
||||
RCL_SUBSCRIPTION_MESSAGE_LOST);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for message lost; not supported");
|
||||
}
|
||||
if (event_callbacks.matched_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.matched_callback,
|
||||
RCL_SUBSCRIPTION_MATCHED);
|
||||
|
||||
try {
|
||||
if (event_callbacks.matched_callback) {
|
||||
this->add_event_handler(
|
||||
event_callbacks.matched_callback,
|
||||
RCL_SUBSCRIPTION_MATCHED);
|
||||
}
|
||||
} catch (const UnsupportedEventTypeException & /*exc*/) {
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Failed to add event handler for matched; not supported");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -60,6 +60,10 @@ 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;
|
||||
}
|
||||
|
||||
@@ -249,6 +253,9 @@ 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;
|
||||
}
|
||||
@@ -264,6 +271,9 @@ 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;
|
||||
}
|
||||
|
||||
@@ -54,114 +54,8 @@ Waitable::get_number_of_ready_guard_conditions()
|
||||
return 0u;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
Waitable::take_data_by_entity_id(size_t id)
|
||||
{
|
||||
(void)id;
|
||||
throw std::runtime_error(
|
||||
"Custom waitables should override take_data_by_entity_id "
|
||||
"if they want to use it.");
|
||||
}
|
||||
|
||||
bool
|
||||
Waitable::exchange_in_use_by_wait_set_state(bool in_use_state)
|
||||
{
|
||||
return in_use_by_wait_set_.exchange(in_use_state);
|
||||
}
|
||||
|
||||
void
|
||||
Waitable::set_on_ready_callback(std::function<void(size_t, int)> callback)
|
||||
{
|
||||
(void)callback;
|
||||
|
||||
throw std::runtime_error(
|
||||
"Custom waitables should override set_on_ready_callback "
|
||||
"if they want to use it.");
|
||||
}
|
||||
|
||||
void
|
||||
Waitable::clear_on_ready_callback()
|
||||
{
|
||||
throw std::runtime_error(
|
||||
"Custom waitables should override clear_on_ready_callback if they "
|
||||
"want to use it and make sure to call it on the waitable destructor.");
|
||||
}
|
||||
|
||||
void
|
||||
Waitable::add_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
this->add_to_wait_set(*wait_set);
|
||||
}
|
||||
|
||||
void
|
||||
Waitable::add_to_wait_set(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
|
||||
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,7 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -190,71 +189,6 @@ BENCHMARK_F(PerformanceTestExecutorSimple, multi_thread_executor_remove_node)(be
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_add_node)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
executor.add_node(node);
|
||||
st.PauseTiming();
|
||||
executor.remove_node(node);
|
||||
st.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_remove_node)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
st.PauseTiming();
|
||||
executor.add_node(node);
|
||||
st.ResumeTiming();
|
||||
executor.remove_node(node);
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_spin_until_future_complete)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
// test success of an immediately finishing future
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
auto shared_future = future.share();
|
||||
|
||||
auto ret = executor.spin_until_future_complete(shared_future, 100ms);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
}
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
// static_single_thread_executor has a special design. We need to add/remove the node each
|
||||
// time you call spin
|
||||
st.PauseTiming();
|
||||
executor.add_node(node);
|
||||
st.ResumeTiming();
|
||||
|
||||
ret = executor.spin_until_future_complete(shared_future, 100ms);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
break;
|
||||
}
|
||||
st.PauseTiming();
|
||||
executor.remove_node(node);
|
||||
st.ResumeTiming();
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
single_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
|
||||
@@ -315,30 +249,6 @@ BENCHMARK_F(
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(
|
||||
PerformanceTestExecutorSimple,
|
||||
static_single_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
|
||||
{
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
// test success of an immediately finishing future
|
||||
std::promise<bool> promise;
|
||||
std::future<bool> future = promise.get_future();
|
||||
promise.set_value(true);
|
||||
auto shared_future = future.share();
|
||||
|
||||
reset_heap_counters();
|
||||
|
||||
for (auto _ : st) {
|
||||
(void)_;
|
||||
auto ret = rclcpp::executors::spin_node_until_future_complete(
|
||||
executor, node, shared_future, 1s);
|
||||
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
|
||||
st.SkipWithError(rcutils_get_error_string().str);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BENCHMARK_F(PerformanceTestExecutorSimple, spin_until_future_complete)(benchmark::State & st)
|
||||
{
|
||||
// test success of an immediately finishing future
|
||||
|
||||
@@ -31,6 +31,7 @@ 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()
|
||||
@@ -52,18 +53,15 @@ 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})
|
||||
@@ -74,6 +72,7 @@ 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 +83,20 @@ if(TARGET test_generic_client)
|
||||
${test_msgs_TARGETS}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_generic_service test_generic_service.cpp)
|
||||
ament_add_test_label(test_generic_service mimick)
|
||||
if(TARGET test_generic_service)
|
||||
target_link_libraries(test_generic_service ${PROJECT_NAME}
|
||||
mimick
|
||||
${rcl_interfaces_TARGETS}
|
||||
rmw::rmw
|
||||
rosidl_runtime_cpp::rosidl_runtime_cpp
|
||||
rosidl_typesupport_cpp::rosidl_typesupport_cpp
|
||||
${test_msgs_TARGETS}
|
||||
)
|
||||
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,18 +111,8 @@ 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()
|
||||
@@ -142,6 +144,7 @@ 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)
|
||||
@@ -151,6 +154,7 @@ 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()
|
||||
@@ -162,6 +166,7 @@ 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()
|
||||
@@ -173,6 +178,7 @@ 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()
|
||||
@@ -183,21 +189,25 @@ 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()
|
||||
@@ -208,6 +218,7 @@ 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()
|
||||
@@ -243,10 +254,12 @@ 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()
|
||||
@@ -275,6 +288,7 @@ 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()
|
||||
@@ -324,27 +338,6 @@ 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)
|
||||
@@ -367,15 +360,18 @@ 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()
|
||||
@@ -426,6 +422,7 @@ 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()
|
||||
@@ -444,6 +441,7 @@ 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()
|
||||
@@ -464,6 +462,13 @@ if(TARGET test_interface_traits)
|
||||
target_link_libraries(test_interface_traits ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_reinitialized_timers
|
||||
executors/test_reinitialized_timers.cpp
|
||||
TIMEOUT 30)
|
||||
if(TARGET test_reinitialized_timers)
|
||||
target_link_libraries(test_reinitialized_timers ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(
|
||||
test_executors
|
||||
executors/test_executors.cpp
|
||||
@@ -482,7 +487,7 @@ ament_add_gtest(
|
||||
executors/test_executors_timer_cancel_behavior.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
TIMEOUT 180)
|
||||
if(TARGET test_executors)
|
||||
if(TARGET test_executors_timer_cancel_behavior)
|
||||
target_link_libraries(test_executors_timer_cancel_behavior ${PROJECT_NAME} ${rosgraph_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
@@ -491,7 +496,7 @@ ament_add_gtest(
|
||||
executors/test_executors_callback_group_behavior.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
TIMEOUT 180)
|
||||
if(TARGET test_executors)
|
||||
if(TARGET test_executors_callback_group_behavior)
|
||||
target_link_libraries(test_executors_callback_group_behavior ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
@@ -500,7 +505,7 @@ ament_add_gtest(
|
||||
executors/test_executors_intraprocess.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}"
|
||||
TIMEOUT 180)
|
||||
if(TARGET test_executors)
|
||||
if(TARGET test_executors_intraprocess)
|
||||
target_link_libraries(test_executors_intraprocess ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
@@ -526,6 +531,7 @@ 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()
|
||||
@@ -544,11 +550,12 @@ 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()
|
||||
|
||||
ament_add_gtest(test_events_executor executors/test_events_executor.cpp TIMEOUT 5)
|
||||
ament_add_gtest(test_events_executor executors/test_events_executor.cpp TIMEOUT 60)
|
||||
if(TARGET test_events_executor)
|
||||
target_link_libraries(test_events_executor ${PROJECT_NAME} ${test_msgs_TARGETS})
|
||||
endif()
|
||||
@@ -561,6 +568,7 @@ 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()
|
||||
@@ -594,6 +602,7 @@ 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()
|
||||
@@ -626,24 +635,62 @@ 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()
|
||||
|
||||
function(test_subscription_content_filter_for_rmw_implementation)
|
||||
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)
|
||||
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
|
||||
ament_add_gmock(test_subscription_content_filter${target_suffix}
|
||||
test_subscription_content_filter.cpp
|
||||
|
||||
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}
|
||||
ENV ${rmw_implementation_env_var}
|
||||
TIMEOUT 120
|
||||
)
|
||||
if(TARGET test_subscription_content_filter${target_suffix})
|
||||
target_link_libraries(test_subscription_content_filter${target_suffix} ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
|
||||
endif()
|
||||
|
||||
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)
|
||||
endfunction()
|
||||
call_for_each_rmw_implementation(test_subscription_content_filter_for_rmw_implementation)
|
||||
call_for_each_rmw_implementation(test_on_all_rmws)
|
||||
|
||||
@@ -25,12 +25,37 @@
|
||||
#include "rclcpp/executors/static_single_threaded_executor.hpp"
|
||||
#include "rclcpp/executors/multi_threaded_executor.hpp"
|
||||
|
||||
// suppress deprecated StaticSingleThreadedExecutor warning
|
||||
// we define an alias that explicitly indicates that this class is deprecated, while avoiding
|
||||
// polluting a lot of files the gcc pragmas
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic push
|
||||
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(push)
|
||||
# pragma warning(disable: 4996)
|
||||
#endif
|
||||
using DeprecatedStaticSingleThreadedExecutor = rclcpp::executors::StaticSingleThreadedExecutor;
|
||||
// remove warning suppression
|
||||
#if !defined(_WIN32)
|
||||
# pragma GCC diagnostic pop
|
||||
#else // !defined(_WIN32)
|
||||
# pragma warning(pop)
|
||||
#endif
|
||||
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
using ExecutorTypes =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::executors::StaticSingleThreadedExecutor,
|
||||
DeprecatedStaticSingleThreadedExecutor,
|
||||
rclcpp::experimental::executors::EventsExecutor>;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
class ExecutorTypeNames
|
||||
{
|
||||
@@ -46,10 +71,16 @@ public:
|
||||
if (std::is_same<T, rclcpp::executors::MultiThreadedExecutor>()) {
|
||||
return "MultiThreadedExecutor";
|
||||
}
|
||||
|
||||
if (std::is_same<T, rclcpp::executors::StaticSingleThreadedExecutor>()) {
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
if (std::is_same<T, DeprecatedStaticSingleThreadedExecutor>()) {
|
||||
return "StaticSingleThreadedExecutor";
|
||||
}
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
|
||||
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
|
||||
return "EventsExecutor";
|
||||
|
||||
@@ -479,14 +479,21 @@ TEST_F(TestEventsExecutor, 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 '/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 '/test_topic', offering incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
sub_log_msg);
|
||||
rclcpp::QoSCheckCompatibleResult qos_compatible = rclcpp::qos_check_compatible(
|
||||
publisher->get_actual_qos(), subscription->get_actual_qos());
|
||||
if (qos_compatible.compatibility == rclcpp::QoSCompatibility::Error) {
|
||||
EXPECT_EQ(
|
||||
"New subscription discovered on topic '/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 '/test_topic', offering incompatible QoS. "
|
||||
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
|
||||
sub_log_msg);
|
||||
} else {
|
||||
EXPECT_EQ("", pub_log_msg);
|
||||
EXPECT_EQ("", sub_log_msg);
|
||||
}
|
||||
|
||||
rcutils_logging_set_output_handler(original_output_handler);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
|
||||
|
||||
@@ -76,7 +76,8 @@ 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 = std::make_shared<rclcpp::GuardCondition>();
|
||||
auto notify_guard_condition =
|
||||
node->get_node_base_interface()->get_shared_notify_guard_condition();
|
||||
EXPECT_NO_THROW(waitable->add_guard_condition(notify_guard_condition));
|
||||
|
||||
auto default_cbg = node->get_node_base_interface()->get_default_callback_group();
|
||||
@@ -85,15 +86,12 @@ 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);
|
||||
|
||||
// no further trigger, therefore no further callback
|
||||
// on_execute_callback doesn't change if the topology doesn't change
|
||||
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, spinSome)
|
||||
TYPED_TEST(TestExecutors, spin_some)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
@@ -480,20 +480,14 @@ TYPED_TEST(TestExecutors, spinSome)
|
||||
|
||||
// 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, spinSomeMaxDuration)
|
||||
// TODO(wjwwood): The `StaticSingleThreadedExecutor`
|
||||
// do not properly implement max_duration (it seems), so disable this test
|
||||
// for them in the meantime.
|
||||
// see: https://github.com/ros2/rclcpp/issues/2462
|
||||
TYPED_TEST(TestExecutorsStable, spin_some_max_duration)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
// TODO(wjwwood): The `StaticSingleThreadedExecutor`
|
||||
// do not properly implement max_duration (it seems), so disable this test
|
||||
// for them in the meantime.
|
||||
// see: https://github.com/ros2/rclcpp/issues/2462
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::executors::StaticSingleThreadedExecutor>())
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
// Use an isolated callback group to avoid interference from any housekeeping
|
||||
// items that may be in the default callback group of the node.
|
||||
constexpr bool automatically_add_to_executor_with_node = false;
|
||||
@@ -638,7 +632,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 `notify_waitable_event_pushed_` flag to true, preventing additional
|
||||
// This would leave the `entities_need_rebuild_` 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
|
||||
@@ -667,20 +661,20 @@ TYPED_TEST(TestExecutors, testRaceConditionAddNode)
|
||||
}
|
||||
|
||||
// Create an executor
|
||||
auto executor = std::make_shared<ExecutorType>();
|
||||
ExecutorType executor;
|
||||
// Start spinning
|
||||
auto executor_thread = std::thread(
|
||||
[executor]() {
|
||||
executor->spin();
|
||||
[&executor]() {
|
||||
executor.spin();
|
||||
});
|
||||
// Add a node to the executor
|
||||
executor->add_node(this->node);
|
||||
executor.add_node(this->node);
|
||||
|
||||
// Cancel the executor (make sure that it's already spinning first)
|
||||
while (!executor->is_spinning() && rclcpp::ok()) {
|
||||
while (!executor.is_spinning() && rclcpp::ok()) {
|
||||
continue;
|
||||
}
|
||||
executor->cancel();
|
||||
executor.cancel();
|
||||
|
||||
// Try to join the thread after cancelling the executor
|
||||
// This is the "test". We want to make sure that we can still cancel the executor
|
||||
@@ -694,6 +688,67 @@ 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)
|
||||
{
|
||||
@@ -765,7 +820,7 @@ TEST(TestExecutors, testSpinWithNonDefaultContext)
|
||||
rclcpp::shutdown(non_default_context);
|
||||
}
|
||||
|
||||
TYPED_TEST(TestExecutors, releaseOwnershipEntityAfterSpinningCancel)
|
||||
TYPED_TEST(TestExecutors, release_ownership_entity_after_spinning_cancel)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
@@ -788,309 +843,3 @@ TYPED_TEST(TestExecutors, releaseOwnershipEntityAfterSpinningCancel)
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -47,9 +47,6 @@ public:
|
||||
auto waitable_interfaces = node->get_node_waitables_interface();
|
||||
waitable = std::make_shared<TestWaitable>();
|
||||
waitable_interfaces->add_waitable(waitable, callback_group);
|
||||
|
||||
executor = std::make_shared<T>();
|
||||
executor->add_callback_group(callback_group, node->get_node_base_interface());
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
@@ -108,7 +105,6 @@ public:
|
||||
rclcpp::CallbackGroup::SharedPtr callback_group;
|
||||
std::shared_ptr<TestWaitable> waitable;
|
||||
std::chrono::steady_clock::time_point start_time;
|
||||
std::shared_ptr<T> executor;
|
||||
bool has_executed;
|
||||
};
|
||||
|
||||
@@ -116,10 +112,16 @@ TYPED_TEST_SUITE(TestBusyWaiting, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
TYPED_TEST(TestBusyWaiting, test_spin_all)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
executor.add_callback_group(
|
||||
this->callback_group,
|
||||
this->node->get_node_base_interface());
|
||||
|
||||
this->set_up_and_trigger_waitable();
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
this->executor->spin_all(this->max_duration);
|
||||
executor.spin_all(this->max_duration);
|
||||
this->check_for_busy_waits(start_time);
|
||||
// this should get the initial trigger, and the follow up from in the callback
|
||||
ASSERT_EQ(this->waitable->get_count(), 2u);
|
||||
@@ -127,10 +129,16 @@ TYPED_TEST(TestBusyWaiting, test_spin_all)
|
||||
|
||||
TYPED_TEST(TestBusyWaiting, test_spin_some)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
executor.add_callback_group(
|
||||
this->callback_group,
|
||||
this->node->get_node_base_interface());
|
||||
|
||||
this->set_up_and_trigger_waitable();
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
this->executor->spin_some(this->max_duration);
|
||||
executor.spin_some(this->max_duration);
|
||||
this->check_for_busy_waits(start_time);
|
||||
// this should get the inital trigger, but not the follow up in the callback
|
||||
ASSERT_EQ(this->waitable->get_count(), 1u);
|
||||
@@ -138,6 +146,12 @@ TYPED_TEST(TestBusyWaiting, test_spin_some)
|
||||
|
||||
TYPED_TEST(TestBusyWaiting, test_spin)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
executor.add_callback_group(
|
||||
this->callback_group,
|
||||
this->node->get_node_base_interface());
|
||||
|
||||
std::condition_variable cv;
|
||||
std::mutex cv_m;
|
||||
bool first_check_passed = false;
|
||||
@@ -151,8 +165,8 @@ TYPED_TEST(TestBusyWaiting, test_spin)
|
||||
});
|
||||
|
||||
auto start_time = std::chrono::steady_clock::now();
|
||||
std::thread t([this]() {
|
||||
this->executor->spin();
|
||||
std::thread t([&executor]() {
|
||||
executor.spin();
|
||||
});
|
||||
|
||||
// wait until thread has started (first execute of waitable)
|
||||
@@ -172,7 +186,7 @@ TYPED_TEST(TestBusyWaiting, test_spin)
|
||||
}
|
||||
EXPECT_EQ(this->waitable->get_count(), 2u);
|
||||
|
||||
this->executor->cancel();
|
||||
executor.cancel();
|
||||
t.join();
|
||||
|
||||
this->check_for_busy_waits(start_time);
|
||||
|
||||
@@ -267,15 +267,12 @@ public:
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
executor.cancel();
|
||||
node.reset();
|
||||
|
||||
// Clean up thread object
|
||||
if (standalone_thread.joinable()) {
|
||||
standalone_thread.join();
|
||||
}
|
||||
|
||||
node.reset();
|
||||
sim_clock_node.reset();
|
||||
}
|
||||
|
||||
std::shared_ptr<TimerNode> node;
|
||||
@@ -285,12 +282,20 @@ public:
|
||||
T executor;
|
||||
};
|
||||
|
||||
#if !defined(_WIN32)
|
||||
# ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
# endif
|
||||
#endif
|
||||
using MainExecutorTypes =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::executors::StaticSingleThreadedExecutor>;
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor>;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
// TODO(@fujitatomoya): this test excludes EventExecutor because it does not
|
||||
// support simulation time used for this test to relax the racy condition.
|
||||
// See more details for https://github.com/ros2/rclcpp/issues/2457.
|
||||
@@ -305,6 +310,7 @@ 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();
|
||||
@@ -322,6 +328,7 @@ 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();
|
||||
@@ -348,6 +355,8 @@ 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);
|
||||
@@ -375,6 +384,8 @@ 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);
|
||||
@@ -408,6 +419,8 @@ 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);
|
||||
|
||||
@@ -445,6 +458,8 @@ 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);
|
||||
|
||||
|
||||
@@ -99,6 +99,14 @@ TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup_with_cbgroup)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
|
||||
// TODO(alsora): Enable when https://github.com/ros2/rclcpp/pull/2595 gets merged
|
||||
if (
|
||||
std::is_same<ExecutorType, rclcpp::executors::SingleThreadedExecutor>() ||
|
||||
std::is_same<ExecutorType, rclcpp::executors::MultiThreadedExecutor>())
|
||||
{
|
||||
GTEST_SKIP();
|
||||
}
|
||||
|
||||
ExecutorType executor;
|
||||
|
||||
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
|
||||
|
||||
@@ -32,6 +32,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
constexpr std::chrono::milliseconds PERIOD_MS = 1000ms;
|
||||
|
||||
88
rclcpp/test/rclcpp/executors/test_reinitialized_timers.cpp
Normal file
88
rclcpp/test/rclcpp/executors/test_reinitialized_timers.cpp
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2024 iRobot Corporation.
|
||||
//
|
||||
// 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 <cstddef>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "./executor_types.hpp"
|
||||
|
||||
template<typename ExecutorType>
|
||||
class TestTimersLifecycle : public testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp() override {rclcpp::init(0, nullptr);}
|
||||
|
||||
void TearDown() override {rclcpp::shutdown();}
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE(TestTimersLifecycle, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
TYPED_TEST(TestTimersLifecycle, timers_lifecycle_reinitialized_object)
|
||||
{
|
||||
using ExecutorType = TypeParam;
|
||||
ExecutorType executor;
|
||||
|
||||
auto timers_period = std::chrono::milliseconds(50);
|
||||
auto node = std::make_shared<rclcpp::Node>("test_node");
|
||||
|
||||
executor.add_node(node);
|
||||
|
||||
size_t count_1 = 0;
|
||||
auto timer_1 = rclcpp::create_timer(
|
||||
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_1]() {count_1++;});
|
||||
|
||||
size_t count_2 = 0;
|
||||
auto timer_2 = rclcpp::create_timer(
|
||||
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_2]() {count_2++;});
|
||||
|
||||
{
|
||||
std::thread executor_thread([&executor]() {executor.spin();});
|
||||
|
||||
while (count_2 < 10u) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
executor.cancel();
|
||||
executor_thread.join();
|
||||
|
||||
EXPECT_GE(count_2, 10u);
|
||||
EXPECT_LE(count_2 - count_1, 1u);
|
||||
}
|
||||
|
||||
count_1 = 0;
|
||||
timer_1 = rclcpp::create_timer(
|
||||
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_1]() {count_1++;});
|
||||
|
||||
count_2 = 0;
|
||||
timer_2 = rclcpp::create_timer(
|
||||
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_2]() {count_2++;});
|
||||
|
||||
{
|
||||
std::thread executor_thread([&executor]() {executor.spin();});
|
||||
|
||||
while (count_2 < 10u) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
}
|
||||
executor.cancel();
|
||||
executor_thread.join();
|
||||
|
||||
EXPECT_GE(count_2, 10u);
|
||||
EXPECT_LE(count_2 - count_1, 1u);
|
||||
}
|
||||
}
|
||||
@@ -20,12 +20,13 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
|
||||
#include "./executor_types.hpp"
|
||||
#include "../../mocking_utils/patch.hpp"
|
||||
#include "../../utils/rclcpp_gtest_macros.hpp"
|
||||
|
||||
@@ -46,7 +47,15 @@ public:
|
||||
};
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, add_callback_group_trigger_guard_failed) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
@@ -56,12 +65,20 @@ 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 trigger guard condition on callback group add: error not set"));
|
||||
std::runtime_error("Failed to handle entities update on callback group add: error not set"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, add_node_trigger_guard_failed) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
|
||||
{
|
||||
@@ -69,12 +86,20 @@ 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 trigger guard condition on node add: error not set"));
|
||||
std::runtime_error("Failed to handle entities update on node add: error not set"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, remove_callback_group_trigger_guard_failed) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
|
||||
rclcpp::CallbackGroupType::MutuallyExclusive);
|
||||
@@ -87,12 +112,20 @@ 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 trigger guard condition on callback group remove: error not set"));
|
||||
"Failed to handle entities update on callback group remove: error not set"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, remove_node_failed) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
|
||||
{
|
||||
@@ -105,7 +138,15 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_node_failed) {
|
||||
}
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, remove_node_trigger_guard_failed) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
|
||||
executor.add_node(node);
|
||||
@@ -115,12 +156,20 @@ 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 trigger guard condition on node remove: error not set"));
|
||||
std::runtime_error("Failed to handle entities update on node remove: error not set"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestStaticSingleThreadedExecutor, execute_service) {
|
||||
rclcpp::executors::StaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic push
|
||||
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
#endif
|
||||
|
||||
DeprecatedStaticSingleThreadedExecutor executor;
|
||||
#ifdef __clang__
|
||||
# pragma clang diagnostic pop
|
||||
#endif
|
||||
auto node = std::make_shared<rclcpp::Node>("node", "ns");
|
||||
executor.add_node(node);
|
||||
|
||||
|
||||
@@ -13,7 +13,6 @@
|
||||
// limitations under the License.
|
||||
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
@@ -38,6 +38,7 @@ protected:
|
||||
{
|
||||
node.reset();
|
||||
wrapped_node.reset();
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
static rclcpp::Node::SharedPtr node;
|
||||
|
||||
@@ -47,9 +47,13 @@ constexpr char absolute_namespace[] = "/ns";
|
||||
class TestNodeGraph : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void SetUp()
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node_ = std::make_shared<rclcpp::Node>(node_name, node_namespace);
|
||||
|
||||
// This dynamic cast is not necessary for the unittests, but instead is used to ensure
|
||||
@@ -59,7 +63,7 @@ public:
|
||||
ASSERT_NE(nullptr, node_graph_);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -61,7 +62,7 @@ protected:
|
||||
std::shared_ptr<rclcpp::Node> node;
|
||||
rclcpp::node_interfaces::NodeParameters * node_parameters;
|
||||
|
||||
rcpputils::fs::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
std::filesystem::path test_resources_path{TEST_RESOURCES_DIRECTORY};
|
||||
};
|
||||
|
||||
TEST_F(TestNodeParameters, construct_destruct_rcl_errors) {
|
||||
|
||||
@@ -31,13 +31,13 @@ public:
|
||||
void add_to_wait_set(rcl_wait_set_t &) override {}
|
||||
bool is_ready(const rcl_wait_set_t &) override {return false;}
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
void execute(const std::shared_ptr<void> &) override {}
|
||||
|
||||
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
|
||||
void clear_on_ready_callback() override {}
|
||||
|
||||
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
|
||||
};
|
||||
|
||||
class TestNodeWaitables : public ::testing::Test
|
||||
|
||||
@@ -51,13 +51,13 @@ public:
|
||||
return test_waitable_result;
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
take_data() override
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
void execute(const std::shared_ptr<void> &) override {}
|
||||
|
||||
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
|
||||
void clear_on_ready_callback() override {}
|
||||
|
||||
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
|
||||
};
|
||||
|
||||
struct RclWaitSetSizes
|
||||
|
||||
@@ -23,13 +23,14 @@
|
||||
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
#include "test_msgs/msg/empty.h"
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
#include "rclcpp/executor.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "./executors/executor_types.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
template<typename T>
|
||||
@@ -49,48 +50,8 @@ public:
|
||||
template<typename T>
|
||||
class TestAddCallbackGroupsToExecutorStable : public TestAddCallbackGroupsToExecutor<T> {};
|
||||
|
||||
using ExecutorTypes =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::executors::StaticSingleThreadedExecutor,
|
||||
rclcpp::experimental::executors::EventsExecutor>;
|
||||
|
||||
class ExecutorTypeNames
|
||||
{
|
||||
public:
|
||||
template<typename T>
|
||||
static std::string GetName(int idx)
|
||||
{
|
||||
(void)idx;
|
||||
if (std::is_same<T, rclcpp::executors::SingleThreadedExecutor>()) {
|
||||
return "SingleThreadedExecutor";
|
||||
}
|
||||
|
||||
if (std::is_same<T, rclcpp::executors::MultiThreadedExecutor>()) {
|
||||
return "MultiThreadedExecutor";
|
||||
}
|
||||
|
||||
if (std::is_same<T, rclcpp::executors::StaticSingleThreadedExecutor>()) {
|
||||
return "StaticSingleThreadedExecutor";
|
||||
}
|
||||
|
||||
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
|
||||
return "EventsExecutor";
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutor, ExecutorTypes, ExecutorTypeNames);
|
||||
|
||||
// StaticSingleThreadedExecutor is not included in these tests for now
|
||||
using StandardExecutors =
|
||||
::testing::Types<
|
||||
rclcpp::executors::SingleThreadedExecutor,
|
||||
rclcpp::executors::MultiThreadedExecutor,
|
||||
rclcpp::experimental::executors::EventsExecutor>;
|
||||
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutorStable, StandardExecutors, ExecutorTypeNames);
|
||||
|
||||
/*
|
||||
@@ -216,7 +177,6 @@ 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();
|
||||
}
|
||||
@@ -345,32 +305,30 @@ 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 =
|
||||
[&subscription, &publisher, &timer, &cb_grp, &node, &sub_callback, &timer_promise]() {
|
||||
if (timer) {
|
||||
timer.reset();
|
||||
[&publisher, &timer_promise]() {
|
||||
if (publisher->get_subscription_count() == 0) {
|
||||
// If discovery hasn't happened yet, get out.
|
||||
return;
|
||||
}
|
||||
|
||||
// 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;
|
||||
timer = node->create_wall_timer(100ms, timer_callback);
|
||||
|
||||
rclcpp::TimerBase::SharedPtr 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,7 +24,6 @@
|
||||
#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,7 +21,6 @@
|
||||
|
||||
#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,31 +89,10 @@ 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(
|
||||
{
|
||||
@@ -123,27 +102,6 @@ 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::milliseconds(1));
|
||||
rclcpp::Duration duration(std::chrono::nanoseconds(1));
|
||||
qos_profile.deadline(duration);
|
||||
qos_profile.lifespan(duration);
|
||||
qos_profile.liveliness_lease_duration(duration);
|
||||
|
||||
@@ -178,10 +178,7 @@ 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<std::atomic_bool> thread_finished(10);
|
||||
for (std::atomic_bool & finished : thread_finished) {
|
||||
finished = false;
|
||||
}
|
||||
std::vector<bool> thread_finished(10, false);
|
||||
|
||||
std::vector<std::thread> threads;
|
||||
|
||||
@@ -199,7 +196,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 std::atomic_bool & finished : thread_finished) {
|
||||
for (const bool & finished : thread_finished) {
|
||||
EXPECT_FALSE(finished);
|
||||
}
|
||||
|
||||
@@ -210,7 +207,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 std::atomic_bool & finished : thread_finished) {
|
||||
for (const bool finished : thread_finished) {
|
||||
if (!finished) {
|
||||
threads_finished = false;
|
||||
}
|
||||
@@ -220,7 +217,7 @@ TEST_F(TestClockWakeup, multiple_threads_wait_on_one_clock) {
|
||||
cur_time = std::chrono::steady_clock::now();
|
||||
}
|
||||
|
||||
for (const std::atomic_bool & finished : thread_finished) {
|
||||
for (const bool finished : thread_finished) {
|
||||
EXPECT_TRUE(finished);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,246 +0,0 @@
|
||||
// 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,7 +20,6 @@
|
||||
#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;
|
||||
|
||||
@@ -93,19 +92,3 @@ 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 trigger guard condition on callback group add: error not set"));
|
||||
std::runtime_error("Failed to handle entities update 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 trigger guard condition on callback group remove: error not set"));
|
||||
"Failed to handle entities update on callback group remove: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestExecutor, remove_node_not_associated) {
|
||||
|
||||
@@ -34,6 +34,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
void
|
||||
|
||||
@@ -27,6 +27,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(TestFindWeakNodes, allocator_strategy_with_weak_nodes) {
|
||||
|
||||
@@ -14,6 +14,8 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
@@ -28,6 +30,7 @@
|
||||
#include "../mocking_utils/patch.hpp"
|
||||
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
#include "test_msgs/srv/basic_types.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
@@ -105,7 +108,7 @@ TEST_F(TestGenericClient, construction_and_destruction) {
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto client = node->create_generic_client("test_service", "test_msgs/srv/InvalidType");
|
||||
}, std::runtime_error);
|
||||
}, rclcpp::exceptions::InvalidServiceTypeError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,3 +231,69 @@ TEST_F(TestGenericClientSub, construction_and_destruction) {
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestGenericClientSub, async_send_request_with_request) {
|
||||
const std::string service_name = "test_service";
|
||||
int64_t expected_change = 1111;
|
||||
|
||||
auto client = node->create_generic_client(service_name, "test_msgs/srv/BasicTypes");
|
||||
|
||||
auto callback = [&expected_change](
|
||||
const test_msgs::srv::BasicTypes::Request::SharedPtr request,
|
||||
test_msgs::srv::BasicTypes::Response::SharedPtr response) {
|
||||
response->int64_value = request->int64_value + expected_change;
|
||||
};
|
||||
|
||||
auto service =
|
||||
node->create_service<test_msgs::srv::BasicTypes>(service_name, std::move(callback));
|
||||
|
||||
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
|
||||
ASSERT_TRUE(client->service_is_ready());
|
||||
|
||||
test_msgs::srv::BasicTypes::Request request;
|
||||
request.int64_value = 12345678;
|
||||
|
||||
auto future = client->async_send_request(static_cast<void *>(&request));
|
||||
rclcpp::spin_until_future_complete(
|
||||
node->get_node_base_interface(), future, std::chrono::seconds(5));
|
||||
ASSERT_TRUE(future.valid());
|
||||
auto get_untyped_response = future.get();
|
||||
auto typed_response =
|
||||
static_cast<test_msgs::srv::BasicTypes::Response *>(get_untyped_response.get());
|
||||
EXPECT_EQ(typed_response->int64_value, (request.int64_value + expected_change));
|
||||
}
|
||||
|
||||
TEST_F(TestGenericClientSub, async_send_request_with_request_and_callback) {
|
||||
const std::string service_name = "test_service";
|
||||
int64_t expected_change = 2222;
|
||||
|
||||
auto client = node->create_generic_client(service_name, "test_msgs/srv/BasicTypes");
|
||||
|
||||
auto server_callback = [&expected_change](
|
||||
const test_msgs::srv::BasicTypes::Request::SharedPtr request,
|
||||
test_msgs::srv::BasicTypes::Response::SharedPtr response) {
|
||||
response->int64_value = request->int64_value + expected_change;
|
||||
};
|
||||
|
||||
auto service =
|
||||
node->create_service<test_msgs::srv::BasicTypes>(service_name, std::move(server_callback));
|
||||
|
||||
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
|
||||
ASSERT_TRUE(client->service_is_ready());
|
||||
|
||||
test_msgs::srv::BasicTypes::Request request;
|
||||
request.int64_value = 12345678;
|
||||
|
||||
auto client_callback = [&request, &expected_change](
|
||||
rclcpp::GenericClient::SharedFuture future) {
|
||||
auto untyped_response = future.get();
|
||||
auto typed_response =
|
||||
static_cast<test_msgs::srv::BasicTypes::Response *>(untyped_response.get());
|
||||
EXPECT_EQ(typed_response->int64_value, (request.int64_value + expected_change));
|
||||
};
|
||||
|
||||
auto future =
|
||||
client->async_send_request(static_cast<void *>(&request), client_callback);
|
||||
rclcpp::spin_until_future_complete(
|
||||
node->get_node_base_interface(), future, std::chrono::seconds(5));
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
|
||||
#include <gmock/gmock.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <future>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
422
rclcpp/test/rclcpp/test_generic_service.cpp
Normal file
422
rclcpp/test/rclcpp/test_generic_service.cpp
Normal file
@@ -0,0 +1,422 @@
|
||||
// Copyright 2024 Sony Group Corporation.
|
||||
//
|
||||
// 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 <cstddef>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "../mocking_utils/patch.hpp"
|
||||
#include "../utils/rclcpp_gtest_macros.hpp"
|
||||
|
||||
#include "rcl_interfaces/srv/list_parameters.hpp"
|
||||
#include "test_msgs/srv/empty.hpp"
|
||||
#include "test_msgs/srv/basic_types.hpp"
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
class TestGenericService : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("test_node", "/ns");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
class TestGenericServiceSub : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("test_node", "/ns");
|
||||
subnode = node->create_sub_node("sub_ns");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::Node::SharedPtr subnode;
|
||||
};
|
||||
|
||||
/*
|
||||
Testing service construction and destruction.
|
||||
*/
|
||||
TEST_F(TestGenericService, construction_and_destruction) {
|
||||
auto callback = [](
|
||||
rclcpp::GenericService::SharedRequest,
|
||||
rclcpp::GenericService::SharedResponse) {};
|
||||
{
|
||||
auto generic_service = node->create_generic_service(
|
||||
"test_generic_service", "rcl_interfaces/srv/ListParameters", callback);
|
||||
EXPECT_NE(nullptr, generic_service->get_service_handle());
|
||||
const rclcpp::ServiceBase * const_service_base = generic_service.get();
|
||||
EXPECT_NE(nullptr, const_service_base->get_service_handle());
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto generic_service = node->create_generic_service(
|
||||
"invalid_service?", "test_msgs/srv/Empty", callback);
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto generic_service = node->create_generic_service(
|
||||
"test_generic_service", "test_msgs/srv/NotExist", callback);
|
||||
}, rclcpp::exceptions::InvalidServiceTypeError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing service construction and destruction for subnodes.
|
||||
*/
|
||||
TEST_F(TestGenericServiceSub, construction_and_destruction) {
|
||||
auto callback = [](
|
||||
rclcpp::GenericService::SharedRequest,
|
||||
rclcpp::GenericService::SharedResponse) {};
|
||||
{
|
||||
auto generic_service = subnode->create_generic_service(
|
||||
"test_generic_service", "rcl_interfaces/srv/ListParameters", callback);
|
||||
EXPECT_STREQ(generic_service->get_service_name(), "/ns/sub_ns/test_generic_service");
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto generic_service = subnode->create_generic_service(
|
||||
"invalid_service?", "test_msgs/srv/Empty", callback);
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{
|
||||
auto generic_service = subnode->create_generic_service(
|
||||
"test_generic_service", "test_msgs/srv/NotExist", callback);
|
||||
}, rclcpp::exceptions::InvalidServiceTypeError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, construction_and_destruction_rcl_errors) {
|
||||
auto callback = [](
|
||||
rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
|
||||
{
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_service_init, RCL_RET_ERROR);
|
||||
// reset() isn't necessary for this exception, it just avoids unused return value warning
|
||||
EXPECT_THROW(
|
||||
node->create_generic_service("service", "test_msgs/srv/Empty", callback).reset(),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
{
|
||||
// reset() is required for this one
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_service_fini, RCL_RET_ERROR);
|
||||
EXPECT_NO_THROW(
|
||||
node->create_generic_service("service", "test_msgs/srv/Empty", callback).reset());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, generic_service_take_request) {
|
||||
auto callback = [](
|
||||
rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
auto generic_service =
|
||||
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
|
||||
{
|
||||
auto request_id = generic_service->create_request_header();
|
||||
auto request = generic_service->create_request();
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_take_request, RCL_RET_OK);
|
||||
EXPECT_TRUE(generic_service->take_request(request, *request_id.get()));
|
||||
}
|
||||
{
|
||||
auto request_id = generic_service->create_request_header();
|
||||
auto request = generic_service->create_request();
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_take_request, RCL_RET_SERVICE_TAKE_FAILED);
|
||||
EXPECT_FALSE(generic_service->take_request(request, *request_id.get()));
|
||||
}
|
||||
{
|
||||
auto request_id = generic_service->create_request_header();
|
||||
auto request = generic_service->create_request();
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_take_request, RCL_RET_ERROR);
|
||||
EXPECT_THROW(
|
||||
generic_service->take_request(request, *request_id.get()), rclcpp::exceptions::RCLError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, generic_service_send_response) {
|
||||
auto callback = [](
|
||||
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
auto generic_service =
|
||||
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
|
||||
|
||||
{
|
||||
auto request_id = generic_service->create_request_header();
|
||||
auto response = generic_service->create_response();
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_response, RCL_RET_OK);
|
||||
EXPECT_NO_THROW(generic_service->send_response(*request_id.get(), response));
|
||||
}
|
||||
|
||||
{
|
||||
auto request_id = generic_service->create_request_header();
|
||||
auto response = generic_service->create_response();
|
||||
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_response, RCL_RET_ERROR);
|
||||
EXPECT_THROW(
|
||||
generic_service->send_response(*request_id.get(), response),
|
||||
rclcpp::exceptions::RCLError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing on_new_request callbacks.
|
||||
*/
|
||||
TEST_F(TestGenericService, generic_service_on_new_request_callback) {
|
||||
auto server_callback = [](
|
||||
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {FAIL();};
|
||||
rclcpp::ServicesQoS service_qos;
|
||||
service_qos.keep_last(3);
|
||||
auto generic_service = node->create_generic_service(
|
||||
"~/test_service", "test_msgs/srv/Empty", server_callback, service_qos);
|
||||
|
||||
std::atomic<size_t> c1 {0};
|
||||
auto increase_c1_cb = [&c1](size_t count_msgs) {c1 += count_msgs;};
|
||||
generic_service->set_on_new_request_callback(increase_c1_cb);
|
||||
|
||||
auto client = node->create_client<test_msgs::srv::Empty>(
|
||||
"~/test_service", service_qos);
|
||||
{
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
client->async_send_request(request);
|
||||
}
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
do {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
} while (c1 == 0 && std::chrono::steady_clock::now() - start < 10s);
|
||||
|
||||
EXPECT_EQ(c1.load(), 1u);
|
||||
|
||||
std::atomic<size_t> c2 {0};
|
||||
auto increase_c2_cb = [&c2](size_t count_msgs) {c2 += count_msgs;};
|
||||
generic_service->set_on_new_request_callback(increase_c2_cb);
|
||||
|
||||
{
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
client->async_send_request(request);
|
||||
}
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
do {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
} while (c2 == 0 && std::chrono::steady_clock::now() - start < 10s);
|
||||
|
||||
EXPECT_EQ(c1.load(), 1u);
|
||||
EXPECT_EQ(c2.load(), 1u);
|
||||
|
||||
generic_service->clear_on_new_request_callback();
|
||||
|
||||
{
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
client->async_send_request(request);
|
||||
client->async_send_request(request);
|
||||
client->async_send_request(request);
|
||||
}
|
||||
|
||||
std::atomic<size_t> c3 {0};
|
||||
auto increase_c3_cb = [&c3](size_t count_msgs) {c3 += count_msgs;};
|
||||
generic_service->set_on_new_request_callback(increase_c3_cb);
|
||||
|
||||
start = std::chrono::steady_clock::now();
|
||||
do {
|
||||
std::this_thread::sleep_for(100ms);
|
||||
} while (c3 < 3 && std::chrono::steady_clock::now() - start < 10s);
|
||||
|
||||
EXPECT_EQ(c1.load(), 1u);
|
||||
EXPECT_EQ(c2.load(), 1u);
|
||||
EXPECT_EQ(c3.load(), 3u);
|
||||
|
||||
std::function<void(size_t)> invalid_cb = nullptr;
|
||||
EXPECT_THROW(generic_service->set_on_new_request_callback(invalid_cb), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, rcl_service_response_publisher_get_actual_qos_error) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_service_response_publisher_get_actual_qos, nullptr);
|
||||
auto callback = [](
|
||||
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
auto generic_service =
|
||||
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
generic_service->get_response_publisher_actual_qos(),
|
||||
std::runtime_error("failed to get service's response publisher qos settings: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, rcl_service_request_subscription_get_actual_qos_error) {
|
||||
auto mock = mocking_utils::patch_and_return(
|
||||
"lib:rclcpp", rcl_service_request_subscription_get_actual_qos, nullptr);
|
||||
auto callback = [](
|
||||
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
auto generic_service =
|
||||
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
|
||||
RCLCPP_EXPECT_THROW_EQ(
|
||||
generic_service->get_request_subscription_actual_qos(),
|
||||
std::runtime_error("failed to get service's request subscription qos settings: error not set"));
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, generic_service_qos) {
|
||||
rclcpp::ServicesQoS qos_profile;
|
||||
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
|
||||
rclcpp::Duration duration(std::chrono::nanoseconds(1));
|
||||
qos_profile.deadline(duration);
|
||||
qos_profile.lifespan(duration);
|
||||
qos_profile.liveliness_lease_duration(duration);
|
||||
|
||||
auto callback = [](
|
||||
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
|
||||
auto generic_service =
|
||||
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback, qos_profile);
|
||||
|
||||
auto rs_qos = generic_service->get_request_subscription_actual_qos();
|
||||
auto rp_qos = generic_service->get_response_publisher_actual_qos();
|
||||
|
||||
EXPECT_EQ(qos_profile, rp_qos);
|
||||
// Lifespan has no meaning for subscription/readers
|
||||
rs_qos.lifespan(qos_profile.lifespan());
|
||||
EXPECT_EQ(qos_profile, rs_qos);
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, generic_service_qos_depth) {
|
||||
uint64_t server_cb_count_ = 0;
|
||||
auto server_callback = [&](
|
||||
const rclcpp::GenericService::SharedRequest,
|
||||
rclcpp::GenericService::SharedResponse) {server_cb_count_++;};
|
||||
|
||||
auto server_node = std::make_shared<rclcpp::Node>("server_node", "/ns");
|
||||
|
||||
rclcpp::QoS server_qos_profile(2);
|
||||
|
||||
auto generic_service = server_node->create_generic_service(
|
||||
"test_qos_depth", "test_msgs/srv/Empty", std::move(server_callback), server_qos_profile);
|
||||
|
||||
rclcpp::QoS client_qos_profile(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_default));
|
||||
auto client = node->create_client<test_msgs::srv::Empty>("test_qos_depth", client_qos_profile);
|
||||
|
||||
::testing::AssertionResult request_result = ::testing::AssertionSuccess();
|
||||
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
|
||||
|
||||
auto client_callback = [&request_result](
|
||||
rclcpp::Client<test_msgs::srv::Empty>::SharedFuture future_response) {
|
||||
if (nullptr == future_response.get()) {
|
||||
request_result = ::testing::AssertionFailure() << "Future response was null";
|
||||
}
|
||||
};
|
||||
|
||||
uint64_t client_requests = 5;
|
||||
for (uint64_t i = 0; i < client_requests; i++) {
|
||||
client->async_send_request(request, client_callback);
|
||||
std::this_thread::sleep_for(10ms);
|
||||
}
|
||||
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
while ((server_cb_count_ < server_qos_profile.depth()) &&
|
||||
(std::chrono::steady_clock::now() - start) < 1s)
|
||||
{
|
||||
rclcpp::spin_some(server_node);
|
||||
std::this_thread::sleep_for(1ms);
|
||||
}
|
||||
|
||||
// Spin an extra time to check if server QoS depth has been ignored,
|
||||
// so more server responses might be processed than expected.
|
||||
rclcpp::spin_some(server_node);
|
||||
|
||||
EXPECT_EQ(server_cb_count_, server_qos_profile.depth());
|
||||
}
|
||||
|
||||
TEST_F(TestGenericService, generic_service_and_client) {
|
||||
const std::string service_name = "test_service";
|
||||
const std::string service_type = "test_msgs/srv/BasicTypes";
|
||||
int64_t expected_change = 87654321;
|
||||
|
||||
auto callback = [&expected_change](
|
||||
const rclcpp::GenericService::SharedRequest request,
|
||||
rclcpp::GenericService::SharedResponse response) {
|
||||
auto typed_request = static_cast<test_msgs::srv::BasicTypes_Request *>(request.get());
|
||||
auto typed_response = static_cast<test_msgs::srv::BasicTypes_Response *>(response.get());
|
||||
|
||||
typed_response->int64_value = typed_request->int64_value + expected_change;
|
||||
};
|
||||
auto generic_service = node->create_generic_service(service_name, service_type, callback);
|
||||
|
||||
auto client = node->create_client<test_msgs::srv::BasicTypes>(service_name);
|
||||
|
||||
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
|
||||
ASSERT_TRUE(client->service_is_ready());
|
||||
|
||||
auto request = std::make_shared<test_msgs::srv::BasicTypes::Request>();
|
||||
request->int64_value = 12345678;
|
||||
|
||||
auto generic_client_callback = [&request, &expected_change](
|
||||
std::shared_future<test_msgs::srv::BasicTypes_Response::SharedPtr> future) {
|
||||
auto response = future.get();
|
||||
EXPECT_EQ(response->int64_value, (request->int64_value + expected_change));
|
||||
};
|
||||
|
||||
auto future =
|
||||
client->async_send_request(request, generic_client_callback);
|
||||
rclcpp::spin_until_future_complete(
|
||||
node->get_node_base_interface(), future, std::chrono::seconds(5));
|
||||
}
|
||||
@@ -27,6 +27,11 @@ protected:
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
|
||||
@@ -123,6 +123,7 @@ public:
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
// need to store the messages somewhere otherwise the memory address will be reused
|
||||
ConstMessageSharedPtr shared_msg;
|
||||
MessageUniquePtr unique_msg;
|
||||
@@ -158,9 +159,9 @@ class PublisherBase
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
|
||||
|
||||
explicit PublisherBase(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: qos_profile(qos),
|
||||
topic_name("topic")
|
||||
explicit PublisherBase(const std::string & topic, const rclcpp::QoS & qos)
|
||||
: topic_name(topic),
|
||||
qos_profile(qos)
|
||||
{}
|
||||
|
||||
virtual ~PublisherBase()
|
||||
@@ -205,10 +206,12 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
rclcpp::QoS qos_profile;
|
||||
std::string topic_name;
|
||||
uint64_t intra_process_publisher_id_;
|
||||
IntraProcessManagerWeakPtr weak_ipm_;
|
||||
|
||||
private:
|
||||
std::string topic_name;
|
||||
rclcpp::QoS qos_profile;
|
||||
};
|
||||
|
||||
template<typename T, typename Alloc = std::allocator<void>>
|
||||
@@ -223,8 +226,8 @@ public:
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>)
|
||||
|
||||
explicit Publisher(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: PublisherBase(qos)
|
||||
explicit Publisher(const std::string & topic, const rclcpp::QoS & qos)
|
||||
: PublisherBase(topic, qos)
|
||||
{
|
||||
auto allocator = std::make_shared<Alloc>();
|
||||
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
|
||||
@@ -258,9 +261,9 @@ public:
|
||||
|
||||
explicit SubscriptionIntraProcessBase(
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::string & topic = "topic",
|
||||
rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: qos_profile(qos), topic_name(topic)
|
||||
const std::string & topic,
|
||||
const rclcpp::QoS & qos)
|
||||
: topic_name(topic), qos_profile(qos)
|
||||
{
|
||||
(void)context;
|
||||
}
|
||||
@@ -292,8 +295,8 @@ public:
|
||||
size_t
|
||||
available_capacity() const = 0;
|
||||
|
||||
rclcpp::QoS qos_profile;
|
||||
std::string topic_name;
|
||||
rclcpp::QoS qos_profile;
|
||||
};
|
||||
|
||||
template<
|
||||
@@ -307,8 +310,8 @@ class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
|
||||
|
||||
explicit SubscriptionIntraProcessBuffer(rclcpp::QoS qos)
|
||||
: SubscriptionIntraProcessBase(nullptr, "topic", qos), take_shared_method(false)
|
||||
explicit SubscriptionIntraProcessBuffer(const std::string & topic, const rclcpp::QoS & qos)
|
||||
: SubscriptionIntraProcessBase(nullptr, topic, qos), take_shared_method(false)
|
||||
{
|
||||
buffer = std::make_unique<rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>>();
|
||||
}
|
||||
@@ -375,8 +378,8 @@ class SubscriptionIntraProcess : public SubscriptionIntraProcessBuffer<
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
|
||||
|
||||
explicit SubscriptionIntraProcess(rclcpp::QoS qos = rclcpp::QoS(10))
|
||||
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(qos)
|
||||
explicit SubscriptionIntraProcess(const std::string & topic, const rclcpp::QoS & qos)
|
||||
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(topic, qos)
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -466,12 +469,11 @@ TEST(TestIntraProcessManager, add_pub_sub) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10).best_effort());
|
||||
|
||||
auto p2 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
|
||||
p2->topic_name = "different_topic_name";
|
||||
auto p2 = std::make_shared<PublisherT>("different_topic_name", rclcpp::QoS(10).best_effort());
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).best_effort());
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10).best_effort());
|
||||
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
auto p2_id = ipm->add_publisher(p2);
|
||||
@@ -480,24 +482,42 @@ TEST(TestIntraProcessManager, add_pub_sub) {
|
||||
bool unique_ids = p1_id != p2_id && p2_id != s1_id;
|
||||
ASSERT_TRUE(unique_ids);
|
||||
|
||||
// p1 has 1 subcription, s1
|
||||
size_t p1_subs = ipm->get_subscription_count(p1_id);
|
||||
// p2 has 0 subscriptions
|
||||
size_t p2_subs = ipm->get_subscription_count(p2_id);
|
||||
// Non-existent publisher_id has 0 subscriptions
|
||||
size_t non_existing_pub_subs = ipm->get_subscription_count(42);
|
||||
ASSERT_EQ(1u, p1_subs);
|
||||
ASSERT_EQ(0u, p2_subs);
|
||||
ASSERT_EQ(0u, non_existing_pub_subs);
|
||||
|
||||
auto p3 = std::make_shared<PublisherT>(rclcpp::QoS(10).reliable());
|
||||
auto p3 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10).reliable());
|
||||
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).reliable());
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10).reliable());
|
||||
|
||||
// s2 may be able to communicate with p1 depending on the RMW
|
||||
auto s2_id = ipm->template add_subscription<MessageT>(s2);
|
||||
// p3 can definitely communicate with s2, may be able to communicate with s1 depending on the RMW
|
||||
auto p3_id = ipm->add_publisher(p3);
|
||||
|
||||
// p1 definitely matches subscription s1, since the topic name and QoS match exactly.
|
||||
// If the RMW can match best-effort publishers to reliable subscriptions (like Zenoh can),
|
||||
// then p1 will also match s2.
|
||||
p1_subs = ipm->get_subscription_count(p1_id);
|
||||
// No subscriptions with a topic name of "different_topic_name" were added.
|
||||
p2_subs = ipm->get_subscription_count(p2_id);
|
||||
// On all current RMWs (DDS and Zenoh), a reliable publisher like p3 can communicate with both
|
||||
// reliable and best-effort subscriptions (s1 and s2).
|
||||
size_t p3_subs = ipm->get_subscription_count(p3_id);
|
||||
ASSERT_EQ(1u, p1_subs);
|
||||
|
||||
rclcpp::QoSCheckCompatibleResult qos_compatible =
|
||||
rclcpp::qos_check_compatible(p1->get_actual_qos(), s2->get_actual_qos());
|
||||
if (qos_compatible.compatibility == rclcpp::QoSCompatibility::Error) {
|
||||
ASSERT_EQ(1u, p1_subs);
|
||||
} else {
|
||||
ASSERT_EQ(2u, p1_subs);
|
||||
}
|
||||
ASSERT_EQ(0u, p2_subs);
|
||||
ASSERT_EQ(2u, p3_subs);
|
||||
|
||||
@@ -528,11 +548,11 @@ TEST(TestIntraProcessManager, single_subscription) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>();
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
p1->set_intra_process_manager(p1_id, ipm);
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s1->take_shared_method = false;
|
||||
auto s1_id = ipm->template add_subscription<MessageT>(s1);
|
||||
|
||||
@@ -543,7 +563,7 @@ TEST(TestIntraProcessManager, single_subscription) {
|
||||
ASSERT_EQ(original_message_pointer, received_message_pointer_1);
|
||||
|
||||
ipm->remove_subscription(s1_id);
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s2->take_shared_method = true;
|
||||
auto s2_id = ipm->template add_subscription<MessageT>(s2);
|
||||
(void)s2_id;
|
||||
@@ -582,15 +602,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>();
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
p1->set_intra_process_manager(p1_id, ipm);
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s1->take_shared_method = false;
|
||||
auto s1_id = ipm->template add_subscription<MessageT>(s1);
|
||||
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s2->take_shared_method = false;
|
||||
auto s2_id = ipm->template add_subscription<MessageT>(s2);
|
||||
|
||||
@@ -606,11 +626,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
ipm->remove_subscription(s1_id);
|
||||
ipm->remove_subscription(s2_id);
|
||||
|
||||
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s3 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s3->take_shared_method = true;
|
||||
auto s3_id = ipm->template add_subscription<MessageT>(s3);
|
||||
|
||||
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s4 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s4->take_shared_method = true;
|
||||
auto s4_id = ipm->template add_subscription<MessageT>(s4);
|
||||
|
||||
@@ -625,11 +645,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
ipm->remove_subscription(s3_id);
|
||||
ipm->remove_subscription(s4_id);
|
||||
|
||||
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s5 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s5->take_shared_method = false;
|
||||
auto s5_id = ipm->template add_subscription<MessageT>(s5);
|
||||
|
||||
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s6 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s6->take_shared_method = false;
|
||||
auto s6_id = ipm->template add_subscription<MessageT>(s6);
|
||||
|
||||
@@ -645,12 +665,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
|
||||
ipm->remove_subscription(s5_id);
|
||||
ipm->remove_subscription(s6_id);
|
||||
|
||||
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s7 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s7->take_shared_method = true;
|
||||
auto s7_id = ipm->template add_subscription<MessageT>(s7);
|
||||
(void)s7_id;
|
||||
|
||||
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s8 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s8->take_shared_method = true;
|
||||
auto s8_id = ipm->template add_subscription<MessageT>(s8);
|
||||
(void)s8_id;
|
||||
@@ -688,15 +708,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>();
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
p1->set_intra_process_manager(p1_id, ipm);
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s1->take_shared_method = true;
|
||||
auto s1_id = ipm->template add_subscription<MessageT>(s1);
|
||||
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s2->take_shared_method = false;
|
||||
auto s2_id = ipm->template add_subscription<MessageT>(s2);
|
||||
|
||||
@@ -711,15 +731,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
ipm->remove_subscription(s1_id);
|
||||
ipm->remove_subscription(s2_id);
|
||||
|
||||
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s3 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s3->take_shared_method = false;
|
||||
auto s3_id = ipm->template add_subscription<MessageT>(s3);
|
||||
|
||||
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s4 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s4->take_shared_method = false;
|
||||
auto s4_id = ipm->template add_subscription<MessageT>(s4);
|
||||
|
||||
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s5 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s5->take_shared_method = true;
|
||||
auto s5_id = ipm->template add_subscription<MessageT>(s5);
|
||||
|
||||
@@ -743,19 +763,19 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
ipm->remove_subscription(s4_id);
|
||||
ipm->remove_subscription(s5_id);
|
||||
|
||||
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s6 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s6->take_shared_method = true;
|
||||
auto s6_id = ipm->template add_subscription<MessageT>(s6);
|
||||
|
||||
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s7 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s7->take_shared_method = true;
|
||||
auto s7_id = ipm->template add_subscription<MessageT>(s7);
|
||||
|
||||
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s8 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s8->take_shared_method = false;
|
||||
auto s8_id = ipm->template add_subscription<MessageT>(s8);
|
||||
|
||||
auto s9 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s9 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s9->take_shared_method = false;
|
||||
auto s9_id = ipm->template add_subscription<MessageT>(s9);
|
||||
|
||||
@@ -781,12 +801,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
|
||||
ipm->remove_subscription(s8_id);
|
||||
ipm->remove_subscription(s9_id);
|
||||
|
||||
auto s10 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s10 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s10->take_shared_method = false;
|
||||
auto s10_id = ipm->template add_subscription<MessageT>(s10);
|
||||
(void)s10_id;
|
||||
|
||||
auto s11 = std::make_shared<SubscriptionIntraProcessT>();
|
||||
auto s11 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
|
||||
s11->take_shared_method = true;
|
||||
auto s11_id = ipm->template add_subscription<MessageT>(s11);
|
||||
(void)s11_id;
|
||||
@@ -831,10 +851,12 @@ TEST(TestIntraProcessManager, lowest_available_capacity) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).best_effort());
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).best_effort());
|
||||
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
|
||||
auto s1 =
|
||||
std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(history_depth).best_effort());
|
||||
auto s2 =
|
||||
std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(history_depth).best_effort());
|
||||
|
||||
auto p1_id = ipm->add_publisher(p1);
|
||||
p1->set_intra_process_manager(p1_id, ipm);
|
||||
@@ -902,7 +924,7 @@ TEST(TestIntraProcessManager, transient_local_invalid_buffer) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).transient_local());
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).transient_local());
|
||||
|
||||
ASSERT_THROW(
|
||||
{
|
||||
@@ -926,14 +948,14 @@ TEST(TestIntraProcessManager, transient_local) {
|
||||
|
||||
auto ipm = std::make_shared<IntraProcessManagerT>();
|
||||
|
||||
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).transient_local());
|
||||
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).transient_local());
|
||||
|
||||
auto s1 =
|
||||
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
|
||||
auto s2 =
|
||||
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
|
||||
auto s3 =
|
||||
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
|
||||
auto s1 = std::make_shared<SubscriptionIntraProcessT>(
|
||||
"topic", rclcpp::QoS(history_depth).transient_local());
|
||||
auto s2 = std::make_shared<SubscriptionIntraProcessT>(
|
||||
"topic", rclcpp::QoS(history_depth).transient_local());
|
||||
auto s3 = std::make_shared<SubscriptionIntraProcessT>(
|
||||
"topic", rclcpp::QoS(history_depth).transient_local());
|
||||
|
||||
s1->take_shared_method = false;
|
||||
s2->take_shared_method = true;
|
||||
|
||||
@@ -40,53 +40,6 @@ 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,6 +14,7 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
@@ -210,15 +211,7 @@ 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_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());
|
||||
auto path = rclcpp::get_log_directory();
|
||||
auto expected_path = std::filesystem::path{"/fake_home_dir"} / ".ros" / "log";
|
||||
EXPECT_EQ(path, expected_path);
|
||||
}
|
||||
|
||||
@@ -14,7 +14,6 @@
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -40,6 +40,11 @@ public:
|
||||
|
||||
std::shared_ptr<void> take_data() override {return nullptr;}
|
||||
void execute(const std::shared_ptr<void> &) override {}
|
||||
|
||||
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
|
||||
void clear_on_ready_callback() override {}
|
||||
|
||||
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
|
||||
};
|
||||
|
||||
class TestMemoryStrategy : 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