Compare commits

...

65 Commits

Author SHA1 Message Date
Alejandro Hernandez Cordero
a4a72ac544 30.1.4 2025-12-23 11:05:21 +01:00
Alejandro Hernandez Cordero
4e166cbed3 Changelog
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-12-23 11:05:17 +01:00
Alejandro Hernández Cordero
6cbb994aca Updated deprecated ament_index_cpp API (#3011)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-12-23 10:27:30 +01:00
fabianhirmann
825b4e4650 Unified Node Interfaces: Add const version of get_node_x_interface() (#3006)
Signed-off-by: Fabian Hirmann <f.hirmann@arti-robots.com>
2025-12-16 10:24:41 +01:00
Lucas Wendland
41c7f68013 Parameter Descriptor Simplification (#2179)
Signed-off-by: CursedRock17 <mtglucas1@gmail.com>
Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-12-05 10:13:49 +01:00
Barry Xu
02caec12c3 ParameterEventHandler support ContentFiltering (#2971)
* ParameterEventHandler support ContentFiltering

Signed-off-by: Barry Xu <barry.xu@sony.com>

* Address review comments

Signed-off-by: Barry Xu <barry.xu@sony.com>

---------

Signed-off-by: Barry Xu <barry.xu@sony.com>
2025-12-03 16:45:45 +09:00
Andrianov Roman
87a4cefb83 update policy_name_from_kind && test_qos (#2156)
Signed-off-by: Splinter1984 <rom.andrianov1984@gmail.com>
Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-12-01 13:35:41 +01:00
Michael Orlov
d3c95a6a46 Add ability to disable and enable subscription's callbacks (#2985)
* Add disable(enable)_callbacks() API to the subscriptions

Signed-off-by: Michael Orlov <morlovmr@gmail.com>

* Add test coverage for the disable(enable)_calbacks()

Signed-off-by: Michael Orlov <morlovmr@gmail.com>

* Address code review comments. Add missing includes in tests

Signed-off-by: Michael Orlov <morlovmr@gmail.com>

* Don't trace callback in dispatch methods if callbacks are disabled

Signed-off-by: Michael Orlov <morlovmr@gmail.com>

* Protect enable/disable callbacks and callbacks execution with mutex

Reasoning:
 To avoid possible UB when callbacks disabled during callback
 execution and callback handler object deleted while execution hasn't
 been finished yet.

Signed-off-by: Michael Orlov <morlovmr@gmail.com>

* Renames for shadowed callback_mutex_ from the EventHandlerBase

Note: We can't reuse `on_new_event_callback_mutex_` from the
EventHandlerBase because those mutex used to protect another type of
callbacks.

Signed-off-by: Michael Orlov <morlovmr@gmail.com>

* Temporary removes the on_new_[message]event on disable_callbacks()

Note: While we are temporary removes the on new message callback and all
 on new event callbacks from the underlying rmw layer to prevent them
 from being called. We can't guarantee that the currently executing
 on_new_[message]event callbacks are finished before we exit from the
 disable_callbacks().

Signed-off-by: Michael Orlov <morlovmr@gmail.com>

* Use recursive_mutex for callback lock in any_subscription_callback

Signed-off-by: Michael Orlov <morlovmr@gmail.com>

* Address CI warnings about usage of deprecated rclcpp::spin_some(node)

- Use spin_some() from the rclcpp::executors::SingleThreadedExecutor
 directly.

Signed-off-by: Michael Orlov <morlovmr@gmail.com>

---------

Signed-off-by: Michael Orlov <morlovmr@gmail.com>
2025-11-27 11:09:49 -08:00
yadunund
1500449c11 Switch to isolated testing via rmw_test_fixture (#2929)
Signed-off-by: Yadunund <yadunund@gmail.com>
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-11-25 10:48:28 +01:00
Tomoya Fujita
95cb964a3b remove I/O from signal handler. (#2986)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-11-19 11:37:04 +01:00
Alejandro Hernandez Cordero
f160024ba4 30.1.3 2025-11-18 11:09:53 +01:00
Alejandro Hernandez Cordero
e6ea37ed5a Changelog
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-11-18 11:09:49 +01:00
Yuchen966
354413c060 correct test function descriptions (#2970)
Signed-off-by: Yuchen Liu <lyuchen9696@gmail.com>
2025-11-17 15:51:23 +01:00
Minju, Lee
63bdf2add4 add : get clients, servers info (#2569)
Signed-off-by: Minju, Lee <dlalswn531@naver.com>
2025-11-17 13:43:50 +01:00
Tim Clephas
ad019b9827 Fix REP url locations (#2987)
This popped up in my global replace

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>
2025-11-17 10:01:03 +01:00
Zheng Qu
eb49444c32 Add get_parameter_or overload returning value or alternative (#2973)
Signed-off-by: Zheng Qu <quzhengrobot@gmail.com>
2025-11-07 12:24:01 +01:00
Alejandro Hernandez Cordero
4d14414d91 30.1.2 2025-10-21 11:43:39 +02:00
Alejandro Hernandez Cordero
7fcc7e723c Changelog
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-10-21 11:43:35 +02:00
Tomoya Fujita
db4f9c21de clear handles before node destruction in test_memory_strategy. (#2969)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-10-21 08:50:48 +09:00
Ivo Ivanov
8499404709 Added static assert asserting custom types have no overloaded operator new (#2954)
Signed-off-by: Ivo Ivanov <ivo.ivanov@tha.de>
2025-10-20 09:14:36 +02:00
Skyler Medeiros
715a983c9f Store graph listener inside the context instead of the node graph (#2952)
* factor shutdown hook registration into its own method

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* run ament_uncrustify

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* consistent use of is_shutdown in graph-listener

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* this doesn't actually need to be a separate function

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* whitespace

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* fix typo

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* move graph listener into the context

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* make cpplint happy

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

* Update rclcpp/src/rclcpp/context.cpp

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>

---------

Signed-off-by: Skyler Medeiros <smedeiros@irobot.com>
2025-10-09 10:09:39 +09:00
Tomoya Fujita
cd9099d85b Reapply "Catch the exception from rate.sleep() if the context is invalid. (#2956)" (#2963) (#2964)
This reverts commit 6fbf03945f.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-10-06 17:12:43 +09:00
Tomoya Fujita
6fbf03945f Revert "Catch the exception from rate.sleep() if the context is invalid. (#2956)" (#2963)
This reverts commit 10f0186c6d.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-10-06 11:22:29 +09:00
Tomoya Fujita
10f0186c6d Catch the exception from rate.sleep() if the context is invalid. (#2956)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-10-06 11:16:59 +09:00
Tomoya Fujita
aa60fcf22a it misses the iterator second to lock the weakptr. (#2958)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-09-30 07:56:36 +09:00
Tomoya Fujita
b14af74a4c try aborting before canceling 1st on dtor of ServerGoalHandle. (#2953)
* try aborting before canceling 1st on dtor of ServerGoalHandle.

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

* address review comments from Copilot AI.

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

* add missing header file and descriptions in docsection.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-09-16 14:06:05 +09:00
Ilario A. Azzollini
38f4073474 update Time documentation (#2955)
Signed-off-by: ilarioazzollini <ilario.azzollini92@gmail.com>
2025-09-16 06:43:19 +02:00
Alejandro Hernandez Cordero
380866b338 30.1.1 2025-09-11 15:10:23 +02:00
Alejandro Hernandez Cordero
150a0e8ad9 Changelog
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-09-11 15:10:16 +02:00
Alejandro Hernández Cordero
b432df117a Removed warning (#2949)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-09-11 10:57:56 +09:00
Alberto Soragna
3644195892 add note about problems with spin_until_future_complete (#2849)
* add note indicating that spin_until_future_complete should be used only with futures generated by ROS entities

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* fix indentation and improve explanation of consequences

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

---------

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2025-08-26 14:36:02 +02:00
Alberto Soragna
bdab46d64e deprecate rclcpp::spin_some and rclcpp::spin_all (#2848)
* reorder events executor constructor arguments to have the first args match the other executors

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* deprecate rclcpp::spin_some and rclcpp::spin_all

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* use rcpputils to ignore deprecation and remove deprecated APIs from comments

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* fix rclcpp_lifecycle build failures.

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

* make linters happy

Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>

* fix test_service_introspection

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

* fix lifecycle unit-test

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

---------

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-08-26 19:26:34 +09:00
Barry Xu
82696f6e57 Improve the function extract_type_identifier (#2923)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2025-08-18 20:00:45 +02:00
Tim Clephas
e615c7c309 Allow for implicitly convertable loggers as well (#2922)
* Allow for implicitly convertable loggers as well

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>

* Test implicitly convertable logger

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>

* This can be simplified now

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>

* fixup! This can be simplified now

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>

---------

Signed-off-by: Tim Clephas <tim.clephas@nobleo.nl>
2025-08-13 10:46:44 +02:00
Peter Mitrano (AR)
37677791ca Clearer warning message, the old one lacked information and was perhaps misleading (#2927)
* change misleading warning message, making it more correct and informative

Signed-off-by: Peter Mitrano (AR) <peter.mitrano@agile-robots.com>

* Fix compile error. Needed to also build rcl from source.

Signed-off-by: Peter Mitrano (AR) <peter.mitrano@agile-robots.com>

* explicitely initialize pointer as null, to adhere to best practice

Signed-off-by: Peter Mitrano (AR) <peter.mitrano@agile-robots.com>

---------

Signed-off-by: Peter Mitrano (AR) <peter.mitrano@agile-robots.com>
2025-08-12 15:01:53 +02:00
Chris Lalancette
a919a6e5ed Cleanup the dependencies in rclcpp_components. (#2918)
The most important change in here is the changes to the package.xml
and the CMakeLists.txt, which now properly export the dependencies
to downstream packages as required.  On those two fronts:

1.  Make sure to add a dependency on rmw, which this package does depend
    on for rmw_request_id_t
2. Make sure to add a dependency on rcl_interfaces, which this package
   also depends on.
3. Export depend class_loader, composition_interfaces, rclcpp,
   rcpputils, and rmw, all of which are exported in the header files.
4. Remove the unnecessary test dependencies on launch_testing and
   std_msgs, neither of which is used.

The rest of the change here is to cleanup the header files to include
what you use everywhere.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2025-08-03 21:22:30 -04:00
Chris Lalancette
b7e4aad091 30.1.0 2025-07-29 21:03:19 +00:00
Chris Lalancette
fb6d43c5e0 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2025-07-29 21:03:12 +00:00
Michiel Leegwater
1f2adc9829 Fix: improve exception context for parameter_value_from (#2917)
Signed-off-by: Michiel Leegwater <mleegwt@users.noreply.github.com>
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2025-07-29 18:50:32 +02:00
mosfet80
6392ee5187 fix cmake deprecation (#2914)
cmake version < then 3.10 is deprecated

Signed-off-by: Mos <realeandrea@yahoo.it>
2025-07-24 08:57:47 +02:00
Patrick Roncagliolo
4fb558ae7b Fix start_type_description_service param handling (#2897)
* Fix `start_type_description_service` param handling

Signed-off-by: Patrick Roncagliolo <ronca.pat@gmail.com>

* Add test

Signed-off-by: Patrick Roncagliolo <ronca.pat@gmail.com>

* Demonstrate different exceptions depending on node options

Signed-off-by: Patrick Roncagliolo <ronca.pat@gmail.com>

* Same exact exception and `what()` message in both cases

Signed-off-by: Patrick Roncagliolo <ronca.pat@gmail.com>

* Uncrustify

Signed-off-by: Patrick Roncagliolo <ronca.pat@gmail.com>

---------

Signed-off-by: Patrick Roncagliolo <ronca.pat@gmail.com>
2025-07-16 15:45:44 -07:00
Sriharsha Ghanta
2fcef70ea7 Add qos parameter for wait_for_message function (#2903)
Signed-off-by: Sriharsha Ghanta <ghanta1996@gmail.com>
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
Co-authored-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-07-16 09:57:12 +02:00
Tomoya Fujita
84c6fb1cfc Fujitatomoya/test append parameter override (#2896)
* add parameter_overrides_with_parameter test.

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

* no need to template the function signature of append_parameter_override().

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-07-09 14:04:24 -07:00
Michael Orlov
448287b109 Expose typesupport_helpers API needed for the Rosbag2 (#2858)
* Expose extract_type_identifier and get_typesupport_library_path API

- Rationale: We need to use this API in the Rosbag2
- Reference PR https://github.com/ros2/rosbag2/pull/2017 in the Rosbag2

Signed-off-by: Michael Orlov <michael.orlov@apex.ai>

* Use C++ style in doxygen documentation

Signed-off-by: Michael Orlov <michael.orlov@apex.ai>

---------

Signed-off-by: Michael Orlov <michael.orlov@apex.ai>
2025-07-08 15:43:43 -07:00
Christophe Bedard
b6f03db3d4 Remove comment about now-removed StaticSingleThreadedExecutor (#2893)
Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>
2025-07-08 22:49:03 +02:00
Patrick Roncagliolo
fa0cf2da31 Add overload of append_parameter_override (#2891)
Signed-off-by: Patrick Roncagliolo <ronca.pat@gmail.com>
2025-07-08 17:18:57 +02:00
Janosch Machowinski
7ebc9e4cae fix: Don't deadlock if removing shutdown callbacks in a shutdown callback (#2886)
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-07-02 10:40:25 +02:00
Chris Lalancette
05eafed5b2 30.0.0 2025-07-01 15:46:34 +00:00
Chris Lalancette
eac7d1ad68 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2025-07-01 15:46:17 +00:00
Chris Lalancette
5fd85245bb Hand-code logging.hpp (#2870)
With the similar rewrite coming in rcutils logging macros,
this is now required (since the old machinery relied on the
removed code in rcutils).  Regardless, I think this is more
readable and maintainable.

Note that this does *not* remove the python3-empy dependency,
since that is still needed for other things.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2025-07-01 09:59:27 -04:00
Alejandro Hernández Cordero
e677f4cf39 Adressed TODO in node_graph (#2877)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-06-30 13:20:15 +02:00
Mihir Rao
5122c312d4 NEW PR: Add component_container for EventsExecutor (#2885)
* Add component_container for EventsExecutor

Signed-off-by: Mihir Rao <mihirr@nvidia.com>

* Fix build error

Signed-off-by: Mihir Rao <mihirr@nvidia.com>

---------

Signed-off-by: Mihir Rao <mihirr@nvidia.com>
2025-06-28 14:48:20 -07:00
Tomoya Fujita
e6577c6792 fix test_publisher_with_system_default_qos. (#2881)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-06-26 14:00:01 +02:00
Tomoya Fujita
3cc2a0eacf make sure that plugin arg includes the double colon. (#2878)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-06-25 22:05:25 +02:00
Alejandro Hernandez Cordero
5beec9e32b 29.6.1 2025-06-23 16:00:13 +02:00
Alejandro Hernandez Cordero
6ee3a55fc3 Changelog
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-06-23 16:00:09 +02:00
Adam Aposhian
1e95407bdf set thread names by node in component container isolated (#2871)
Signed-off-by: Adam Aposhian <adam.aposhian@fireflyautomatix.com>
2025-06-18 22:57:00 +02:00
keeponoiro
db8d917a12 Replace std::default_random_engine with std::mt19937 (rolling) (#2843)
Signed-off-by: keeponoiro <keeeeeeep@gmail.com>
2025-05-29 15:29:57 -07:00
Michael Orlov
8d44b95d8b Fix for memory leaks in rclcpp::SerializedMessage (#2861)
Signed-off-by: Michael Orlov <morlovmr@gmail.com>
Signed-off-by: Michael Orlov <michael.orlov@apex.ai>
Co-authored-by: kylemarcey <marcey.kyle@gmail.com>
2025-05-29 10:49:17 +02:00
Alejandro Hernández Cordero
df3a303a17 Removed warning test_qos (#2859)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-05-26 22:05:08 +02:00
Alejandro Hernández Cordero
373a63c5e6 Added missing chrono includes (#2854)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-05-23 00:22:27 +02:00
Alejandro Hernández Cordero
6c478834fb get_all_data_impl() does not handle null pointers properly, causing segmentation fault (#2840)
* get_all_data_impl() does not handle null pointers properly, causing segmentation fault

Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>

* Update rclcpp/include/rclcpp/experimental/buffers/ring_buffer_implementation.hpp

Co-authored-by: Janosch Machowinski <jmachowinski@users.noreply.github.com>
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>

* make linters happy

Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>

---------

Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
Co-authored-by: Janosch Machowinski <jmachowinski@users.noreply.github.com>
2025-05-17 15:49:25 +02:00
Alejandro Hernández Cordero
73e9bfb62b QoSInitialization::from_rmw does not validate invalid history policy values, leading to silent failures (#2841)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-05-16 18:56:22 +02:00
Tomoya Fujita
1aaade8976 remove get_notify_guard_condition from NodeBaseInterface. (#2839)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-05-14 09:37:58 -07:00
Alejandro Hernández Cordero
88d8a9e75a Removed deprecated StaticSingleThreadedExecutor (#2835)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-05-12 10:04:32 +02:00
135 changed files with 4120 additions and 1246 deletions

View File

@@ -2,6 +2,81 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30.1.4 (2025-12-23)
-------------------
* Updated deprecated ament_index_cpp API (`#3011 <https://github.com/ros2/rclcpp/issues/3011>`_)
* Unified Node Interfaces: Add const version of get_node_x_interface() (`#3006 <https://github.com/ros2/rclcpp/issues/3006>`_)
* Parameter Descriptor Simplification (`#2179 <https://github.com/ros2/rclcpp/issues/2179>`_)
* ParameterEventHandler support ContentFiltering (`#2971 <https://github.com/ros2/rclcpp/issues/2971>`_)
* update policy_name_from_kind && test_qos (`#2156 <https://github.com/ros2/rclcpp/issues/2156>`_)
* Add ability to disable and enable subscription's callbacks (`#2985 <https://github.com/ros2/rclcpp/issues/2985>`_)
* Switch to isolated testing via rmw_test_fixture (`#2929 <https://github.com/ros2/rclcpp/issues/2929>`_)
* remove I/O from signal handler. (`#2986 <https://github.com/ros2/rclcpp/issues/2986>`_)
* Contributors: Alejandro Hernández Cordero, Andrianov Roman, Barry Xu, Lucas Wendland, Michael Orlov, Tomoya Fujita, fabianhirmann, yadunund
30.1.3 (2025-11-18)
-------------------
* correct test function descriptions (`#2970 <https://github.com/ros2/rclcpp/issues/2970>`_)
* add : get clients, servers info (`#2569 <https://github.com/ros2/rclcpp/issues/2569>`_)
* Fix REP url locations (`#2987 <https://github.com/ros2/rclcpp/issues/2987>`_)
* Contributors: Minju, Lee, Tim Clephas, Yuchen966
30.1.2 (2025-10-21)
-------------------
* clear handles before node destruction in test_memory_strategy. (`#2969 <https://github.com/ros2/rclcpp/issues/2969>`_)
* Added static assert asserting custom types have no overloaded operator new (`#2954 <https://github.com/ros2/rclcpp/issues/2954>`_)
* Store graph listener inside the context instead of the node graph (`#2952 <https://github.com/ros2/rclcpp/issues/2952>`_)
* Reapply "Catch the exception from rate.sleep() if the context is invalid. (`#2956 <https://github.com/ros2/rclcpp/issues/2956>`_)" (`#2963 <https://github.com/ros2/rclcpp/issues/2963>`_) (`#2964 <https://github.com/ros2/rclcpp/issues/2964>`_)
* Revert "Catch the exception from rate.sleep() if the context is invalid. (`#2956 <https://github.com/ros2/rclcpp/issues/2956>`_)" (`#2963 <https://github.com/ros2/rclcpp/issues/2963>`_)
* Catch the exception from rate.sleep() if the context is invalid. (`#2956 <https://github.com/ros2/rclcpp/issues/2956>`_)
* update Time documentation (`#2955 <https://github.com/ros2/rclcpp/issues/2955>`_)
* Contributors: Ilario A. Azzollini, Ivo Ivanov, Skyler Medeiros, Tomoya Fujita
30.1.1 (2025-09-11)
-------------------
* Removed warning (`#2949 <https://github.com/ros2/rclcpp/issues/2949>`_)
* add note about problems with spin_until_future_complete (`#2849 <https://github.com/ros2/rclcpp/issues/2849>`_)
* deprecate rclcpp::spin_some and rclcpp::spin_all (`#2848 <https://github.com/ros2/rclcpp/issues/2848>`_)
* Improve the function extract_type_identifier (`#2923 <https://github.com/ros2/rclcpp/issues/2923>`_)
* Allow for implicitly convertable loggers as well (`#2922 <https://github.com/ros2/rclcpp/issues/2922>`_)
* Contributors: Alberto Soragna, Alejandro Hernández Cordero, Barry Xu, Tim Clephas
30.1.0 (2025-07-29)
-------------------
* Fix: improve exception context for parameter_value_from (`#2917 <https://github.com/ros2/rclcpp/issues/2917>`_)
* Fix `start_type_description_service` param handling (`#2897 <https://github.com/ros2/rclcpp/issues/2897>`_)
* Add qos parameter for wait_for_message function (`#2903 <https://github.com/ros2/rclcpp/issues/2903>`_)
* Fujitatomoya/test append parameter override (`#2896 <https://github.com/ros2/rclcpp/issues/2896>`_)
* Expose `typesupport_helpers` API needed for the Rosbag2 (`#2858 <https://github.com/ros2/rclcpp/issues/2858>`_)
* Remove comment about now-removed StaticSingleThreadedExecutor (`#2893 <https://github.com/ros2/rclcpp/issues/2893>`_)
* Add overload of `append_parameter_override` (`#2891 <https://github.com/ros2/rclcpp/issues/2891>`_)
* fix: Don't deadlock if removing shutdown callbacks in a shutdown callback (`#2886 <https://github.com/ros2/rclcpp/issues/2886>`_)
* Contributors: Christophe Bedard, Janosch Machowinski, Michael Orlov, Michiel Leegwater, Patrick Roncagliolo, Sriharsha Ghanta, Tomoya Fujita
30.0.0 (2025-07-01)
-------------------
* Hand-code logging.hpp (`#2870 <https://github.com/ros2/rclcpp/issues/2870>`_)
* Adressed TODO in node_graph (`#2877 <https://github.com/ros2/rclcpp/issues/2877>`_)
* fix test_publisher_with_system_default_qos. (`#2881 <https://github.com/ros2/rclcpp/issues/2881>`_)
* Contributors: Alejandro Hernández Cordero, Chris Lalancette, Tomoya Fujita
29.6.1 (2025-06-23)
-------------------
* Fix for memory leaks in rclcpp::SerializedMessage (`#2861 <https://github.com/ros2/rclcpp/issues/2861>`_)
* Removed warning test_qos (`#2859 <https://github.com/ros2/rclcpp/issues/2859>`_)
* Added missing chrono includes (`#2854 <https://github.com/ros2/rclcpp/issues/2854>`_)
* get_all_data_impl() does not handle null pointers properly, causing segmentation fault (`#2840 <https://github.com/ros2/rclcpp/issues/2840>`_)
* QoSInitialization::from_rmw does not validate invalid history policy values, leading to silent failures (`#2841 <https://github.com/ros2/rclcpp/issues/2841>`_)
* remove get_notify_guard_condition from NodeBaseInterface. (`#2839 <https://github.com/ros2/rclcpp/issues/2839>`_)
* Removed deprecated StaticSingleThreadedExecutor (`#2835 <https://github.com/ros2/rclcpp/issues/2835>`_)
* Removed deprecated rcpputils Path (`#2834 <https://github.com/ros2/rclcpp/issues/2834>`_)
* Add range constraints for applicable array parameters (`#2828 <https://github.com/ros2/rclcpp/issues/2828>`_)
* Update RingBufferImplementation to clear internal data. (`#2837 <https://github.com/ros2/rclcpp/issues/2837>`_)
* Removed deprecated cancel_sleep_or_wait (`#2836 <https://github.com/ros2/rclcpp/issues/2836>`_)
* Add missing 's' to 'NodeParametersInterface' in doc/comment (`#2831 <https://github.com/ros2/rclcpp/issues/2831>`_)
* subordinate node consistent behavior and update docstring. (`#2822 <https://github.com/ros2/rclcpp/issues/2822>`_)
* Contributors: Alejandro Hernández Cordero, Alex Youngs, Christophe Bedard, Michael Carlstrom, Michael Orlov, Tomoya Fujita
29.6.0 (2025-04-25)
-------------------
* throws std::invalid_argument if ParameterEvent is NULL. (`#2814 <https://github.com/ros2/rclcpp/issues/2814>`_)

View File

@@ -70,7 +70,6 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/executors/executor_notify_waitable.cpp
src/rclcpp/executors/multi_threaded_executor.cpp
src/rclcpp/executors/single_threaded_executor.cpp
src/rclcpp/executors/static_single_threaded_executor.cpp
src/rclcpp/expand_topic_or_service_name.cpp
src/rclcpp/experimental/executors/events_executor/events_executor.cpp
src/rclcpp/experimental/timers_manager.cpp
@@ -104,6 +103,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/node_options.cpp
src/rclcpp/parameter.cpp
src/rclcpp/parameter_client.cpp
src/rclcpp/parameter_descriptor_wrapper.cpp
src/rclcpp/parameter_event_handler.cpp
src/rclcpp/parameter_events_filter.cpp
src/rclcpp/parameter_map.cpp
@@ -147,27 +147,6 @@ set(Python3_FIND_UNVERSIONED_NAMES FIRST)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
# "watch" template for changes
configure_file(
"resource/logging.hpp.em"
"logging.hpp.em.watch"
COPYONLY
)
# generate header with logging macros
set(python_code_logging
"import em"
"em.invoke(['-o', 'include/rclcpp/logging.hpp', '${CMAKE_CURRENT_SOURCE_DIR}/resource/logging.hpp.em'])")
string(REPLACE ";" "$<SEMICOLON>" python_code_logging "${python_code_logging}")
add_custom_command(OUTPUT include/rclcpp/logging.hpp
COMMAND ${CMAKE_COMMAND} -E make_directory "include/rclcpp"
COMMAND Python3::Interpreter ARGS -c "${python_code_logging}"
DEPENDS "${CMAKE_CURRENT_BINARY_DIR}/logging.hpp.em.watch"
COMMENT "Expanding logging.hpp.em"
VERBATIM
)
list(APPEND ${PROJECT_NAME}_SRCS
include/rclcpp/logging.hpp)
file(GLOB interface_files "include/rclcpp/node_interfaces/node_*_interface.hpp")
foreach(interface_file ${interface_files})
get_filename_component(interface_name ${interface_file} NAME_WE)

View File

@@ -21,6 +21,22 @@ GENERATE_LATEX = NO
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
EXCLUDE_SYMBOLS += RCLCPP_STATIC_ASSERT_LOGGER
EXCLUDE_SYMBOLS += RCLCPP_LOG
EXCLUDE_SYMBOLS += RCLCPP_LOG_ONCE
EXCLUDE_SYMBOLS += RCLCPP_LOG_EXPRESSION
EXCLUDE_SYMBOLS += RCLCPP_LOG_FUNCTION
EXCLUDE_SYMBOLS += RCLCPP_LOG_SKIPFIRST
EXCLUDE_SYMBOLS += RCLCPP_LOG_TIME_POINT_FUNC
EXCLUDE_SYMBOLS += RCLCPP_LOG_THROTTLE
EXCLUDE_SYMBOLS += RCLCPP_LOG_SKIPFIRST_THROTTLE
EXCLUDE_SYMBOLS += RCLCPP_LOG_STREAM
EXCLUDE_SYMBOLS += RCLCPP_LOG_STREAM_ONCE
EXCLUDE_SYMBOLS += RCLCPP_LOG_STREAM_EXPRESSION
EXCLUDE_SYMBOLS += RCLCPP_LOG_STREAM_FUNCTION
EXCLUDE_SYMBOLS += RCLCPP_LOG_STREAM_SKIPFIRST
EXCLUDE_SYMBOLS += RCLCPP_LOG_STREAM_THROTTLE
EXCLUDE_SYMBOLS += RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE
PREDEFINED += DOXYGEN_ONLY
PREDEFINED += RCLCPP_LOCAL=
PREDEFINED += RCLCPP_PUBLIC=

View File

@@ -1,10 +1,10 @@
This document is a declaration of software quality for the `rclcpp` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
This document is a declaration of software quality for the `rclcpp` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/).
# rclcpp Quality Declaration
The package `rclcpp` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware.
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html).
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://reps.openrobotics.org/rep-2004/).
## Version Policy [1]
@@ -55,7 +55,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d
### Continuous Integration [2.iv]
All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers)
All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers)
Currently nightly results can be seen here:
@@ -213,7 +213,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
## Platform Support [6]
`rclcpp` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them.
`rclcpp` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them.
Currently nightly build status can be seen here:
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp/)
@@ -225,4 +225,4 @@ Currently nightly build status can be seen here:
### Vulnerability Disclosure Policy [7.i]
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html).
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/).

View File

@@ -15,8 +15,10 @@
#ifndef RCLCPP__ANY_SUBSCRIPTION_CALLBACK_HPP_
#define RCLCPP__ANY_SUBSCRIPTION_CALLBACK_HPP_
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <type_traits>
#include <utility>
@@ -375,7 +377,19 @@ public:
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
}
AnySubscriptionCallback(const AnySubscriptionCallback &) = default;
AnySubscriptionCallback(const AnySubscriptionCallback & other)
: callback_variant_(other.callback_variant_),
callback_disabled_(other.callback_disabled_.load()),
subscribed_type_allocator_(other.subscribed_type_allocator_),
subscribed_type_deleter_(other.subscribed_type_deleter_),
ros_message_type_allocator_(other.ros_message_type_allocator_),
ros_message_type_deleter_(other.ros_message_type_deleter_),
serialized_message_allocator_(other.serialized_message_allocator_),
serialized_message_deleter_(other.serialized_message_deleter_)
{
allocator::set_allocator_for_deleter(&subscribed_type_deleter_, &subscribed_type_allocator_);
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
}
/// Generic function for setting the callback.
/**
@@ -399,6 +413,20 @@ public:
return *this;
}
/// Disable the callback from being called during dispatch.
void disable()
{
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
callback_disabled_.store(true);
}
/// Enable the callback to be called during dispatch.
void enable()
{
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
callback_disabled_.store(false);
}
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
create_ros_unique_ptr_from_ros_shared_ptr_message(
const std::shared_ptr<const ROSMessageType> & message)
@@ -469,6 +497,10 @@ public:
std::shared_ptr<ROSMessageType> message,
const rclcpp::MessageInfo & message_info)
{
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
if (callback_disabled_.load()) {
return;
}
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
// Check if the variant is "unset", throw if it is.
if (callback_variant_.index() == 0) {
@@ -569,6 +601,10 @@ public:
std::shared_ptr<const rclcpp::SerializedMessage> serialized_message,
const rclcpp::MessageInfo & message_info)
{
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
if (callback_disabled_.load()) {
return;
}
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
// Check if the variant is "unset", throw if it is.
if (callback_variant_.index() == 0) {
@@ -648,6 +684,10 @@ public:
std::shared_ptr<const SubscribedType> message,
const rclcpp::MessageInfo & message_info)
{
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
if (callback_disabled_.load()) {
return;
}
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), true);
// Check if the variant is "unset", throw if it is.
if (callback_variant_.index() == 0) {
@@ -778,6 +818,10 @@ public:
std::unique_ptr<SubscribedType, SubscribedTypeDeleter> message,
const rclcpp::MessageInfo & message_info)
{
std::unique_lock<std::recursive_mutex> callback_lock(callback_mutex_);
if (callback_disabled_.load()) {
return;
}
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), true);
// Check if the variant is "unset", throw if it is.
if (callback_variant_.index() == 0) {
@@ -972,6 +1016,8 @@ private:
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2162r0.html
// For now, compose the variant into this class as a private attribute.
typename HelperT::variant_type callback_variant_;
std::recursive_mutex callback_mutex_;
std::atomic_bool callback_disabled_{false};
SubscribedTypeAllocator subscribed_type_allocator_;
SubscribedTypeDeleter subscribed_type_deleter_;

View File

@@ -37,6 +37,10 @@
namespace rclcpp
{
namespace graph_listener
{
class GraphListener;
} // namespace graph_listener
/// Thrown when init is called on an already initialized context.
class ContextAlreadyInitialized : public std::runtime_error
@@ -309,6 +313,10 @@ public:
std::shared_ptr<rcl_context_t>
get_rcl_context();
RCLCPP_PUBLIC
std::shared_ptr<rclcpp::graph_listener::GraphListener>
get_graph_listener();
/// Sleep for a given period of time or until shutdown() is called.
/**
* This function can be interrupted early if:
@@ -381,16 +389,19 @@ private:
std::recursive_mutex sub_contexts_mutex_;
std::vector<std::shared_ptr<OnShutdownCallback>> on_shutdown_callbacks_;
mutable std::mutex on_shutdown_callbacks_mutex_;
mutable std::recursive_mutex on_shutdown_callbacks_mutex_;
std::vector<std::shared_ptr<PreShutdownCallback>> pre_shutdown_callbacks_;
mutable std::mutex pre_shutdown_callbacks_mutex_;
mutable std::recursive_mutex pre_shutdown_callbacks_mutex_;
/// Condition variable for timed sleep (see sleep_for).
std::condition_variable interrupt_condition_variable_;
/// Mutex for protecting the global condition variable.
std::mutex interrupt_mutex_;
/// Graph Listener which waits on graph changes for the node and is shared across nodes.
std::shared_ptr<rclcpp::graph_listener::GraphListener> graph_listener_;
/// Keep shared ownership of global vector of weak contexts
std::shared_ptr<WeakContextsWrapper> weak_contexts_;

View File

@@ -15,6 +15,7 @@
#ifndef RCLCPP__EVENT_HANDLER_HPP_
#define RCLCPP__EVENT_HANDLER_HPP_
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
@@ -110,6 +111,14 @@ public:
RCLCPP_PUBLIC
virtual ~EventHandlerBase();
RCLCPP_PUBLIC
virtual
void enable() = 0;
RCLCPP_PUBLIC
virtual
void disable() = 0;
/// Get the number of ready events
RCLCPP_PUBLIC
size_t
@@ -192,7 +201,7 @@ public:
}
};
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
std::lock_guard<std::recursive_mutex> lock(on_new_event_callback_mutex_);
// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
@@ -215,7 +224,7 @@ public:
void
clear_on_ready_callback() override
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
std::lock_guard<std::recursive_mutex> lock(on_new_event_callback_mutex_);
if (on_new_event_callback_) {
set_on_new_event_callback(nullptr, nullptr);
on_new_event_callback_ = nullptr;
@@ -234,7 +243,7 @@ protected:
void
set_on_new_event_callback(rcl_event_callback_t callback, const void * user_data);
std::recursive_mutex callback_mutex_;
std::recursive_mutex on_new_event_callback_mutex_;
std::function<void(size_t)> on_new_event_callback_{nullptr};
rcl_event_t event_handle_;
@@ -302,6 +311,10 @@ public:
void
execute(const std::shared_ptr<void> & data) override
{
std::unique_lock<std::mutex> event_callback_lock(event_callback_mutex_);
if (disabled_.load()) {
return;
}
if (!data) {
throw std::runtime_error("'data' is empty");
}
@@ -310,12 +323,53 @@ public:
callback_ptr.reset();
}
/// Disable the event callback from being called when execute(..) invoked
/**
* This will also temporarily remove the on_new_event_callback from the underlying rmw layer,
* so that it is not called from the middleware while disabled.
*/
void disable() override
{
{
// Temporary remove the on_new_event_callback_ to prevent it from being called
std::lock_guard<std::recursive_mutex> on_new_event_lock(on_new_event_callback_mutex_);
if (on_new_event_callback_) {
set_on_new_event_callback(nullptr, nullptr);
}
}
std::lock_guard<std::mutex> event_callback_lock(event_callback_mutex_);
disabled_.store(true);
}
/// Enable the event callback to be called when execute(..) invoked
/**
* This will also set back the on_new_event_callback to the underlying rmw layer, if it was
* previously removed with disable().
*/
void enable() override
{
{
// Set callback again if it was previously removed in disable()
std::lock_guard<std::recursive_mutex> on_new_event_lock(on_new_event_callback_mutex_);
if (on_new_event_callback_) {
set_on_new_event_callback(
rclcpp::detail::cpp_callback_trampoline<
decltype(on_new_event_callback_), const void *, size_t>,
static_cast<const void *>(&on_new_event_callback_));
}
}
std::lock_guard<std::mutex> event_callback_lock(event_callback_mutex_);
disabled_.store(false);
}
private:
using EventCallbackInfoT = typename std::remove_reference<typename
rclcpp::function_traits::function_traits<EventCallbackT>::template argument_type<0>>::type;
ParentHandleT parent_handle_;
EventCallbackT event_callback_;
std::mutex event_callback_mutex_;
std::atomic_bool disabled_{false};
};
} // namespace rclcpp

View File

@@ -371,6 +371,9 @@ public:
* If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
* code.
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
* \note This method will check the future and the timeout only when the executor is woken up.
* If this future is unrelated to an executor's entity, this method will not correctly detect
* when it's completed and therefore may wait forever and never time out.
*/
template<typename FutureT, typename TimeRepT = int64_t, typename TimeT = std::milli>
FutureReturnCode

View File

@@ -20,7 +20,6 @@
#include "rclcpp/executors/multi_threaded_executor.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/executors/static_single_threaded_executor.hpp"
#include "rclcpp/experimental/executors/events_executor/events_executor.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/utilities.hpp"
@@ -29,24 +28,82 @@
namespace rclcpp
{
/// Create a default single-threaded executor and execute all available work exhaustively.
/** \param[in] node_ptr Shared pointer to the node to spin. */
/**
* @brief Create a default single-threaded executor and execute all available work exhaustively.
* @param node_ptr Shared pointer to the base interface of the node to spin.
* @param max_duration max duration to spin
*
* This method is deprecated because it can lead to very bad performance if used in a loop:
* each call will create a new executor and register the node, which is an expensive operation.
* It's recommended to always manually instantiate an executor and call the methods with
* the same name on it.
* For example:
* SingleThreadedExecutor executor;
* executor.add_node(node_ptr);
* executor.spin_all(max_duration);
* If you are using a non-default context, this should be passed to the executor's constructor.
*/
[[deprecated("use SingleThreadedExecutor::spin_all instead")]]
RCLCPP_PUBLIC
void
spin_all(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
std::chrono::nanoseconds max_duration);
/**
* @brief Create a default single-threaded executor and execute all available work exhaustively.
* @param node_ptr Shared pointer to the node to spin.
* @param max_duration max duration to spin
*
* This method is deprecated because it can lead to very bad performance if used in a loop:
* each call will create a new executor and register the node, which is an expensive operation.
* It's recommended to always manually instantiate an executor and call the methods with
* the same name on it.
* For example:
* SingleThreadedExecutor executor;
* executor.add_node(node_ptr);
* executor.spin_all(max_duration);
* If you are using a non-default context, this should be passed to the executor's constructor.
*/
[[deprecated("use SingleThreadedExecutor::spin_all instead")]]
RCLCPP_PUBLIC
void
spin_all(rclcpp::Node::SharedPtr node_ptr, std::chrono::nanoseconds max_duration);
/// Create a default single-threaded executor and execute any immediately available work.
/** \param[in] node_ptr Shared pointer to the node to spin. */
/**
* @brief Create a default single-threaded executor and execute any immediately available work.
* @param node_ptr Shared pointer to the base interface of the node to spin.
*
* This method is deprecated because it can lead to very bad performance if used in a loop:
* each call will create a new executor and register the node, which is an expensive operation.
* It's recommended to always manually instantiate an executor and call the methods with
* the same name on it.
* For example:
* SingleThreadedExecutor executor;
* executor.add_node(node_ptr);
* executor.spin_some();
* If you are using a non-default context, this should be passed to the executor's constructor.
*/
[[deprecated("use SingleThreadedExecutor::spin_some instead")]]
RCLCPP_PUBLIC
void
spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
/**
* @brief Create a default single-threaded executor and execute any immediately available work.
* @param node_ptr Shared pointer to the node to spin.
*
* This method is deprecated because it can lead to very bad performance if used in a loop:
* each call will create a new executor and register the node, which is an expensive operation.
* It's recommended to always manually instantiate an executor and call the methods with
* the same name on it.
* For example:
* SingleThreadedExecutor executor;
* executor.add_node(node_ptr);
* executor.spin_some();
* If you are using a non-default context, this should be passed to the executor's constructor.
*/
[[deprecated("use SingleThreadedExecutor::spin_some instead")]]
RCLCPP_PUBLIC
void
spin_some(rclcpp::Node::SharedPtr node_ptr);

View File

@@ -1,145 +0,0 @@
// Copyright 2019 Nobleo Technology
//
// 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__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
#define RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
#include <atomic>
#include <chrono>
#include <memory>
#include "rclcpp/executor.hpp"
#include "rclcpp/executors/executor_entities_collection.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
namespace rclcpp
{
namespace executors
{
/// Static executor implementation
/**
* This executor is a static version of the original single threaded executor.
* 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;
* by
* rclcpp::executors::StaticSingleThreadedExecutor exec;
* in your source code and spin node(s) in the following way:
* exec.add_node(node);
* exec.spin();
* exec.remove_node(node);
*/
class [[deprecated("Use rclcpp::executors::SingleThreadedExecutor")]] StaticSingleThreadedExecutor
: public rclcpp::Executor
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(StaticSingleThreadedExecutor)
/// Default constructor. See the default constructor for Executor.
RCLCPP_PUBLIC
explicit StaticSingleThreadedExecutor(
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
/// Default destructor.
RCLCPP_PUBLIC
virtual ~StaticSingleThreadedExecutor();
/// Static executor implementation of spin.
/**
* This function will block until work comes in, execute it, and keep blocking.
* It will only be interrupted by a CTRL-C (managed by the global signal handler).
* \throws std::runtime_error when spin() called while already spinning
*/
RCLCPP_PUBLIC
void
spin() override;
/// Static executor implementation of spin some
/**
* This non-blocking function will execute entities that
* were ready when this API was called, until timeout or no
* more work available. Entities that got ready while
* executing work, won't be taken into account here.
*
* Example:
* while(condition) {
* spin_some();
* sleep(); // User should have some sync work or
* // sleep to avoid a 100% CPU usage
* }
*/
RCLCPP_PUBLIC
void
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0)) override;
/// Static executor implementation of spin all
/**
* This non-blocking function will execute entities until timeout (must be >= 0)
* or no more work available.
* If timeout is `0`, potentially it blocks forever until no more work is available.
* If new entities get ready while executing work available, they will be executed
* as long as the timeout hasn't expired.
*
* Example:
* while(condition) {
* spin_all();
* sleep(); // User should have some sync work or
* // sleep to avoid a 100% CPU usage
* }
*/
RCLCPP_PUBLIC
void
spin_all(std::chrono::nanoseconds max_duration) override;
protected:
/**
* @brief Executes ready executables from wait set.
* @param collection entities to evaluate for ready executables.
* @param wait_result result to check for ready executables.
* @param spin_once if true executes only the first ready executable.
* @return true if any executable was ready.
*/
bool
execute_ready_executables(
const rclcpp::executors::ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
bool spin_once);
void
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
void
spin_once_impl(std::chrono::nanoseconds timeout) override;
std::optional<rclcpp::WaitResult<rclcpp::WaitSet>>
collect_and_wait(std::chrono::nanoseconds timeout);
private:
RCLCPP_DISABLE_COPY(StaticSingleThreadedExecutor)
};
} // namespace executors
} // namespace rclcpp
#endif // RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_

View File

@@ -265,9 +265,13 @@ private:
std::vector<BufferT> result_vtr;
result_vtr.reserve(size_);
for (size_t id = 0; id < size_; ++id) {
result_vtr.emplace_back(
new typename is_std_unique_ptr<T>::Ptr_type(
*(ring_buffer_[(read_index_ + id) % capacity_])));
const auto & elem(ring_buffer_[(read_index_ + id) % capacity_]);
if (elem != nullptr) {
result_vtr.emplace_back(new typename is_std_unique_ptr<T>::Ptr_type(
*elem));
} else {
result_vtr.emplace_back(nullptr);
}
}
return result_vtr;
}

View File

@@ -64,17 +64,17 @@ public:
/// Default constructor. See the default constructor for Executor.
/**
* \param[in] options Options used to configure the executor.
* \param[in] events_queue The queue used to store events.
* \param[in] execute_timers_separate_thread If true, timers are executed in a separate
* thread. If false, timers are executed in the same thread as all other entities.
* \param[in] options Options used to configure the executor.
*/
RCLCPP_PUBLIC
EventsExecutor(
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions(),
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue = std::make_unique<
rclcpp::experimental::executors::SimpleEventsQueue>(),
bool execute_timers_separate_thread = false,
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
bool execute_timers_separate_thread = false);
/// Default destructor.
RCLCPP_PUBLIC

View File

@@ -154,6 +154,29 @@ public:
execute_impl<SubscribedType>(data);
}
/// Disable callbacks from being called
/**
* This method will block, until any subscription's callbacks currently being executed are
* finished.
* This method is thread safe, and provides a safe way to atomically disable the callbacks.
*/
void disable_callbacks() override
{
SubscriptionIntraProcessBase::disable_callbacks();
any_callback_.disable();
}
/// Enable the callbacks to be called
/**
* This method is thread safe, and provides a safe way to atomically enable the callbacks
* in a multithreaded environment.
*/
void enable_callbacks() override
{
SubscriptionIntraProcessBase::enable_callbacks();
any_callback_.enable();
}
protected:
template<typename T>
typename std::enable_if<std::is_same<T, rcl_serialized_message_t>::value, void>::type

View File

@@ -87,6 +87,22 @@ public:
void
execute(const std::shared_ptr<void> & data) override = 0;
/// Disable callbacks from being called
/**
* This function temporary disable on_new_message_callback to prevent it from being called.
*/
RCLCPP_PUBLIC
virtual
void disable_callbacks();
/// Enable the callbacks to be called
/**
* This function enable the on_new_message_callback if it was previously set.
*/
RCLCPP_PUBLIC
virtual
void enable_callbacks();
virtual
bool
use_take_shared_method() const = 0;
@@ -158,7 +174,7 @@ public:
}
};
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
on_new_message_callback_ = new_callback;
if (unread_count_ > 0) {
@@ -176,7 +192,7 @@ public:
void
clear_on_ready_callback() override
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
on_new_message_callback_ = nullptr;
}
@@ -188,8 +204,9 @@ public:
}
protected:
std::recursive_mutex callback_mutex_;
std::recursive_mutex on_new_message_callback_mutex_;
std::function<void(size_t)> on_new_message_callback_ {nullptr};
bool on_new_message_callback_disabled_{false};
size_t unread_count_{0};
rclcpp::GuardCondition gc_;
@@ -199,11 +216,13 @@ protected:
void
invoke_on_new_message()
{
std::lock_guard<std::recursive_mutex> lock(this->callback_mutex_);
if (this->on_new_message_callback_) {
this->on_new_message_callback_(1);
} else {
this->unread_count_++;
std::lock_guard<std::recursive_mutex> lock(this->on_new_message_callback_mutex_);
if (!on_new_message_callback_disabled_) {
if (this->on_new_message_callback_) {
this->on_new_message_callback_(1);
} else {
this->unread_count_++;
}
}
}

View File

@@ -111,6 +111,26 @@ public:
RCLCPP_PUBLIC
std::shared_ptr<rclcpp::SerializedMessage> create_serialized_message() override;
/// Disable callbacks from being called
/**
* This method will block, until any subscription's callbacks provided during construction
* currently being executed are finished.
* \note This method also temporary removes the on new message callback and all
* on new event callbacks from the rmw layer to prevent them from being called. However, this
* method will not block and wait until the currently executing on_new_[message]event callbacks
* are finished.
*/
RCLCPP_PUBLIC
void disable_callbacks() override;
/// Enable the callbacks to be called
/**
* This method is thread safe, and provides a safe way to atomically enable the callbacks
* in a multithreaded environment.
*/
RCLCPP_PUBLIC
void enable_callbacks() override;
/// Cast the message to a rclcpp::SerializedMessage and call the callback.
RCLCPP_PUBLIC
void handle_message(

View File

@@ -154,6 +154,12 @@ public:
bool
is_shutdown();
/// Return true if the graph listener was started.
RCLCPP_PUBLIC
virtual
bool
is_started();
protected:
/// Main function for the listening thread.
RCLCPP_PUBLIC
@@ -181,7 +187,6 @@ private:
void
__shutdown();
std::weak_ptr<rclcpp::Context> weak_parent_context_;
std::shared_ptr<rcl_context_t> rcl_parent_context_;
std::thread listener_thread_;

View File

@@ -0,0 +1,983 @@
// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__LOGGING_HPP_
#define RCLCPP__LOGGING_HPP_
#include <sstream>
#include <type_traits>
#include "rclcpp/logger.hpp"
#include "rcutils/logging_macros.h"
// These are used for compiling out logging macros lower than a minimum severity.
#define RCLCPP_LOG_MIN_SEVERITY_DEBUG 0
#define RCLCPP_LOG_MIN_SEVERITY_INFO 1
#define RCLCPP_LOG_MIN_SEVERITY_WARN 2
#define RCLCPP_LOG_MIN_SEVERITY_ERROR 3
#define RCLCPP_LOG_MIN_SEVERITY_FATAL 4
#define RCLCPP_LOG_MIN_SEVERITY_NONE 5
#define RCLCPP_STATIC_ASSERT_LOGGER(logger) \
do { \
static_assert( \
::std::is_convertible_v<decltype(logger), ::rclcpp::Logger>, \
"First argument to logging macros must be an rclcpp::Logger"); \
} while (0)
/**
* \def RCLCPP_LOG
* Log a message with given severity.
* \param logger The `rclcpp::Logger` to use
* \param ... The format string, followed by the variable arguments for the format string.
*/
#define RCLCPP_LOG(severity, logger, ...) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
RCUTILS_LOG_NAMED(severity, (logger).get_name(), __VA_ARGS__); \
} while (0)
/**
* \def RCLCPP_LOG_ONCE
* Log a message with given severity with the following condition:
* - All log calls except the first one are ignored.
*
* \param logger The `rclcpp::Logger` to use
* \param ... The format string, followed by the variable arguments for the format string.
*/
#define RCLCPP_LOG_ONCE(severity, logger, ...) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
RCUTILS_LOG_ONCE_NAMED(severity, (logger).get_name(), __VA_ARGS__); \
} while (0)
/**
* \def RCLCPP_LOG_EXPRESSION
* Log a message with given severity with the following condition:
* - Log calls are ignored when the expression evaluates to false.
*
* \param logger The `rclcpp::Logger` to use
* \param expression The expression determining if the message should be logged
* \param ... The format string, followed by the variable arguments for the format string.
*/
#define RCLCPP_LOG_EXPRESSION(severity, logger, expression, ...) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
RCUTILS_LOG_EXPRESSION_NAMED(severity, expression, (logger).get_name(), __VA_ARGS__); \
} while (0)
/**
* \def RCLCPP_LOG_FUNCTION
* Log a message with given severity with the following condition:
* - Log calls are ignored when the function returns false.
*
* \param logger The `rclcpp::Logger` to use
* \param function The functions return value determines if the message should be logged
* \param ... The format string, followed by the variable arguments for the format string.
*/
#define RCLCPP_LOG_FUNCTION(severity, logger, function, ...) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
RCUTILS_LOG_FUNCTION_NAMED(severity, function, (logger).get_name(), __VA_ARGS__); \
} while (0)
/**
* \def RCLCPP_LOG_SKIPFIRST
* Log a message with given severity with the following condition:
* - The first log call is ignored but all subsequent calls are processed.
*
* \param logger The `rclcpp::Logger` to use
* \param ... The format string, followed by the variable arguments for the format string.
*/
#define RCLCPP_LOG_SKIPFIRST(severity, logger, ...) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
RCUTILS_LOG_SKIPFIRST_NAMED(severity, (logger).get_name(), __VA_ARGS__); \
} while (0)
#define RCLCPP_LOG_TIME_POINT_FUNC(clock) \
[&c = clock](rcutils_time_point_value_t * time_point)->rcutils_ret_t { \
try { \
*time_point = c.now().nanoseconds(); \
} catch (...) { \
RCUTILS_SAFE_FWRITE_TO_STDERR( \
"[rclcpp|logging.hpp] RCLCPP_DEBUG_THROTTLE could not get current time stamp\n"); \
return RCUTILS_RET_ERROR; \
} \
return RCUTILS_RET_OK; \
}
/**
* \def RCLCPP_LOG_THROTTLE
* Log a message with given severity with the following condition:
* - Log calls are ignored if the last logged message is not longer ago than the specified duration.
*
* \param logger The `rclcpp::Logger` to use
* \param clock rclcpp::Clock that will be used to get the time point.
* \param duration The duration of the throttle interval as an integral value in milliseconds.
* \param ... The format string, followed by the variable arguments for the format string.
*/
#define RCLCPP_LOG_THROTTLE(severity, logger, clock, duration, ...) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
RCUTILS_LOG_THROTTLE_NAMED( \
severity, \
RCLCPP_LOG_TIME_POINT_FUNC(clock), \
duration, \
(logger).get_name(), \
__VA_ARGS__); \
} while (0)
/**
* \def RCLCPP_LOG_SKIPFIRST_THROTTLE
* Log a message with given severity with the following conditions:
* - The first log call is ignored but all subsequent calls are processed.
* - Log calls are ignored if the last logged message is not longer ago than the specified duration.
*
* \param logger The `rclcpp::Logger` to use
* \param clock rclcpp::Clock that will be used to get the time point.
* \param duration The duration of the throttle interval as an integral value in milliseconds.
* \param ... The format string, followed by the variable arguments for the format string.
*/
#define RCLCPP_LOG_SKIPFIRST_THROTTLE(severity, logger, clock, duration, ...) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
RCUTILS_LOG_SKIPFIRST_THROTTLE_NAMED( \
severity, \
RCLCPP_LOG_TIME_POINT_FUNC(clock), \
duration, \
(logger).get_name(), \
__VA_ARGS__); \
} while (0)
/**
* \def RCLCPP_LOG_STREAM
* Log a message with given severity.
*
* \param logger The `rclcpp::Logger` to use
* \param stream_arg The argument << into a stringstream
*/
#define RCLCPP_LOG_STREAM(severity, logger, stream_arg) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
std::stringstream rclcpp_stream_ss_; \
rclcpp_stream_ss_ << stream_arg; \
RCUTILS_LOG_NAMED(severity, (logger).get_name(), "%s", rclcpp_stream_ss_.str().c_str()); \
} while (0)
/**
* \def RCLCPP_LOG_STREAM_ONCE
* Log a message with given severity with the following condition:
* - All log calls except the first one are ignored.
*
* \param logger The `rclcpp::Logger` to use
* \param stream_arg The argument << into a stringstream
*/
#define RCLCPP_LOG_STREAM_ONCE(severity, logger, stream_arg) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
std::stringstream rclcpp_stream_ss_; \
rclcpp_stream_ss_ << stream_arg; \
RCUTILS_LOG_ONCE_NAMED(severity, (logger).get_name(), "%s", rclcpp_stream_ss_.str().c_str()); \
} while (0)
/**
* \def RCLCPP_LOG_STREAM_EXPRESSION
* Log a message with given severity with the following condition:
* - Log calls are being ignored when the expression evaluates to false.
*
* \param logger The `rclcpp::Logger` to use
* \param expression The expression determining if the message should be logged
* \param stream_arg The argument << into a stringstream
*/
#define RCLCPP_LOG_STREAM_EXPRESSION(severity, logger, expression, stream_arg) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
std::stringstream rclcpp_stream_ss_; \
rclcpp_stream_ss_ << stream_arg; \
RCUTILS_LOG_EXPRESSION_NAMED( \
severity, \
expression, \
(logger).get_name(), \
"%s", rclcpp_stream_ss_.str().c_str()); \
} while (0)
/**
* \def RCLCPP_LOG_STREAM_FUNCTION
* Log a message with given severity with the following condition:
* - Log calls are being ignored when the function returns false.
*
* \param logger The `rclcpp::Logger` to use
* \param function The functions return value determines if the message should be logged
* \param stream_arg The argument << into a stringstream
*/
#define RCLCPP_LOG_STREAM_FUNCTION(severity, logger, function, stream_arg) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
std::stringstream rclcpp_stream_ss_; \
rclcpp_stream_ss_ << stream_arg; \
RCUTILS_LOG_FUNCTION_NAMED( \
severity, \
function, \
(logger).get_name(), \
"%s", rclcpp_stream_ss_.str().c_str()); \
} while (0)
/**
* \def RCLCPP_LOG_STREAM_SKIPFIRST
* Log a message with given severity with the following condition:
* - The first log call is ignored but all subsequent calls are processed.
*
* \param logger The `rclcpp::Logger` to use
* \param stream_arg The argument << into a stringstream
*/
#define RCLCPP_LOG_STREAM_SKIPFIRST(severity, logger, stream_arg) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
std::stringstream rclcpp_stream_ss_; \
rclcpp_stream_ss_ << stream_arg; \
RCUTILS_LOG_SKIPFIRST_NAMED( \
severity, \
(logger).get_name(), \
"%s", rclcpp_stream_ss_.str().c_str()); \
} while (0)
/**
* \def RCLCPP_LOG_STREAM_THROTTLE
* Log a message with given severity with the following condition:
* - Log calls are ignored if the last logged message is not longer ago than the specified duration.
*
* \param logger The `rclcpp::Logger` to use
* \param clock rclcpp::Clock that will be used to get the time point.
* \param duration The duration of the throttle interval as an integral value in milliseconds.
* \param stream_arg The argument << into a stringstream
*/
#define RCLCPP_LOG_STREAM_THROTTLE(severity, logger, clock, duration, stream_arg) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
std::stringstream rclcpp_stream_ss_; \
rclcpp_stream_ss_ << stream_arg; \
RCUTILS_LOG_THROTTLE_NAMED( \
severity, \
RCLCPP_LOG_TIME_POINT_FUNC(clock), \
duration, \
(logger).get_name(), \
"%s", rclcpp_stream_ss_.str().c_str()); \
} while (0)
/**
* \def RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE
* Log a message with given severity with the following conditions:
* - The first log call is ignored but all subsequent calls are processed.
* - Log calls are ignored if the last logged message is not longer ago than the specified duration.
*
* \param logger The `rclcpp::Logger` to use
* \param clock rclcpp::Clock that will be used to get the time point.
* \param duration The duration of the throttle interval as an integral value in milliseconds.
* \param stream_arg The argument << into a stringstream
*/
#define RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE(severity, logger, clock, duration, stream_arg) \
do { \
RCLCPP_STATIC_ASSERT_LOGGER(logger); \
std::stringstream rclcpp_stream_ss_; \
rclcpp_stream_ss_ << stream_arg; \
RCUTILS_LOG_SKIPFIRST_THROTTLE_NAMED( \
severity, \
RCLCPP_LOG_TIME_POINT_FUNC(clock), \
duration, \
(logger).get_name(), \
"%s", rclcpp_stream_ss_.str().c_str()); \
} while (0)
/**
* \def RCLCPP_LOG_MIN_SEVERITY
* Define RCLCPP_LOG_MIN_SEVERITY=RCLCPP_LOG_MIN_SEVERITY_[DEBUG|INFO|WARN|ERROR|FATAL]
* in your build options to compile out anything below that severity.
* Use RCLCPP_LOG_MIN_SEVERITY_NONE to compile out all macros.
*/
#ifndef RCLCPP_LOG_MIN_SEVERITY
#define RCLCPP_LOG_MIN_SEVERITY RCLCPP_LOG_MIN_SEVERITY_DEBUG
#endif
/** @name Logging macros for severity DEBUG.
*/
#if (RCLCPP_LOG_MIN_SEVERITY > RCLCPP_LOG_MIN_SEVERITY_DEBUG)
// empty logging macros for severity DEBUG when being disabled at compile time
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_ONCE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_EXPRESSION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_FUNCTION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_SKIPFIRST(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_SKIPFIRST_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_STREAM(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_STREAM_ONCE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_STREAM_EXPRESSION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_STREAM_FUNCTION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_STREAM_SKIPFIRST(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_STREAM_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_DEBUG_STREAM_SKIPFIRST_THROTTLE(...)
#else
/**
* \def RCLCPP_DEBUG
* \copydoc RCLCPP_LOG
*/
#define RCLCPP_DEBUG(logger, ...) RCLCPP_LOG(RCUTILS_LOG_SEVERITY_DEBUG, logger, __VA_ARGS__)
/**
* \def RCLCPP_DEBUG_ONCE
* \copydoc RCLCPP_LOG_ONCE
*/
#define RCLCPP_DEBUG_ONCE(logger, ...) \
RCLCPP_LOG_ONCE(RCUTILS_LOG_SEVERITY_DEBUG, logger, __VA_ARGS__)
/**
* \def RCLCPP_DEBUG_EXPRESSION
* \copydoc RCLCPP_LOG_EXPRESSION
*/
#define RCLCPP_DEBUG_EXPRESSION(logger, expression, ...) \
RCLCPP_LOG_EXPRESSION(RCUTILS_LOG_SEVERITY_DEBUG, logger, expression, __VA_ARGS__)
/**
* \def RCLCPP_DEBUG_FUNCTION
* \copydoc RCLCPP_LOG_FUNCTION
*/
#define RCLCPP_DEBUG_FUNCTION(logger, function, ...) \
RCLCPP_LOG_FUNCTION(RCUTILS_LOG_SEVERITY_DEBUG, logger, function, __VA_ARGS__)
/**
* \def RCLCPP_DEBUG_SKIPFIRST
* \copydoc RCLCPP_LOG_SKIPFIRST
*/
#define RCLCPP_DEBUG_SKIPFIRST(logger, ...) \
RCLCPP_LOG_SKIPFIRST(RCUTILS_LOG_SEVERITY_DEBUG, logger, __VA_ARGS__)
/**
* \def RCLCPP_DEBUG_THROTTLE
* \copydoc RCLCPP_LOG_THROTTLE
*/
#define RCLCPP_DEBUG_THROTTLE(logger, clock, duration, ...) \
RCLCPP_LOG_THROTTLE(RCUTILS_LOG_SEVERITY_DEBUG, logger, clock, duration, __VA_ARGS__)
/**
* \def RCLCPP_DEBUG_SKIPFIRST_THROTTLE
* \copydoc RCLCPP_LOG_SKIPFIRST_THROTTLE
*/
#define RCLCPP_DEBUG_SKIPFIRST_THROTTLE(logger, clock, duration, ...) \
RCLCPP_LOG_SKIPFIRST_THROTTLE(RCUTILS_LOG_SEVERITY_DEBUG, logger, clock, duration, __VA_ARGS__)
/**
* \def RCLCPP_DEBUG_STREAM
* \copydoc RCLCPP_LOG_STREAM
*/
#define RCLCPP_DEBUG_STREAM(logger, stream_arg) \
RCLCPP_LOG_STREAM(RCUTILS_LOG_SEVERITY_DEBUG, logger, stream_arg)
/**
* \def RCLCPP_DEBUG_STREAM_ONCE
* \copydoc RCLCPP_LOG_STREAM_ONCE
*/
#define RCLCPP_DEBUG_STREAM_ONCE(logger, stream_arg) \
RCLCPP_LOG_STREAM_ONCE(RCUTILS_LOG_SEVERITY_DEBUG, logger, stream_arg)
/**
* \def RCLCPP_DEBUG_STREAM_EXPRESSION
* \copydoc RCLCPP_LOG_STREAM_EXPRESSION
*/
#define RCLCPP_DEBUG_STREAM_EXPRESSION(logger, expression, stream_arg) \
RCLCPP_LOG_STREAM_EXPRESSION(RCUTILS_LOG_SEVERITY_DEBUG, logger, expression, stream_arg)
/**
* \def RCLCPP_DEBUG_STREAM_FUNCTION
* \copydoc RCLCPP_LOG_STREAM_FUNCTION
*/
#define RCLCPP_DEBUG_STREAM_FUNCTION(logger, function, stream_arg) \
RCLCPP_LOG_STREAM_FUNCTION(RCUTILS_LOG_SEVERITY_DEBUG, logger, function, stream_args)
/**
* \def RCLCPP_DEBUG_STREAM_SKIPFIRST
* \copydoc RCLCPP_LOG_STREAM_SKIPFIRST
*/
#define RCLCPP_DEBUG_STREAM_SKIPFIRST(logger, stream_arg) \
RCLCPP_LOG_STREAM_SKIPFIRST(RCUTILS_LOG_SEVERITY_DEBUG, logger, stream_arg)
/**
* \def RCLCPP_DEBUG_STREAM_THROTTLE
* \copydoc RCLCPP_LOG_STREAM_THROTTLE
*/
#define RCLCPP_DEBUG_STREAM_THROTTLE(logger, clock, duration, stream_arg) \
RCLCPP_LOG_STREAM_THROTTLE(RCUTILS_LOG_SEVERITY_DEBUG, logger, clock, duration, stream_arg)
/**
* \def RCLCPP_DEBUG_STREAM_SKIPFIRST_THROTTLE
* \copydoc RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE
*/
#define RCLCPP_DEBUG_STREAM_SKIPFIRST_THROTTLE(logger, clock, duration, stream_arg) \
RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE(RCUTILS_LOG_SEVERITY_DEBUG, logger, clock, duration, \
stream_arg)
#endif
/** @name Logging macros for severity INFO.
*/
#if (RCLCPP_LOG_MIN_SEVERITY > RCLCPP_LOG_MIN_SEVERITY_INFO)
// empty logging macros for severity INFO when being disabled at compile time
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_ONCE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_EXPRESSION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_FUNCTION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_SKIPFIRST(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_SKIPFIRST_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_STREAM(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_STREAM_ONCE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_STREAM_EXPRESSION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_STREAM_FUNCTION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_STREAM_SKIPFIRST(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_STREAM_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_INFO_STREAM_SKIPFIRST_THROTTLE(...)
#else
/**
* \def RCLCPP_INFO
* \copydoc RCLCPP_LOG
*/
#define RCLCPP_INFO(logger, ...) RCLCPP_LOG(RCUTILS_LOG_SEVERITY_INFO, logger, __VA_ARGS__)
/**
* \def RCLCPP_INFO_ONCE
* \copydoc RCLCPP_LOG_ONCE
*/
#define RCLCPP_INFO_ONCE(logger, ...) \
RCLCPP_LOG_ONCE(RCUTILS_LOG_SEVERITY_INFO, logger, __VA_ARGS__)
/**
* \def RCLCPP_INFO_EXPRESSION
* \copydoc RCLCPP_LOG_EXPRESSION
*/
#define RCLCPP_INFO_EXPRESSION(logger, expression, ...) \
RCLCPP_LOG_EXPRESSION(RCUTILS_LOG_SEVERITY_INFO, logger, expression, __VA_ARGS__)
/**
* \def RCLCPP_INFO_FUNCTION
* \copydoc RCLCPP_LOG_FUNCTION
*/
#define RCLCPP_INFO_FUNCTION(logger, function, ...) \
RCLCPP_LOG_FUNCTION(RCUTILS_LOG_SEVERITY_INFO, logger, function, __VA_ARGS__)
/**
* \def RCLCPP_INFO_SKIPFIRST
* \copydoc RCLCPP_LOG_SKIPFIRST
*/
#define RCLCPP_INFO_SKIPFIRST(logger, ...) \
RCLCPP_LOG_SKIPFIRST(RCUTILS_LOG_SEVERITY_INFO, logger, __VA_ARGS__)
/**
* \def RCLCPP_INFO_THROTTLE
* \copydoc RCLCPP_LOG_THROTTLE
*/
#define RCLCPP_INFO_THROTTLE(logger, clock, duration, ...) \
RCLCPP_LOG_THROTTLE(RCUTILS_LOG_SEVERITY_INFO, logger, clock, duration, __VA_ARGS__)
/**
* \def RCLCPP_INFO_SKIPFIRST_THROTTLE
* \copydoc RCLCPP_LOG_SKIPFIRST_THROTTLE
*/
#define RCLCPP_INFO_SKIPFIRST_THROTTLE(logger, clock, duration, ...) \
RCLCPP_LOG_SKIPFIRST_THROTTLE(RCUTILS_LOG_SEVERITY_INFO, logger, clock, duration, __VA_ARGS__)
/**
* \def RCLCPP_INFO_STREAM
* \copydoc RCLCPP_LOG_STREAM
*/
#define RCLCPP_INFO_STREAM(logger, stream_arg) \
RCLCPP_LOG_STREAM(RCUTILS_LOG_SEVERITY_INFO, logger, stream_arg)
/**
* \def RCLCPP_INFO_STREAM_ONCE
* \copydoc RCLCPP_LOG_STREAM_ONCE
*/
#define RCLCPP_INFO_STREAM_ONCE(logger, stream_arg) \
RCLCPP_LOG_STREAM_ONCE(RCUTILS_LOG_SEVERITY_INFO, logger, stream_arg)
/**
* \def RCLCPP_INFO_STREAM_EXPRESSION
* \copydoc RCLCPP_LOG_STREAM_EXPRESSION
*/
#define RCLCPP_INFO_STREAM_EXPRESSION(logger, expression, stream_arg) \
RCLCPP_LOG_STREAM_EXPRESSION(RCUTILS_LOG_SEVERITY_INFO, logger, expression, stream_arg)
/**
* \def RCLCPP_INFO_STREAM_FUNCTION
* \copydoc RCLCPP_LOG_STREAM_FUNCTION
*/
#define RCLCPP_INFO_STREAM_FUNCTION(logger, function, stream_arg) \
RCLCPP_LOG_STREAM_FUNCTION(RCUTILS_LOG_SEVERITY_INFO, logger, function, stream_args)
/**
* \def RCLCPP_INFO_STREAM_SKIPFIRST
* \copydoc RCLCPP_LOG_STREAM_SKIPFIRST
*/
#define RCLCPP_INFO_STREAM_SKIPFIRST(logger, stream_arg) \
RCLCPP_LOG_STREAM_SKIPFIRST(RCUTILS_LOG_SEVERITY_INFO, logger, stream_arg)
/**
* \def RCLCPP_INFO_STREAM_THROTTLE
* \copydoc RCLCPP_LOG_STREAM_THROTTLE
*/
#define RCLCPP_INFO_STREAM_THROTTLE(logger, clock, duration, stream_arg) \
RCLCPP_LOG_STREAM_THROTTLE(RCUTILS_LOG_SEVERITY_INFO, logger, clock, duration, stream_arg)
/**
* \def RCLCPP_INFO_STREAM_SKIPFIRST_THROTTLE
* \copydoc RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE
*/
#define RCLCPP_INFO_STREAM_SKIPFIRST_THROTTLE(logger, clock, duration, stream_arg) \
RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE(RCUTILS_LOG_SEVERITY_INFO, logger, clock, duration, \
stream_arg)
#endif
/** @name Logging macros for severity WARN.
*/
#if (RCLCPP_LOG_MIN_SEVERITY > RCLCPP_LOG_MIN_SEVERITY_WARN)
// empty logging macros for severity WARN when being disabled at compile time
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_ONCE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_EXPRESSION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_FUNCTION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_SKIPFIRST(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_SKIPFIRST_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_STREAM(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_STREAM_ONCE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_STREAM_EXPRESSION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_STREAM_FUNCTION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_STREAM_SKIPFIRST(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_STREAM_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_WARN_STREAM_SKIPFIRST_THROTTLE(...)
#else
/**
* \def RCLCPP_WARN
* \copydoc RCLCPP_LOG
*/
#define RCLCPP_WARN(logger, ...) RCLCPP_LOG(RCUTILS_LOG_SEVERITY_WARN, logger, __VA_ARGS__)
/**
* \def RCLCPP_WARN_ONCE
* \copydoc RCLCPP_LOG_ONCE
*/
#define RCLCPP_WARN_ONCE(logger, ...) \
RCLCPP_LOG_ONCE(RCUTILS_LOG_SEVERITY_WARN, logger, __VA_ARGS__)
/**
* \def RCLCPP_WARN_EXPRESSION
* \copydoc RCLCPP_LOG_EXPRESSION
*/
#define RCLCPP_WARN_EXPRESSION(logger, expression, ...) \
RCLCPP_LOG_EXPRESSION(RCUTILS_LOG_SEVERITY_WARN, logger, expression, __VA_ARGS__)
/**
* \def RCLCPP_WARN_FUNCTION
* \copydoc RCLCPP_LOG_FUNCTION
*/
#define RCLCPP_WARN_FUNCTION(logger, function, ...) \
RCLCPP_LOG_FUNCTION(RCUTILS_LOG_SEVERITY_WARN, logger, function, __VA_ARGS__)
/**
* \def RCLCPP_WARN_SKIPFIRST
* \copydoc RCLCPP_LOG_SKIPFIRST
*/
#define RCLCPP_WARN_SKIPFIRST(logger, ...) \
RCLCPP_LOG_SKIPFIRST(RCUTILS_LOG_SEVERITY_WARN, logger, __VA_ARGS__)
/**
* \def RCLCPP_WARN_THROTTLE
* \copydoc RCLCPP_LOG_THROTTLE
*/
#define RCLCPP_WARN_THROTTLE(logger, clock, duration, ...) \
RCLCPP_LOG_THROTTLE(RCUTILS_LOG_SEVERITY_WARN, logger, clock, duration, __VA_ARGS__)
/**
* \def RCLCPP_WARN_SKIPFIRST_THROTTLE
* \copydoc RCLCPP_LOG_SKIPFIRST_THROTTLE
*/
#define RCLCPP_WARN_SKIPFIRST_THROTTLE(logger, clock, duration, ...) \
RCLCPP_LOG_SKIPFIRST_THROTTLE(RCUTILS_LOG_SEVERITY_WARN, logger, clock, duration, __VA_ARGS__)
/**
* \def RCLCPP_WARN_STREAM
* \copydoc RCLCPP_LOG_STREAM
*/
#define RCLCPP_WARN_STREAM(logger, stream_arg) \
RCLCPP_LOG_STREAM(RCUTILS_LOG_SEVERITY_WARN, logger, stream_arg)
/**
* \def RCLCPP_WARN_STREAM_ONCE
* \copydoc RCLCPP_LOG_STREAM_ONCE
*/
#define RCLCPP_WARN_STREAM_ONCE(logger, stream_arg) \
RCLCPP_LOG_STREAM_ONCE(RCUTILS_LOG_SEVERITY_WARN, logger, stream_arg)
/**
* \def RCLCPP_WARN_STREAM_EXPRESSION
* \copydoc RCLCPP_LOG_STREAM_EXPRESSION
*/
#define RCLCPP_WARN_STREAM_EXPRESSION(logger, expression, stream_arg) \
RCLCPP_LOG_STREAM_EXPRESSION(RCUTILS_LOG_SEVERITY_WARN, logger, expression, stream_arg)
/**
* \def RCLCPP_WARN_STREAM_FUNCTION
* \copydoc RCLCPP_LOG_STREAM_FUNCTION
*/
#define RCLCPP_WARN_STREAM_FUNCTION(logger, function, stream_arg) \
RCLCPP_LOG_STREAM_FUNCTION(RCUTILS_LOG_SEVERITY_WARN, logger, function, stream_args)
/**
* \def RCLCPP_WARN_STREAM_SKIPFIRST
* \copydoc RCLCPP_LOG_STREAM_SKIPFIRST
*/
#define RCLCPP_WARN_STREAM_SKIPFIRST(logger, stream_arg) \
RCLCPP_LOG_STREAM_SKIPFIRST(RCUTILS_LOG_SEVERITY_WARN, logger, stream_arg)
/**
* \def RCLCPP_WARN_STREAM_THROTTLE
* \copydoc RCLCPP_LOG_STREAM_THROTTLE
*/
#define RCLCPP_WARN_STREAM_THROTTLE(logger, clock, duration, stream_arg) \
RCLCPP_LOG_STREAM_THROTTLE(RCUTILS_LOG_SEVERITY_WARN, logger, clock, duration, stream_arg)
/**
* \def RCLCPP_WARN_STREAM_SKIPFIRST_THROTTLE
* \copydoc RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE
*/
#define RCLCPP_WARN_STREAM_SKIPFIRST_THROTTLE(logger, clock, duration, stream_arg) \
RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE(RCUTILS_LOG_SEVERITY_WARN, logger, clock, duration, \
stream_arg)
#endif
/** @name Logging macros for severity ERROR.
*/
#if (RCLCPP_LOG_MIN_SEVERITY > RCLCPP_LOG_MIN_SEVERITY_ERROR)
// empty logging macros for severity ERROR when being disabled at compile time
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_ONCE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_EXPRESSION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_FUNCTION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_SKIPFIRST(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_SKIPFIRST_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_STREAM(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_STREAM_ONCE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_STREAM_EXPRESSION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_STREAM_FUNCTION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_STREAM_SKIPFIRST(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_STREAM_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_ERROR_STREAM_SKIPFIRST_THROTTLE(...)
#else
/**
* \def RCLCPP_ERROR
* \copydoc RCLCPP_LOG
*/
#define RCLCPP_ERROR(logger, ...) RCLCPP_LOG(RCUTILS_LOG_SEVERITY_ERROR, logger, __VA_ARGS__)
/**
* \def RCLCPP_ERROR_ONCE
* \copydoc RCLCPP_LOG_ONCE
*/
#define RCLCPP_ERROR_ONCE(logger, ...) \
RCLCPP_LOG_ONCE(RCUTILS_LOG_SEVERITY_ERROR, logger, __VA_ARGS__)
/**
* \def RCLCPP_ERROR_EXPRESSION
* \copydoc RCLCPP_LOG_EXPRESSION
*/
#define RCLCPP_ERROR_EXPRESSION(logger, expression, ...) \
RCLCPP_LOG_EXPRESSION(RCUTILS_LOG_SEVERITY_ERROR, logger, expression, __VA_ARGS__)
/**
* \def RCLCPP_ERROR_FUNCTION
* \copydoc RCLCPP_LOG_FUNCTION
*/
#define RCLCPP_ERROR_FUNCTION(logger, function, ...) \
RCLCPP_LOG_FUNCTION(RCUTILS_LOG_SEVERITY_ERROR, logger, function, __VA_ARGS__)
/**
* \def RCLCPP_ERROR_SKIPFIRST
* \copydoc RCLCPP_LOG_SKIPFIRST
*/
#define RCLCPP_ERROR_SKIPFIRST(logger, ...) \
RCLCPP_LOG_SKIPFIRST(RCUTILS_LOG_SEVERITY_ERROR, logger, __VA_ARGS__)
/**
* \def RCLCPP_ERROR_THROTTLE
* \copydoc RCLCPP_LOG_THROTTLE
*/
#define RCLCPP_ERROR_THROTTLE(logger, clock, duration, ...) \
RCLCPP_LOG_THROTTLE(RCUTILS_LOG_SEVERITY_ERROR, logger, clock, duration, __VA_ARGS__)
/**
* \def RCLCPP_ERROR_SKIPFIRST_THROTTLE
* \copydoc RCLCPP_LOG_SKIPFIRST_THROTTLE
*/
#define RCLCPP_ERROR_SKIPFIRST_THROTTLE(logger, clock, duration, ...) \
RCLCPP_LOG_SKIPFIRST_THROTTLE(RCUTILS_LOG_SEVERITY_ERROR, logger, clock, duration, __VA_ARGS__)
/**
* \def RCLCPP_ERROR_STREAM
* \copydoc RCLCPP_LOG_STREAM
*/
#define RCLCPP_ERROR_STREAM(logger, stream_arg) \
RCLCPP_LOG_STREAM(RCUTILS_LOG_SEVERITY_ERROR, logger, stream_arg)
/**
* \def RCLCPP_ERROR_STREAM_ONCE
* \copydoc RCLCPP_LOG_STREAM_ONCE
*/
#define RCLCPP_ERROR_STREAM_ONCE(logger, stream_arg) \
RCLCPP_LOG_STREAM_ONCE(RCUTILS_LOG_SEVERITY_ERROR, logger, stream_arg)
/**
* \def RCLCPP_ERROR_STREAM_EXPRESSION
* \copydoc RCLCPP_LOG_STREAM_EXPRESSION
*/
#define RCLCPP_ERROR_STREAM_EXPRESSION(logger, expression, stream_arg) \
RCLCPP_LOG_STREAM_EXPRESSION(RCUTILS_LOG_SEVERITY_ERROR, logger, expression, stream_arg)
/**
* \def RCLCPP_ERROR_STREAM_FUNCTION
* \copydoc RCLCPP_LOG_STREAM_FUNCTION
*/
#define RCLCPP_ERROR_STREAM_FUNCTION(logger, function, stream_arg) \
RCLCPP_LOG_STREAM_FUNCTION(RCUTILS_LOG_SEVERITY_ERROR, logger, function, stream_args)
/**
* \def RCLCPP_ERROR_STREAM_SKIPFIRST
* \copydoc RCLCPP_LOG_STREAM_SKIPFIRST
*/
#define RCLCPP_ERROR_STREAM_SKIPFIRST(logger, stream_arg) \
RCLCPP_LOG_STREAM_SKIPFIRST(RCUTILS_LOG_SEVERITY_ERROR, logger, stream_arg)
/**
* \def RCLCPP_ERROR_STREAM_THROTTLE
* \copydoc RCLCPP_LOG_STREAM_THROTTLE
*/
#define RCLCPP_ERROR_STREAM_THROTTLE(logger, clock, duration, stream_arg) \
RCLCPP_LOG_STREAM_THROTTLE(RCUTILS_LOG_SEVERITY_ERROR, logger, clock, duration, stream_arg)
/**
* \def RCLCPP_ERROR_STREAM_SKIPFIRST_THROTTLE
* \copydoc RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE
*/
#define RCLCPP_ERROR_STREAM_SKIPFIRST_THROTTLE(logger, clock, duration, stream_arg) \
RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE(RCUTILS_LOG_SEVERITY_ERROR, logger, clock, duration, \
stream_arg)
#endif
/** @name Logging macros for severity FATAL.
*/
#if (RCLCPP_LOG_MIN_SEVERITY > RCLCPP_LOG_MIN_SEVERITY_FATAL)
// empty logging macros for severity FATAL when being disabled at compile time
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_ONCE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_EXPRESSION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_FUNCTION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_SKIPFIRST(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_SKIPFIRST_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_STREAM(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_STREAM_ONCE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_STREAM_EXPRESSION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_STREAM_FUNCTION(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_STREAM_SKIPFIRST(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_STREAM_THROTTLE(...)
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_FATAL_STREAM_SKIPFIRST_THROTTLE(...)
#else
/**
* \def RCLCPP_FATAL
* \copydoc RCLCPP_LOG
*/
#define RCLCPP_FATAL(logger, ...) RCLCPP_LOG(RCUTILS_LOG_SEVERITY_FATAL, logger, __VA_ARGS__)
/**
* \def RCLCPP_FATAL_ONCE
* \copydoc RCLCPP_LOG_ONCE
*/
#define RCLCPP_FATAL_ONCE(logger, ...) \
RCLCPP_LOG_ONCE(RCUTILS_LOG_SEVERITY_FATAL, logger, __VA_ARGS__)
/**
* \def RCLCPP_FATAL_EXPRESSION
* \copydoc RCLCPP_LOG_EXPRESSION
*/
#define RCLCPP_FATAL_EXPRESSION(logger, expression, ...) \
RCLCPP_LOG_EXPRESSION(RCUTILS_LOG_SEVERITY_FATAL, logger, expression, __VA_ARGS__)
/**
* \def RCLCPP_FATAL_FUNCTION
* \copydoc RCLCPP_LOG_FUNCTION
*/
#define RCLCPP_FATAL_FUNCTION(logger, function, ...) \
RCLCPP_LOG_FUNCTION(RCUTILS_LOG_SEVERITY_FATAL, logger, function, __VA_ARGS__)
/**
* \def RCLCPP_FATAL_SKIPFIRST
* \copydoc RCLCPP_LOG_SKIPFIRST
*/
#define RCLCPP_FATAL_SKIPFIRST(logger, ...) \
RCLCPP_LOG_SKIPFIRST(RCUTILS_LOG_SEVERITY_FATAL, logger, __VA_ARGS__)
/**
* \def RCLCPP_FATAL_THROTTLE
* \copydoc RCLCPP_LOG_THROTTLE
*/
#define RCLCPP_FATAL_THROTTLE(logger, clock, duration, ...) \
RCLCPP_LOG_THROTTLE(RCUTILS_LOG_SEVERITY_FATAL, logger, clock, duration, __VA_ARGS__)
/**
* \def RCLCPP_FATAL_SKIPFIRST_THROTTLE
* \copydoc RCLCPP_LOG_SKIPFIRST_THROTTLE
*/
#define RCLCPP_FATAL_SKIPFIRST_THROTTLE(logger, clock, duration, ...) \
RCLCPP_LOG_SKIPFIRST_THROTTLE(RCUTILS_LOG_SEVERITY_FATAL, logger, clock, duration, __VA_ARGS__)
/**
* \def RCLCPP_FATAL_STREAM
* \copydoc RCLCPP_LOG_STREAM
*/
#define RCLCPP_FATAL_STREAM(logger, stream_arg) \
RCLCPP_LOG_STREAM(RCUTILS_LOG_SEVERITY_FATAL, logger, stream_arg)
/**
* \def RCLCPP_FATAL_STREAM_ONCE
* \copydoc RCLCPP_LOG_STREAM_ONCE
*/
#define RCLCPP_FATAL_STREAM_ONCE(logger, stream_arg) \
RCLCPP_LOG_STREAM_ONCE(RCUTILS_LOG_SEVERITY_FATAL, logger, stream_arg)
/**
* \def RCLCPP_FATAL_STREAM_EXPRESSION
* \copydoc RCLCPP_LOG_STREAM_EXPRESSION
*/
#define RCLCPP_FATAL_STREAM_EXPRESSION(logger, expression, stream_arg) \
RCLCPP_LOG_STREAM_EXPRESSION(RCUTILS_LOG_SEVERITY_FATAL, logger, expression, stream_arg)
/**
* \def RCLCPP_FATAL_STREAM_FUNCTION
* \copydoc RCLCPP_LOG_STREAM_FUNCTION
*/
#define RCLCPP_FATAL_STREAM_FUNCTION(logger, function, stream_arg) \
RCLCPP_LOG_STREAM_FUNCTION(RCUTILS_LOG_SEVERITY_FATAL, logger, function, stream_args)
/**
* \def RCLCPP_FATAL_STREAM_SKIPFIRST
* \copydoc RCLCPP_LOG_STREAM_SKIPFIRST
*/
#define RCLCPP_FATAL_STREAM_SKIPFIRST(logger, stream_arg) \
RCLCPP_LOG_STREAM_SKIPFIRST(RCUTILS_LOG_SEVERITY_FATAL, logger, stream_arg)
/**
* \def RCLCPP_FATAL_STREAM_THROTTLE
* \copydoc RCLCPP_LOG_STREAM_THROTTLE
*/
#define RCLCPP_FATAL_STREAM_THROTTLE(logger, clock, duration, stream_arg) \
RCLCPP_LOG_STREAM_THROTTLE(RCUTILS_LOG_SEVERITY_FATAL, logger, clock, duration, stream_arg)
/**
* \def RCLCPP_FATAL_STREAM_SKIPFIRST_THROTTLE
* \copydoc RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE
*/
#define RCLCPP_FATAL_STREAM_SKIPFIRST_THROTTLE(logger, clock, duration, stream_arg) \
RCLCPP_LOG_STREAM_SKIPFIRST_THROTTLE(RCUTILS_LOG_SEVERITY_FATAL, logger, clock, duration, \
stream_arg)
#endif
#endif // RCLCPP__LOGGING_HPP_

View File

@@ -1388,6 +1388,66 @@ public:
std::vector<rclcpp::TopicEndpointInfo>
get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle = false) const;
/// Return the service endpoint information about clients on a given service.
/**
* The returned parameter is a list of service endpoint information, where each item will contain
* the node name, node namespace, service type, endpoint type, endpoint count,
* service endpoint's GIDs, and its QoS profiles.
*
* When the `no_mangle` parameter is `true`, the provided `service_name` should be a valid service
* name for the middleware (useful when combining ROS with native middleware apps).
* When the `no_mangle` parameter is `false`, the provided `service_name` should follow
* ROS service name conventions. In DDS-based RMWs, services are implemented as topics with mangled
* names (e.g., `rq/my_serviceRequest` and `rp/my_serviceReply`), so `no_mangle = true` is not
* supported and will result in an error. Use `get_publishers_info_by_topic` or
* `get_subscriptions_info_by_topic` for unmangled topic queries in such cases. Other RMWs
* (e.g., Zenoh) may support `no_mangle = true` if they natively handle services without topic-based
*
* 'service_name` may be a relative, private, or fully qualified service name.
* A relative or private service will be expanded using this node's namespace and name.
* The queried `service_name` is not remapped.
*
* \param[in] service_name the actual service name used; it will not be automatically remapped.
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
* otherwise it should be a valid ROS service name. Defaults to `false`.
* \return a list of SeviceEndpointInfo representing all the clients on this service.
* \throws InvalidServiceNameError if the given service_name is invalid.
* \throws std::runtime_error if internal error happens.
*/
RCLCPP_PUBLIC
std::vector<rclcpp::ServiceEndpointInfo>
get_clients_info_by_service(const std::string & service_name, bool no_mangle = false) const;
/// Return the service endpoint information about servers on a given service.
/**
* The returned parameter is a list of service endpoint information, where each item will contain
* the node name, node namespace, service type, endpoint type, endpoint count,
* service endpoint's GIDs, and its QoS profiles.
*
* When the `no_mangle` parameter is `true`, the provided `service_name` should be a valid service
* name for the middleware (useful when combining ROS with native middleware apps).
* When the `no_mangle` parameter is `false`, the provided `service_name` should follow
* ROS service name conventions. In DDS-based RMWs, services are implemented as topics with mangled
* names (e.g., `rq/my_serviceRequest` and `rp/my_serviceReply`), so `no_mangle = true` is not
* supported and will result in an error. Use `rcl_get_publishers_info_by_topic` or
* `rcl_get_subscriptions_info_by_topic` for unmangled topic queries in such cases. Other RMWs
* (e.g., Zenoh) may support `no_mangle = true` if they natively handle services without topic-based
*
* 'service_name` may be a relative, private, or fully qualified service name.
* A relative or private service will be expanded using this node's namespace and name.
* The queried `service_name` is not remapped.
*
* \param[in] service_name the actual service name used; it will not be automatically remapped.
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
* otherwise it should be a valid ROS service name. Defaults to `false`.
* \return a list of SeviceEndpointInfo representing all the servers on this service.
* \throws InvalidServiceNameError if the given service_name is invalid.
* \throws std::runtime_error if internal error happens.
*/
RCLCPP_PUBLIC
std::vector<rclcpp::ServiceEndpointInfo>
get_servers_info_by_service(const std::string & service_name, bool no_mangle = false) const;
/// Return a graph event, which will be set anytime a graph change occurs.
/* The graph Event object is a loan which must be returned.
* The Event object is scoped and therefore to return the loan just let it go

View File

@@ -199,6 +199,12 @@ init_tuple(NodeT & n)
{ \
return StorageClassT::template get<NodeInterfaceType>(); \
} \
\
std::shared_ptr<const NodeInterfaceType> \
get_node_ ## NodeInterfaceName ## _interface() const \
{ \
return StorageClassT::template get<NodeInterfaceType>(); \
} \
}; \
} // namespace rclcpp::node_interfaces::detail
// *INDENT-ON*

View File

@@ -121,11 +121,6 @@ public:
std::atomic_bool &
get_associated_with_executor_atomic() override;
[[deprecated("Use get_shared_notify_guard_condition or trigger_notify_guard_condition instead")]]
RCLCPP_PUBLIC
rclcpp::GuardCondition &
get_notify_guard_condition() override;
RCLCPP_PUBLIC
rclcpp::GuardCondition::SharedPtr
get_shared_notify_guard_condition() override;

View File

@@ -144,17 +144,6 @@ public:
std::atomic_bool &
get_associated_with_executor_atomic() = 0;
/// Return a guard condition that should be notified when the internal node state changes.
/**
* For example, this should be notified when a publisher is added or removed.
*
* \return the GuardCondition if it is valid, else throw runtime error
*/
RCLCPP_PUBLIC
virtual
rclcpp::GuardCondition &
get_notify_guard_condition() = 0;
/// Return a guard condition that should be notified when the internal node state changes.
/**
* For example, this should be notified when a publisher is added or removed.

View File

@@ -33,6 +33,7 @@
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/service_endpoint_info_array.h"
#include "rmw/topic_endpoint_info_array.h"
namespace rclcpp
@@ -159,14 +160,24 @@ public:
const std::string & topic_name,
bool no_mangle = false) const override;
RCLCPP_PUBLIC
std::vector<rclcpp::ServiceEndpointInfo>
get_clients_info_by_service(
const std::string & service_name,
bool no_mangle = false) const override;
RCLCPP_PUBLIC
std::vector<rclcpp::ServiceEndpointInfo>
get_servers_info_by_service(
const std::string & service_name,
bool no_mangle = false) const override;
private:
RCLCPP_DISABLE_COPY(NodeGraph)
/// Handle to the NodeBaseInterface given in the constructor.
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
/// Graph Listener which waits on graph changes for the node and is shared across nodes.
std::shared_ptr<rclcpp::graph_listener::GraphListener> graph_listener_;
/// Whether or not this node needs to be added to the graph listener.
std::atomic_bool should_add_to_graph_listener_;

View File

@@ -18,6 +18,8 @@
#include <algorithm>
#include <array>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <map>
#include <string>
#include <tuple>
@@ -40,7 +42,9 @@ enum class EndpointType
{
Invalid = RMW_ENDPOINT_INVALID,
Publisher = RMW_ENDPOINT_PUBLISHER,
Subscription = RMW_ENDPOINT_SUBSCRIPTION
Subscription = RMW_ENDPOINT_SUBSCRIPTION,
Client = RMW_ENDPOINT_CLIENT,
Server = RMW_ENDPOINT_SERVER
};
/**
@@ -143,6 +147,125 @@ private:
rosidl_type_hash_t topic_type_hash_;
};
/**
* Struct that contains service endpoint information like the associated node name, node namespace,
* service type, endpoint type, endpoint count, endpoint GIDs, and its QoS profiles.
*/
class ServiceEndpointInfo
{
public:
/// Construct a ServiceEndpointInfo from a rcl_service_endpoint_info_t.
RCLCPP_PUBLIC
explicit ServiceEndpointInfo(const rcl_service_endpoint_info_t & info)
: node_name_(info.node_name),
node_namespace_(info.node_namespace),
service_type_(info.service_type),
endpoint_type_(static_cast<rclcpp::EndpointType>(info.endpoint_type)),
service_type_hash_(info.service_type_hash),
endpoint_count_(info.endpoint_count)
{
for(size_t i = 0; i < endpoint_count_; i++) {
std::array<uint8_t, RMW_GID_STORAGE_SIZE> gid;
std::copy(info.endpoint_gids[i], info.endpoint_gids[i] + RMW_GID_STORAGE_SIZE, gid.begin());
endpoint_gids_.push_back(gid);
rclcpp::QoS qos(
{info.qos_profiles[i].history, info.qos_profiles[i].depth}, info.qos_profiles[i]);
qos_profiles_.push_back(qos);
}
}
/// Get a mutable reference to the node name.
RCLCPP_PUBLIC
std::string &
node_name();
/// Get a const reference to the node name.
RCLCPP_PUBLIC
const std::string &
node_name() const;
/// Get a mutable reference to the node namespace.
RCLCPP_PUBLIC
std::string &
node_namespace();
/// Get a const reference to the node namespace.
RCLCPP_PUBLIC
const std::string &
node_namespace() const;
/// Get a mutable reference to the service type string.
RCLCPP_PUBLIC
std::string &
service_type();
/// Get a const reference to the service type string.
RCLCPP_PUBLIC
const std::string &
service_type() const;
/// Get a mutable reference to the service endpoint type.
RCLCPP_PUBLIC
rclcpp::EndpointType &
endpoint_type();
/// Get a const reference to the service endpoint type.
RCLCPP_PUBLIC
const rclcpp::EndpointType &
endpoint_type() const;
/// Get a mutable reference to the endpoint count.
RCLCPP_PUBLIC
size_t &
endpoint_count();
/// Get a const reference to the endpoint count.
RCLCPP_PUBLIC
const size_t &
endpoint_count() const;
/// Get a mutable reference to the GID of the service endpoint.
RCLCPP_PUBLIC
std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
endpoint_gids();
/// Get a const reference to the GID of the service endpoint.
RCLCPP_PUBLIC
const std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
endpoint_gids() const;
/// Get a mutable reference to the QoS profile of the service endpoint.
RCLCPP_PUBLIC
std::vector<rclcpp::QoS> &
qos_profiles();
/// Get a const reference to the QoS profile of the service endpoint.
RCLCPP_PUBLIC
const std::vector<rclcpp::QoS> &
qos_profiles() const;
/// Get a mutable reference to the type hash of the service endpoint.
RCLCPP_PUBLIC
rosidl_type_hash_t &
service_type_hash();
/// Get a const reference to the type hash of the service endpoint.
RCLCPP_PUBLIC
const rosidl_type_hash_t &
service_type_hash() const;
private:
std::string node_name_;
std::string node_namespace_;
std::string service_type_;
rclcpp::EndpointType endpoint_type_;
std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> endpoint_gids_;
std::vector<rclcpp::QoS> qos_profiles_;
rosidl_type_hash_t service_type_hash_;
size_t endpoint_count_;
};
namespace node_interfaces
{
@@ -408,6 +531,30 @@ public:
virtual
std::vector<rclcpp::TopicEndpointInfo>
get_subscriptions_info_by_topic(const std::string & topic_name, bool no_mangle = false) const = 0;
/// Return the service endpoint information about clients on a given service.
/**
* \param[in] service_name the actual service name used; it will not be automatically remapped.
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
* otherwise it should be a valid ROS service name.
* \sa rclcpp::Node::get_clients_info_by_service
*/
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::ServiceEndpointInfo>
get_clients_info_by_service(const std::string & service_name, bool no_mangle = false) const = 0;
/// Return the service endpoint information about servers on a given service.
/**
* \param[in] service_name the actual service name used; it will not be automatically remapped.
* \param[in] no_mangle if `true`, `service_name` needs to be a valid middleware service name,
* otherwise it should be a valid ROS service name.
* \sa rclcpp::Node::get_servers_info_by_service
*/
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::ServiceEndpointInfo>
get_servers_info_by_service(const std::string & service_name, bool no_mangle = false) const = 0;
};
} // namespace node_interfaces

View File

@@ -146,6 +146,14 @@ public:
return *this;
}
/// Append a single parameter override, parameter idiom style.
NodeOptions &
append_parameter_override(const rclcpp::Parameter & param)
{
this->parameter_overrides().push_back(param);
return *this;
}
/// Return the use_global_arguments flag.
RCLCPP_PUBLIC
bool

View File

@@ -0,0 +1,82 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__PARAMETER_DESCRIPTOR_WRAPPER_HPP_
#define RCLCPP__PARAMETER_DESCRIPTOR_WRAPPER_HPP_
// C++ Standard library includes
#include <functional>
#include <utility>
#include <memory>
#include <string>
// Additional ROS libraries needed
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/parameter_value.hpp"
#include "rclcpp/node_interfaces/get_node_parameters_interface.hpp"
#include "rclcpp/visibility_control.hpp"
#include "node_interfaces/node_parameters_interface.hpp"
namespace rclcpp
{
// Implements ParameterDesription class with builder design pattern
class ParameterDescription
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(ParameterDescription)
// List of classes the builder manages
RCLCPP_PUBLIC
ParameterDescription();
// Our Main build methods which will construct the base class
RCLCPP_PUBLIC rcl_interfaces::msg::ParameterDescriptor build() const;
// Builder Methods:
// Describes the instances in a parameter_description object
RCLCPP_PUBLIC ParameterDescription & set_name(const std::string & name);
RCLCPP_PUBLIC ParameterDescription & set_type(std::uint8_t type);
RCLCPP_PUBLIC ParameterDescription & set_description_text(const std::string & description);
RCLCPP_PUBLIC ParameterDescription & set_additional_constraints(const std::string & constraints);
RCLCPP_PUBLIC ParameterDescription & set_read_only(bool read_only);
RCLCPP_PUBLIC ParameterDescription & set_dynamic_typing(bool dynamic_typing);
RCLCPP_PUBLIC ParameterDescription & set_floating_point_description_range(
float min, float max, float step);
RCLCPP_PUBLIC ParameterDescription & set_integer_description_range(int min, int max, int step);
// Need the current node in order to begin the configuration state
// for it via the declare_parameter function
template<typename NodeT>
ParameterDescription & declare_parameter(
const rclcpp::ParameterValue & default_value,
NodeT && node)
{
auto node_param = rclcpp::node_interfaces::get_node_parameters_interface(node);
node_param->declare_parameter(
parameter_descriptor.name, default_value,
parameter_descriptor);
return *this;
}
private:
// The main descriptor object we're meant to initialize and adjust
rcl_interfaces::msg::ParameterDescriptor parameter_descriptor;
};
} // namespace rclcpp
#endif // RCLCPP__PARAMETER_DESCRIPTOR_WRAPPER_HPP_

View File

@@ -234,6 +234,10 @@ public:
/**
* If a node_name is not provided, defaults to the current node.
*
* The configure_nodes_filter() function will affect the behavior of this function.
* If the node specified in this function isn't included in the nodes specified in
* configure_nodes_filter(), the callback will never be called.
*
* Note: if the returned callback handle smart pointer is not captured, the callback
* is immediately unregistered. A compiler warning should be generated to warn
* of this.
@@ -251,6 +255,31 @@ public:
ParameterCallbackType callback,
const std::string & node_name = "");
/// Configure which node parameter events will be received.
/**
* This function depends on rmw implementation support for content filtering.
* If middleware doesn't support contentfilter, return false.
*
* If node_names is empty, the configured node filter will be cleared.
*
* If this function return true, only parameter events from the specified node will be received.
* It affects the behavior of the following two functions.
* - add_parameter_event_callback()
* The callback will only be called for parameter events from the specified nodes which are
* configured in this function.
* - add_parameter_callback()
* The callback will only be called for parameter events from the specified nodes which are
* configured in this function and add_parameter_callback().
* If the nodes specified in this function is different from the nodes specified in
* add_parameter_callback(), the callback will never be called.
*
* \param[in] node_names Node names to filter parameter events from.
* \returns true if configuring was successfully applied, false otherwise.
* \throws rclcpp::exceptions::RCLError if internal error occurred when calling the rcl function.
*/
RCLCPP_PUBLIC
bool configure_nodes_filter(const std::vector<std::string> & node_names);
/// Remove a parameter callback registered with add_parameter_callback.
/**
* The parameter name and node name are inspected from the callback handle. The callback handle

View File

@@ -574,6 +574,13 @@ protected:
std::unique_ptr<PublishedType, PublishedTypeDeleter>
duplicate_type_adapt_message_as_unique_ptr(const PublishedType & msg)
{
/// Assert that the published type has no overloaded operator new since this leads to
/// new/delete mismatch (see https://github.com/ros2/rclcpp/issues/2951)
static_assert(!detail::has_overloaded_operator_new_v<PublishedType>,
"When publishing by value (i.e. when calling publish(const T& msg)), the published "
"message type must not have an overloaded operator new. In this case, please use the "
"publish(std::unique_ptr<T> msg) method instead.");
auto ptr = PublishedTypeAllocatorTraits::allocate(published_type_allocator_, 1);
PublishedTypeAllocatorTraits::construct(published_type_allocator_, ptr, msg);
return std::unique_ptr<PublishedType, PublishedTypeDeleter>(ptr, published_type_deleter_);

View File

@@ -68,8 +68,6 @@
*
* - Executors (responsible for execution of callbacks through a blocking spin):
* - rclcpp::spin()
* - rclcpp::spin_some()
* - rclcpp::spin_until_future_complete()
* - rclcpp::executors::SingleThreadedExecutor
* - rclcpp::executors::SingleThreadedExecutor::add_node()
* - rclcpp::executors::SingleThreadedExecutor::spin()
@@ -177,6 +175,7 @@
#include "rclcpp/parameter_event_handler.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/parameter_service.hpp"
#include "rclcpp/parameter_descriptor_wrapper.hpp"
#include "rclcpp/rate.hpp"
#include "rclcpp/time.hpp"
#include "rclcpp/utilities.hpp"

View File

@@ -279,6 +279,50 @@ public:
return message_memory_strategy_->borrow_serialized_message();
}
/// Disable callbacks from being called
/**
* This method will block, until any subscription's callbacks provided during construction
* currently being executed are finished.
* \note This method also temporary removes the on new message callback and all
* on new event callbacks from the rmw layer to prevent them from being called. However, this
* method will not block and wait until the currently executing on_new_[message]event callbacks
* are finished.
*/
void
disable_callbacks() override
{
SubscriptionBase::disable_callbacks();
any_callback_.disable();
if (subscription_intra_process_) {
subscription_intra_process_->disable_callbacks();
}
for (const auto & [_, event_ptr] : event_handlers_) {
if (event_ptr) {
event_ptr->disable();
}
}
}
/// Enable the callbacks to be called
/**
* This method is thread safe, and provides a safe way to atomically enable the callbacks
* in a multithreaded environment.
*/
void
enable_callbacks() override
{
SubscriptionBase::enable_callbacks();
any_callback_.enable();
if (subscription_intra_process_) {
subscription_intra_process_->enable_callbacks();
}
for (const auto & [_, event_ptr] : event_handlers_) {
if (event_ptr) {
event_ptr->enable();
}
}
}
void
handle_message(
std::shared_ptr<void> & message,

View File

@@ -212,6 +212,23 @@ public:
std::shared_ptr<rclcpp::SerializedMessage>
create_serialized_message() = 0;
/// Disable callbacks from being called
/**
* This function temporary removes the on_new_message_callback to prevent it from being called.
*/
RCLCPP_PUBLIC
virtual
void disable_callbacks();
/// Enable the callbacks to be called
/**
* This function sets back the on_new_message_callback if it was previously removed in
* disable_callbacks().
*/
RCLCPP_PUBLIC
virtual
void enable_callbacks();
/// Check if we need to handle the message, and execute the callback if we do.
/**
* \param[in] message Shared pointer to the message to handle.
@@ -383,7 +400,7 @@ public:
}
};
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
@@ -406,7 +423,7 @@ public:
void
clear_on_new_message_callback()
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
if (on_new_message_callback_) {
set_on_new_message_callback(nullptr, nullptr);
@@ -646,7 +663,7 @@ protected:
std::shared_ptr<rcl_node_t> node_handle_;
std::recursive_mutex callback_mutex_;
std::recursive_mutex on_new_message_callback_mutex_;
// It is important to declare on_new_message_callback_ before
// subscription_handle_, so on destruction the subscription is
// destroyed first. Otherwise, the rmw subscription callback

View File

@@ -33,12 +33,11 @@ class Time
public:
/// Time constructor
/**
* Initializes the time values for seconds and nanoseconds individually.
* Large values for nanoseconds are wrapped automatically with the remainder added to seconds.
* Both inputs must be integers.
* Indicates a specific point in time, relative to a clock's 0 point (its epoch).
* The total time since the epoch is given by seconds + nanoseconds.
*
* \param seconds part of the time in seconds since time epoch
* \param nanoseconds part of the time in nanoseconds since time epoch
* \param seconds the seconds component, valid only if positive
* \param nanoseconds the nanoseconds component, to be added to the seconds component
* \param clock_type clock type
* \throws std::runtime_error if seconds are negative
*/
@@ -47,7 +46,7 @@ public:
/// Time constructor
/**
* \param nanoseconds since time epoch
* \param nanoseconds the total time since the epoch in nanoseconds
* \param clock_type clock type
* \throws std::runtime_error if nanoseconds are negative
*/
@@ -190,7 +189,7 @@ public:
/// Get the nanoseconds since epoch
/**
* \return the nanoseconds since epoch as a rcl_time_point_value_t structure.
* \return the total time since the epoch in nanoseconds, as a rcl_time_point_value_t structure.
*/
RCLCPP_PUBLIC
rcl_time_point_value_t
@@ -209,7 +208,7 @@ public:
* \warning Depending on sizeof(double) there could be significant precision loss.
* When an exact time is required use nanoseconds() instead.
*
* \return the seconds since epoch as a floating point number.
* \return the total time since the epoch in seconds, as a floating point number.
*/
RCLCPP_PUBLIC
double

View File

@@ -16,6 +16,7 @@
#define RCLCPP__TYPE_ADAPTER_HPP_
#include <type_traits>
#include <new>
namespace rclcpp
{
@@ -128,6 +129,24 @@ struct assert_type_pair_is_specialized_type_adapter
"No type adapter for this custom type/ros message type pair");
};
template<typename, typename = void>
struct has_overloaded_operator_new : std::false_type {};
template<typename T>
struct has_overloaded_operator_new<T, std::void_t<
decltype(T::operator new(std::size_t()))
>>: std::true_type {};
template<typename, typename = void>
struct has_overloaded_aligned_operator_new : std::false_type {};
template<typename T>
struct has_overloaded_aligned_operator_new<T,
std::void_t<decltype( T::operator new(std::size_t(), std::align_val_t()) )>>
: std::true_type {};
template<typename T>
inline constexpr bool has_overloaded_operator_new_v = has_overloaded_operator_new<T>::value ||
has_overloaded_aligned_operator_new<T>::value;
} // namespace detail
/// Template metafunction that can make the type being adapted explicit.

View File

@@ -29,26 +29,48 @@
namespace rclcpp
{
/// Load the type support library for the given type.
/**
* \param[in] type The topic type, e.g. "std_msgs/msg/String"
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
* \return A shared library
*/
/// \brief Extract the package name, middle module, and type name from a full type string.
/// \details This function takes a full type string (e.g., "std_msgs/msg/String") and extracts
/// the package name, middle module (if any), and type name. The middle module is the part
/// between the package name and the type name, which is typically used for message types.
/// For example, for "std_msgs/msg/String", it returns ("std_msgs", "msg", "String").
/// \param[in] full_type
/// \throws std::runtime_error if the input full type string is malformed or does not follow the
/// expected format.
/// \return A tuple containing the package name, middle module (if any), and type name.
RCLCPP_PUBLIC
std::tuple<std::string, std::string, std::string>
extract_type_identifier(const std::string & full_type);
/// \brief Look for the library in the ament prefix paths and return the path to the type support
/// library.
/// \param[in] package_name The name of the package containing the type support library,
/// e.g. "std_msgs".
/// \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
/// \throws std::runtime_error if the library is not found.
/// \return The path to the type support library.
RCLCPP_PUBLIC
std::string get_typesupport_library_path(
const std::string & package_name, const std::string & typesupport_identifier);
/// \brief Load the type support library for the given type.
/// \param[in] type The topic type, e.g. "std_msgs/msg/String"
/// \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
/// \throws std::runtime_error if the library is not found or cannot be loaded.
/// \return A shared library
RCLCPP_PUBLIC
std::shared_ptr<rcpputils::SharedLibrary>
get_typesupport_library(const std::string & type, const std::string & typesupport_identifier);
/// Extract the message type support handle from the library.
/**
* The library needs to match the topic type. The shared library must stay loaded for the lifetime of the result.
*
* \param[in] type The topic type, e.g. "std_msgs/msg/String"
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
* \param[in] library The shared type support library
* \throws std::runtime_error if the symbol of type not found in the library.
* \return A message type support handle
*/
/// \brief Extracts the message type support handle from the library.
/// \note The library needs to match the topic type. The shared library must stay loaded for the
/// lifetime of the result.
/// \param[in] type The topic type, e.g. "std_msgs/msg/String"
/// \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
/// \param[in] library The shared type support library
/// \throws std::runtime_error if the symbol of type not found in the library.
/// \return A message type support handle
RCLCPP_PUBLIC
const rosidl_message_type_support_t *
get_message_typesupport_handle(
@@ -56,16 +78,14 @@ get_message_typesupport_handle(
const std::string & typesupport_identifier,
rcpputils::SharedLibrary & library);
/// Extract the service type support handle from the library.
/**
* The library needs to match the service type. The shared library must stay loaded for the lifetime of the result.
*
* \param[in] type The service type, e.g. "std_srvs/srv/Empty"
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
* \param[in] library The shared type support library
* \throws std::runtime_error if the symbol of type not found in the library.
* \return A service type support handle
*/
/// \brief Extracts the service type support handle from the library.
/// \note The library needs to match the service type. The shared library must stay loaded for the
/// lifetime of the result.
/// \param[in] type The service type, e.g. "std_srvs/srv/Empty"
/// \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
/// \param[in] library The shared type support library
/// \throws std::runtime_error if the symbol of type not found in the library.
/// \return A service type support handle
RCLCPP_PUBLIC
const rosidl_service_type_support_t *
get_service_typesupport_handle(
@@ -73,17 +93,14 @@ get_service_typesupport_handle(
const std::string & typesupport_identifier,
rcpputils::SharedLibrary & library);
/// Extract the action type support handle from the library.
/**
* The library needs to match the action type. The shared library must stay loaded for the lifetime
* of the result.
*
* \param[in] type The action type, e.g. "example_interfaces/action/Fibonacci"
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
* \param[in] library The shared type support library
* \throws std::runtime_error if the symbol of type not found in the library.
* \return A action type support handle
*/
/// \brief Extracts the action type support handle from the library.
/// \note The library needs to match the action type. The shared library must stay loaded for the
/// lifetime of the result.
/// \param[in] type The action type, e.g. "example_interfaces/action/Fibonacci"
/// \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
/// \param[in] library The shared type support library
/// \throws std::runtime_error if the symbol of type not found in the library.
/// \return A action type support handle
RCLCPP_PUBLIC
const rosidl_action_type_support_t *
get_action_typesupport_handle(

View File

@@ -15,6 +15,7 @@
#ifndef RCLCPP__WAIT_FOR_MESSAGE_HPP_
#define RCLCPP__WAIT_FOR_MESSAGE_HPP_
#include <future>
#include <memory>
#include <string>
@@ -23,6 +24,7 @@
#include "rclcpp/node.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/wait_set.hpp"
#include "rclcpp/qos.hpp"
namespace rclcpp
{
@@ -79,10 +81,11 @@ bool wait_for_message(
/**
* Wait for the next incoming message to arrive on a specified topic before the specified timeout.
*
* \param[out] out is the message to be filled when a new message is arriving.
* \param[out] out is the message to be filled when a new message is arriving
* \param[in] node the node pointer to initialize the subscription on.
* \param[in] topic the topic to wait for messages.
* \param[in] time_to_wait parameter specifying the timeout before returning.
* \param[in] qos parameter specifying QoS settings for the subscription.
* \return true if a message was successfully received, false if message could not
* be obtained or shutdown was triggered asynchronously on the context.
*/
@@ -91,9 +94,10 @@ bool wait_for_message(
MsgT & out,
rclcpp::Node::SharedPtr node,
const std::string & topic,
std::chrono::duration<Rep, Period> time_to_wait = std::chrono::duration<Rep, Period>(-1))
std::chrono::duration<Rep, Period> time_to_wait = std::chrono::duration<Rep, Period>(-1),
const rclcpp::QoS & qos = rclcpp::SystemDefaultsQoS())
{
auto sub = node->create_subscription<MsgT>(topic, 1, [](const std::shared_ptr<const MsgT>) {});
auto sub = node->create_subscription<MsgT>(topic, qos, [](const std::shared_ptr<const MsgT>) {});
return wait_for_message<MsgT, Rep, Period>(
out, sub, node->get_node_options().context(), time_to_wait);
}

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp</name>
<version>29.6.0</version>
<version>30.1.4</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
@@ -17,6 +17,7 @@
<buildtool_depend>ament_cmake_ros</buildtool_depend>
<buildtool_depend>ament_cmake_gen_version_h</buildtool_depend>
<buildtool_depend>python3</buildtool_depend>
<buildtool_depend>python3-empy</buildtool_depend>
<build_depend>ament_index_cpp</build_depend>
<build_depend>builtin_interfaces</build_depend>
@@ -46,9 +47,9 @@
<depend>statistics_msgs</depend>
<depend>tracetools</depend>
<test_depend>ament_cmake_gmock</test_depend>
<test_depend>ament_cmake_google_benchmark</test_depend>
<test_depend>ament_cmake_gtest</test_depend>
<test_depend>ament_cmake_ros</test_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>mimick_vendor</test_depend>

View File

@@ -1,165 +0,0 @@
// generated from rclcpp/resource/logging.hpp.em
// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__LOGGING_HPP_
#define RCLCPP__LOGGING_HPP_
#include <sstream>
#include <type_traits>
#include "rclcpp/logger.hpp"
#include "rcutils/logging_macros.h"
#include "rclcpp/utilities.hpp"
// These are used for compiling out logging macros lower than a minimum severity.
#define RCLCPP_LOG_MIN_SEVERITY_DEBUG 0
#define RCLCPP_LOG_MIN_SEVERITY_INFO 1
#define RCLCPP_LOG_MIN_SEVERITY_WARN 2
#define RCLCPP_LOG_MIN_SEVERITY_ERROR 3
#define RCLCPP_LOG_MIN_SEVERITY_FATAL 4
#define RCLCPP_LOG_MIN_SEVERITY_NONE 5
#define RCLCPP_FIRST_ARG(N, ...) N
#define RCLCPP_ALL_BUT_FIRST_ARGS(N, ...) __VA_ARGS__
/**
* \def RCLCPP_LOG_MIN_SEVERITY
* Define RCLCPP_LOG_MIN_SEVERITY=RCLCPP_LOG_MIN_SEVERITY_[DEBUG|INFO|WARN|ERROR|FATAL]
* in your build options to compile out anything below that severity.
* Use RCLCPP_LOG_MIN_SEVERITY_NONE to compile out all macros.
*/
#ifndef RCLCPP_LOG_MIN_SEVERITY
#define RCLCPP_LOG_MIN_SEVERITY RCLCPP_LOG_MIN_SEVERITY_DEBUG
#endif
@{
from collections import OrderedDict
from copy import deepcopy
from rcutils.logging import feature_combinations
from rcutils.logging import get_suffix_from_features
from rcutils.logging import severities
from rcutils.logging import throttle_args
from rcutils.logging import throttle_params
throttle_args['condition_before'] = 'RCUTILS_LOG_CONDITION_THROTTLE_BEFORE(clock, duration)'
del throttle_params['get_time_point_value']
throttle_params['clock'] = 'rclcpp::Clock that will be used to get the time point.'
throttle_params.move_to_end('clock', last=False)
rclcpp_feature_combinations = OrderedDict()
for combinations, feature in feature_combinations.items():
# skip feature combinations using 'named'
if 'named' in combinations:
continue
rclcpp_feature_combinations[combinations] = feature
# add a stream variant for each available feature combination
stream_arg = 'stream_arg'
for combinations, feature in list(rclcpp_feature_combinations.items()):
combinations = ('stream', ) + combinations
feature = deepcopy(feature)
feature.params[stream_arg] = 'The argument << into a stringstream'
rclcpp_feature_combinations[combinations] = feature
def get_rclcpp_suffix_from_features(features):
suffix = get_suffix_from_features(features)
if 'stream' in features:
suffix = '_STREAM' + suffix
return suffix
}@
@[for severity in severities]@
/** @@name Logging macros for severity @(severity).
*/
///@@{
#if (RCLCPP_LOG_MIN_SEVERITY > RCLCPP_LOG_MIN_SEVERITY_@(severity))
// empty logging macros for severity @(severity) when being disabled at compile time
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_@(severity)@(suffix)(...)
@[ end for]@
#else
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
// The RCLCPP_@(severity)@(suffix) macro is surrounded by do { .. } while (0)
// to implement the standard C macro idiom to make the macro safe in all
// contexts; see http://c-faq.com/cpp/multistmt.html for more information.
/**
* \def RCLCPP_@(severity)@(suffix)
* Log a message with severity @(severity)@
@[ if rclcpp_feature_combinations[feature_combination].doc_lines]@
with the following conditions:
@[ else]@
.
@[ end if]@
@[ for doc_line in rclcpp_feature_combinations[feature_combination].doc_lines]@
* @(doc_line)
@[ end for]@
* \param logger The `rclcpp::Logger` to use
@[ for param_name, doc_line in rclcpp_feature_combinations[feature_combination].params.items()]@
* \param @(param_name) @(doc_line)
@[ end for]@
@[ if 'stream' not in feature_combination]@
* \param ... The format string, followed by the variable arguments for the format string.
@[ end if]@
*/
@{params = rclcpp_feature_combinations[feature_combination].params.keys()}@
#define RCLCPP_@(severity)@(suffix)(logger@(''.join([', ' + p for p in params]))@
@[ if 'stream' not in feature_combination]@
, ...@
@[ end if]@
) \
do { \
static_assert( \
::std::is_same<typename std::remove_cv_t<typename std::remove_reference_t<decltype(logger)>>, \
typename ::rclcpp::Logger>::value, \
"First argument to logging macros must be an rclcpp::Logger"); \
@[ if 'throttle' in feature_combination]@ \
auto get_time_point = [&c=clock](rcutils_time_point_value_t * time_point) -> rcutils_ret_t { \
try { \
*time_point = c.now().nanoseconds(); \
} catch (...) { \
RCUTILS_SAFE_FWRITE_TO_STDERR( \
"[rclcpp|logging.hpp] RCLCPP_@(severity)@(suffix) could not get current time stamp\n"); \
return RCUTILS_RET_ERROR; \
} \
return RCUTILS_RET_OK; \
}; \
@[ end if] \
@[ if 'stream' in feature_combination]@
std::stringstream rclcpp_stream_ss_; \
rclcpp_stream_ss_ << @(stream_arg); \
@[ end if]@
RCUTILS_LOG_@(severity)@(get_suffix_from_features(feature_combination))_NAMED( \
@{params = ['get_time_point' if p == 'clock' and 'throttle' in feature_combination else p for p in params]}@
@[ if params]@
@(''.join([' ' + p + ', \\\n' for p in params if p != stream_arg]))@
@[ end if]@
(logger).get_name(), \
@[ if 'stream' not in feature_combination]@
__VA_ARGS__); \
@[ else]@
"%s", rclcpp_stream_ss_.str().c_str()); \
@[ end if]@
} while (0)
@[ end for]@
#endif
///@@}
@[end for]@
#endif // RCLCPP__LOGGING_HPP_

View File

@@ -28,7 +28,7 @@
#include "rclcpp/detail/utilities.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/graph_listener.hpp"
#include "rcutils/error_handling.h"
#include "rcutils/macros.h"
@@ -145,7 +145,8 @@ rclcpp_logging_output_handler(
Context::Context()
: rcl_context_(nullptr),
shutdown_reason_(""),
logging_mutex_(nullptr)
logging_mutex_(nullptr),
graph_listener_(nullptr)
{}
Context::~Context()
@@ -243,6 +244,24 @@ Context::init(
weak_contexts_ = get_weak_contexts();
weak_contexts_->add_context(this->shared_from_this());
std::lock_guard<std::recursive_mutex> lock (on_shutdown_callbacks_mutex_);
graph_listener_ = std::make_shared<graph_listener::GraphListener>(shared_from_this());
if (!graph_listener_->is_started()) {
// Register an on_shutdown hook to shutdown the graph listener.
// This is important to ensure that the wait set is finalized before
// destruction of static objects occurs.
std::weak_ptr<rclcpp::graph_listener::GraphListener> weak_graph_listener = graph_listener_;
on_shutdown ([weak_graph_listener]() {
auto shared_graph_listener = weak_graph_listener.lock();
if(shared_graph_listener) {
shared_graph_listener->shutdown(std::nothrow);
}
});
}
} catch (const std::exception & e) {
ret = rcl_shutdown(rcl_context_.get());
rcl_context_.reset();
@@ -310,9 +329,16 @@ Context::shutdown(const std::string & reason)
// call each pre-shutdown callback
{
std::lock_guard<std::mutex> lock{pre_shutdown_callbacks_mutex_};
for (const auto & callback : pre_shutdown_callbacks_) {
(*callback)();
std::lock_guard<std::recursive_mutex> lock{pre_shutdown_callbacks_mutex_};
// callbacks may delete other callbacks during the execution,
// therefore we need to save a copy and check before execution
// if the next callback is still present
auto cpy = pre_shutdown_callbacks_;
for (const auto & callback : cpy) {
auto it = std::find(pre_shutdown_callbacks_.begin(), pre_shutdown_callbacks_.end(), callback);
if(it != pre_shutdown_callbacks_.end()) {
(*callback)();
}
}
}
@@ -325,9 +351,16 @@ Context::shutdown(const std::string & reason)
shutdown_reason_ = reason;
// call each shutdown callback
{
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
for (const auto & callback : on_shutdown_callbacks_) {
(*callback)();
std::lock_guard<std::recursive_mutex> lock(on_shutdown_callbacks_mutex_);
// callbacks may delete other callbacks during the execution,
// therefore we need to save a copy and check before execution
// if the next callback is still present
auto cpy = on_shutdown_callbacks_;
for (const auto & callback : cpy) {
auto it = std::find(on_shutdown_callbacks_.begin(), on_shutdown_callbacks_.end(), callback);
if(it != on_shutdown_callbacks_.end()) {
(*callback)();
}
}
}
@@ -398,10 +431,10 @@ Context::add_shutdown_callback(
shutdown_type == ShutdownType::pre_shutdown || shutdown_type == ShutdownType::on_shutdown);
if constexpr (shutdown_type == ShutdownType::pre_shutdown) {
std::lock_guard<std::mutex> lock(pre_shutdown_callbacks_mutex_);
std::lock_guard<std::recursive_mutex> lock(pre_shutdown_callbacks_mutex_);
pre_shutdown_callbacks_.emplace_back(callback_shared_ptr);
} else {
std::lock_guard<std::mutex> lock(on_shutdown_callbacks_mutex_);
std::lock_guard<std::recursive_mutex> lock(on_shutdown_callbacks_mutex_);
on_shutdown_callbacks_.emplace_back(callback_shared_ptr);
}
@@ -421,7 +454,7 @@ Context::remove_shutdown_callback(
}
const auto remove_callback = [&callback_shared_ptr](auto & mutex, auto & callback_vector) {
const std::lock_guard<std::mutex> lock(mutex);
const std::lock_guard<std::recursive_mutex> lock(mutex);
auto iter = callback_vector.begin();
for (; iter != callback_vector.end(); iter++) {
if ((*iter).get() == callback_shared_ptr.get()) {
@@ -462,7 +495,7 @@ std::vector<rclcpp::Context::ShutdownCallback>
Context::get_shutdown_callback() const
{
const auto get_callback_vector = [](auto & mutex, auto & callback_set) {
const std::lock_guard<std::mutex> lock(mutex);
const std::lock_guard<std::recursive_mutex> lock(mutex);
std::vector<rclcpp::Context::ShutdownCallback> callbacks;
for (auto & callback : callback_set) {
callbacks.push_back(*callback);
@@ -486,6 +519,12 @@ Context::get_rcl_context()
return rcl_context_;
}
std::shared_ptr<rclcpp::graph_listener::GraphListener>
Context::get_graph_listener()
{
return graph_listener_;
}
bool
Context::sleep_for(const std::chrono::nanoseconds & nanoseconds)
{

View File

@@ -18,6 +18,7 @@
#include <iterator>
#include <memory>
#include <map>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
@@ -52,6 +53,7 @@ class rclcpp::ExecutorImplementation {};
Executor::Executor(const std::shared_ptr<rclcpp::Context> & context)
: spinning(false),
context_(context),
entities_need_rebuild_(true),
collector_(nullptr),
wait_set_({}, {}, {}, {}, {}, {}, context)
@@ -186,6 +188,11 @@ Executor::add_callback_group(
void
Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
if (node_ptr->get_context() != context_) {
throw std::runtime_error(
"add_node() called with a node with a different context from this executor");
}
this->collector_.add_node(node_ptr);
try {

View File

@@ -13,6 +13,7 @@
// limitations under the License.
#include "rclcpp/executors.hpp"
#include "rcpputils/compile_warnings.hpp"
void
rclcpp::spin_all(
@@ -25,12 +26,6 @@ rclcpp::spin_all(
exec.spin_node_all(node_ptr, max_duration);
}
void
rclcpp::spin_all(rclcpp::Node::SharedPtr node_ptr, std::chrono::nanoseconds max_duration)
{
rclcpp::spin_all(node_ptr->get_node_base_interface(), max_duration);
}
void
rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
@@ -40,12 +35,6 @@ rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr
exec.spin_node_some(node_ptr);
}
void
rclcpp::spin_some(rclcpp::Node::SharedPtr node_ptr)
{
rclcpp::spin_some(node_ptr->get_node_base_interface());
}
void
rclcpp::spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
@@ -62,3 +51,19 @@ rclcpp::spin(rclcpp::Node::SharedPtr node_ptr)
{
rclcpp::spin(node_ptr->get_node_base_interface());
}
void
rclcpp::spin_all(rclcpp::Node::SharedPtr node_ptr, std::chrono::nanoseconds max_duration)
{
RCPPUTILS_DEPRECATION_WARNING_OFF_START
rclcpp::spin_all(node_ptr->get_node_base_interface(), max_duration);
RCPPUTILS_DEPRECATION_WARNING_OFF_STOP
}
void
rclcpp::spin_some(rclcpp::Node::SharedPtr node_ptr)
{
RCPPUTILS_DEPRECATION_WARNING_OFF_START
rclcpp::spin_some(node_ptr->get_node_base_interface());
RCPPUTILS_DEPRECATION_WARNING_OFF_STOP
}

View File

@@ -1,223 +0,0 @@
// Copyright 2019 Nobleo Technology
//
// 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/executors/executor_entities_collection.hpp"
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/executors/static_single_threaded_executor.hpp"
#include "rclcpp/any_executable.hpp"
using rclcpp::executors::StaticSingleThreadedExecutor;
StaticSingleThreadedExecutor::StaticSingleThreadedExecutor(const rclcpp::ExecutorOptions & options)
: rclcpp::Executor(options)
{
}
StaticSingleThreadedExecutor::~StaticSingleThreadedExecutor() {}
void
StaticSingleThreadedExecutor::spin()
{
if (spinning.exchange(true)) {
throw std::runtime_error("spin() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false); );
// This is essentially the contents of the rclcpp::Executor::wait_for_work method,
// except we need to keep the wait result to reproduce the StaticSingleThreadedExecutor
// behavior.
while (rclcpp::ok(this->context_) && spinning.load()) {
this->spin_once_impl(std::chrono::nanoseconds(-1));
}
}
void
StaticSingleThreadedExecutor::spin_some(std::chrono::nanoseconds max_duration)
{
// In this context a 0 input max_duration means no duration limit
if (std::chrono::nanoseconds(0) == max_duration) {
max_duration = std::chrono::nanoseconds::max();
}
return this->spin_some_impl(max_duration, false);
}
void
StaticSingleThreadedExecutor::spin_all(std::chrono::nanoseconds max_duration)
{
if (max_duration < std::chrono::nanoseconds(0)) {
throw std::invalid_argument("max_duration must be greater than or equal to 0");
}
return this->spin_some_impl(max_duration, true);
}
void
StaticSingleThreadedExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
{
auto start = std::chrono::steady_clock::now();
auto max_duration_not_elapsed = [max_duration, start]() {
const auto spin_forever = std::chrono::nanoseconds(0) == max_duration;
const auto cur_duration = std::chrono::steady_clock::now() - start;
return spin_forever || (cur_duration < max_duration);
};
if (spinning.exchange(true)) {
throw std::runtime_error("spin_some() called while already spinning");
}
RCPPUTILS_SCOPE_EXIT(this->spinning.store(false););
while (rclcpp::ok(context_) && spinning.load() && max_duration_not_elapsed()) {
// Get executables that are ready now
std::lock_guard<std::mutex> guard(mutex_);
auto wait_result = this->collect_and_wait(std::chrono::nanoseconds(0));
if (wait_result.has_value()) {
// Execute ready executables
bool work_available = this->execute_ready_executables(
current_collection_,
wait_result.value(),
false);
if (!work_available || !exhaustive) {
break;
}
}
}
}
void
StaticSingleThreadedExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
{
if (rclcpp::ok(context_) && spinning.load()) {
std::lock_guard<std::mutex> guard(mutex_);
auto wait_result = this->collect_and_wait(timeout);
if (wait_result.has_value()) {
this->execute_ready_executables(current_collection_, wait_result.value(), true);
}
}
}
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",
"empty wait set received in wait(). This should never happen.");
return {};
} else {
if (wait_result.kind() == WaitResultKind::Ready && current_notify_waitable_) {
auto & rcl_wait_set = wait_result.get_wait_set().get_rcl_wait_set();
if (current_notify_waitable_->is_ready(rcl_wait_set)) {
current_notify_waitable_->execute(current_notify_waitable_->take_data());
}
}
}
return wait_result;
}
// This preserves the "scheduling semantics" of the StaticSingleThreadedExecutor
// from the original implementation.
bool StaticSingleThreadedExecutor::execute_ready_executables(
const rclcpp::executors::ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
bool spin_once)
{
bool any_ready_executable = false;
if (wait_result.kind() != rclcpp::WaitResultKind::Ready) {
return any_ready_executable;
}
while (auto subscription = wait_result.next_ready_subscription()) {
auto entity_iter = collection.subscriptions.find(subscription->get_subscription_handle().get());
if (entity_iter != collection.subscriptions.end()) {
execute_subscription(subscription);
any_ready_executable = true;
if (spin_once) {return any_ready_executable;}
}
}
size_t current_timer_index = 0;
while (true) {
auto [timer, timer_index] = wait_result.peek_next_ready_timer(current_timer_index);
if (nullptr == timer) {
break;
}
current_timer_index = timer_index;
auto entity_iter = collection.timers.find(timer->get_timer_handle().get());
if (entity_iter != collection.timers.end()) {
wait_result.clear_timer_with_index(current_timer_index);
auto data = timer->call();
if (!data) {
// someone canceled the timer between is_ready and call
continue;
}
execute_timer(std::move(timer), data);
any_ready_executable = true;
if (spin_once) {return any_ready_executable;}
}
}
while (auto client = wait_result.next_ready_client()) {
auto entity_iter = collection.clients.find(client->get_client_handle().get());
if (entity_iter != collection.clients.end()) {
execute_client(client);
any_ready_executable = true;
if (spin_once) {return any_ready_executable;}
}
}
while (auto service = wait_result.next_ready_service()) {
auto entity_iter = collection.services.find(service->get_service_handle().get());
if (entity_iter != collection.services.end()) {
execute_service(service);
any_ready_executable = true;
if (spin_once) {return any_ready_executable;}
}
}
while (auto waitable = wait_result.next_ready_waitable()) {
auto entity_iter = collection.waitables.find(waitable.get());
if (entity_iter != collection.waitables.end()) {
const auto data = waitable->take_data();
waitable->execute(data);
any_ready_executable = true;
if (spin_once) {return any_ready_executable;}
}
}
return any_ready_executable;
}

View File

@@ -25,9 +25,9 @@ using namespace std::chrono_literals;
using rclcpp::experimental::executors::EventsExecutor;
EventsExecutor::EventsExecutor(
const rclcpp::ExecutorOptions & options,
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue,
bool execute_timers_separate_thread,
const rclcpp::ExecutorOptions & options)
bool execute_timers_separate_thread)
: rclcpp::Executor(options)
{
// Get ownership of the queue used to store events.

View File

@@ -37,6 +37,30 @@ GenericSubscription::create_serialized_message()
return std::make_shared<rclcpp::SerializedMessage>(0);
}
void
GenericSubscription::disable_callbacks()
{
SubscriptionBase::disable_callbacks();
any_callback_.disable();
for (const auto & [_, event_ptr] : event_handlers_) {
if (event_ptr) {
event_ptr->disable();
}
}
}
void
GenericSubscription::enable_callbacks()
{
SubscriptionBase::enable_callbacks();
any_callback_.enable();
for (const auto & [_, event_ptr] : event_handlers_) {
if (event_ptr) {
event_ptr->enable();
}
}
}
void
GenericSubscription::handle_message(
std::shared_ptr<void> &,

View File

@@ -19,7 +19,6 @@
#include <memory>
#include <string>
#include <vector>
#include "rcl/error_handling.h"
#include "rcl/types.h"
#include "rclcpp/detail/add_guard_condition_to_rcl_wait_set.hpp"
@@ -38,8 +37,7 @@ namespace graph_listener
{
GraphListener::GraphListener(const std::shared_ptr<Context> & parent_context)
: weak_parent_context_(parent_context),
rcl_parent_context_(parent_context->get_rcl_context()),
: rcl_parent_context_(parent_context->get_rcl_context()),
is_started_(false),
is_shutdown_(false),
interrupt_guard_condition_(parent_context)
@@ -72,23 +70,11 @@ void
GraphListener::start_if_not_started()
{
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (is_shutdown_.load()) {
if (is_shutdown()) {
throw GraphListenerShutdownError();
}
auto parent_context = weak_parent_context_.lock();
if (!is_started_ && parent_context) {
// Register an on_shutdown hook to shtudown the graph listener.
// This is important to ensure that the wait set is finalized before
// destruction of static objects occurs.
std::weak_ptr<GraphListener> weak_this = shared_from_this();
parent_context->on_shutdown(
[weak_this]() {
auto shared_this = weak_this.lock();
if (shared_this) {
// should not throw from on_shutdown if it can be avoided
shared_this->shutdown(std::nothrow);
}
});
if (!is_started()) {
// Initialize the wait set before starting.
init_wait_set();
// Start the listener thread.
@@ -122,7 +108,7 @@ GraphListener::run_loop()
{
while (true) {
// If shutdown() was called, exit.
if (is_shutdown_.load()) {
if (is_shutdown()) {
return;
}
rcl_ret_t ret;
@@ -190,7 +176,7 @@ GraphListener::run_loop()
if (graph_gc == wait_set_.guard_conditions[graph_gc_indexes[i]]) {
node_ptr->notify_graph_change();
}
if (is_shutdown_) {
if (is_shutdown()) {
// If shutdown, then notify the node of this as well.
node_ptr->notify_shutdown();
}
@@ -257,7 +243,7 @@ GraphListener::add_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph
throw std::invalid_argument("node is nullptr");
}
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (is_shutdown_.load()) {
if (is_shutdown()) {
throw GraphListenerShutdownError();
}
@@ -332,11 +318,11 @@ GraphListener::__shutdown()
{
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (!is_shutdown_.exchange(true)) {
if (is_started_) {
if (is_started()) {
interrupt_(&interrupt_guard_condition_);
listener_thread_.join();
}
if (is_started_) {
if (is_started()) {
cleanup_wait_set();
}
}
@@ -365,6 +351,12 @@ GraphListener::shutdown(const std::nothrow_t &) noexcept
}
}
bool
GraphListener::is_started()
{
return is_started_;
}
bool
GraphListener::is_shutdown()
{

View File

@@ -547,6 +547,18 @@ Node::get_subscriptions_info_by_topic(const std::string & topic_name, bool no_ma
return node_graph_->get_subscriptions_info_by_topic(topic_name, no_mangle);
}
std::vector<rclcpp::ServiceEndpointInfo>
Node::get_clients_info_by_service(const std::string & service_name, bool no_mangle) const
{
return node_graph_->get_clients_info_by_service(service_name, no_mangle);
}
std::vector<rclcpp::ServiceEndpointInfo>
Node::get_servers_info_by_service(const std::string & service_name, bool no_mangle) const
{
return node_graph_->get_servers_info_by_service(service_name, no_mangle);
}
void
Node::for_each_callback_group(
const node_interfaces::NodeBaseInterface::CallbackGroupFunction & func)

View File

@@ -275,16 +275,6 @@ NodeBase::get_associated_with_executor_atomic()
return associated_with_executor_;
}
rclcpp::GuardCondition &
NodeBase::get_notify_guard_condition()
{
std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
if (!notify_guard_condition_is_valid_) {
throw std::runtime_error("failed to get notify guard condition because it is invalid");
}
return *notify_guard_condition_;
}
rclcpp::GuardCondition::SharedPtr
NodeBase::get_shared_notify_guard_condition()
{

View File

@@ -15,6 +15,7 @@
#include "rclcpp/node_interfaces/node_graph.hpp"
#include <algorithm>
#include <cstddef>
#include <map>
#include <string>
#include <tuple>
@@ -36,9 +37,6 @@ using rclcpp::graph_listener::GraphListener;
NodeGraph::NodeGraph(rclcpp::node_interfaces::NodeBaseInterface * node_base)
: node_base_(node_base),
graph_listener_(
node_base->get_context()->get_sub_context<GraphListener>(node_base->get_context())
),
should_add_to_graph_listener_(true),
graph_users_count_(0)
{}
@@ -50,7 +48,7 @@ NodeGraph::~NodeGraph()
// graph listener after checking that it was not here.
if (!should_add_to_graph_listener_.exchange(false)) {
// If it was already false, then it needs to now be removed.
graph_listener_->remove_node(this);
node_base_->get_context()->get_graph_listener()->remove_node(this);
}
}
@@ -419,7 +417,7 @@ NodeGraph::get_node_names_and_namespaces() const
rcl_get_error_string().str;
rcl_reset_error();
}
// TODO(karsten1987): Append rcutils_error_message once it's in master
RCUTILS_LOG_ERROR_NAMED("rclcpp", "%s", error_msg.c_str());
throw std::runtime_error(error_msg);
}
@@ -434,20 +432,15 @@ NodeGraph::get_node_names_and_namespaces() const
std::string error;
rcl_ret_t ret_names = rcutils_string_array_fini(&node_names_c);
if (ret_names != RCUTILS_RET_OK) {
// *INDENT-OFF*
// TODO(karsten1987): Append rcutils_error_message once it's in master
error = "could not destroy node names";
// *INDENT-ON*
error = std::string("could not destroy node names: ") + rcl_get_error_string().str;
}
rcl_ret_t ret_ns = rcutils_string_array_fini(&node_namespaces_c);
if (ret_ns != RCUTILS_RET_OK) {
// *INDENT-OFF*
// TODO(karsten1987): Append rcutils_error_message once it's in master
error += ", could not destroy node namespaces";
// *INDENT-ON*
error += std::string(", could not destroy node namespaces: ") + rcl_get_error_string().str;
}
if (ret_names != RCUTILS_RET_OK || ret_ns != RCUTILS_RET_OK) {
RCUTILS_LOG_ERROR_NAMED("rclcpp", "%s", error.c_str());
throw std::runtime_error(error);
}
@@ -603,8 +596,8 @@ NodeGraph::get_graph_event()
}
// on first call, add node to graph_listener_
if (should_add_to_graph_listener_.exchange(false)) {
graph_listener_->add_node(this);
graph_listener_->start_if_not_started();
node_base_->get_context()->get_graph_listener()->add_node(this);
node_base_->get_context()->get_graph_listener()->start_if_not_started();
}
return event;
}
@@ -761,6 +754,122 @@ NodeGraph::get_subscriptions_info_by_topic(
rcl_get_subscriptions_info_by_topic);
}
static
std::vector<rclcpp::ServiceEndpointInfo>
convert_to_service_info_list(const rcl_service_endpoint_info_array_t & info_array)
{
std::vector<rclcpp::ServiceEndpointInfo> service_info_list;
for (size_t i = 0; i < info_array.size; ++i) {
service_info_list.push_back(rclcpp::ServiceEndpointInfo(info_array.info_array[i]));
}
return service_info_list;
}
template<const char * EndpointType, typename FunctionT>
static std::vector<rclcpp::ServiceEndpointInfo>
get_info_by_service(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & service_name,
bool no_mangle,
FunctionT rcl_get_info_by_service)
{
std::string fqdn;
auto rcl_node_handle = node_base->get_rcl_node_handle();
if (no_mangle) {
fqdn = service_name;
} else {
fqdn = rclcpp::expand_topic_or_service_name(
service_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
true);
// Get the node options
const rcl_node_options_t * node_options = rcl_node_get_options(rcl_node_handle);
if (nullptr == node_options) {
throw std::runtime_error("Need valid node options in get_info_by_service()");
}
const rcl_arguments_t * global_args = nullptr;
if (node_options->use_global_arguments) {
global_args = &(rcl_node_handle->context->global_arguments);
}
char * remapped_service_name = nullptr;
rcl_ret_t ret = rcl_remap_service_name(
&(node_options->arguments),
global_args,
fqdn.c_str(),
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
node_options->allocator,
&remapped_service_name);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, std::string("Failed to remap service name ") + fqdn);
} else if (nullptr != remapped_service_name) {
fqdn = remapped_service_name;
node_options->allocator.deallocate(remapped_service_name, node_options->allocator.state);
}
}
rcutils_allocator_t allocator = rcutils_get_default_allocator();
rcl_service_endpoint_info_array_t info_array =
rcl_get_zero_initialized_service_endpoint_info_array();
rcl_ret_t ret =
rcl_get_info_by_service(rcl_node_handle, &allocator, fqdn.c_str(), no_mangle, &info_array);
if (RCL_RET_OK != ret) {
auto error_msg =
std::string("Failed to get information by service for ") + EndpointType + std::string(":");
if (RCL_RET_UNSUPPORTED == ret) {
error_msg += std::string("function not supported by RMW_IMPLEMENTATION");
} else {
error_msg += rcl_get_error_string().str;
}
rcl_reset_error();
if (RCL_RET_OK != rcl_service_endpoint_info_array_fini(&info_array, &allocator)) {
error_msg += std::string(", failed also to cleanup service info array, leaking memory: ") +
rcl_get_error_string().str;
rcl_reset_error();
}
throw_from_rcl_error(ret, error_msg);
}
std::vector<rclcpp::ServiceEndpointInfo> service_info_list =
convert_to_service_info_list(info_array);
ret = rcl_service_endpoint_info_array_fini(&info_array, &allocator);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "rcl_service_info_array_fini failed.");
}
return service_info_list;
}
static constexpr char kClientEndpointTypeName[] = "clients";
std::vector<rclcpp::ServiceEndpointInfo>
NodeGraph::get_clients_info_by_service(
const std::string & service_name,
bool no_mangle) const
{
return get_info_by_service<kClientEndpointTypeName>(
node_base_,
service_name,
no_mangle,
rcl_get_clients_info_by_service);
}
static constexpr char kServerEndpointTypeName[] = "servers";
std::vector<rclcpp::ServiceEndpointInfo>
NodeGraph::get_servers_info_by_service(
const std::string & service_name,
bool no_mangle) const
{
return get_info_by_service<kServerEndpointTypeName>(
node_base_,
service_name,
no_mangle,
rcl_get_servers_info_by_service);
}
std::string &
rclcpp::TopicEndpointInfo::node_name()
{
@@ -844,3 +953,99 @@ rclcpp::TopicEndpointInfo::topic_type_hash() const
{
return topic_type_hash_;
}
std::string &
rclcpp::ServiceEndpointInfo::node_name()
{
return node_name_;
}
const std::string &
rclcpp::ServiceEndpointInfo::node_name() const
{
return node_name_;
}
std::string &
rclcpp::ServiceEndpointInfo::node_namespace()
{
return node_namespace_;
}
const std::string &
rclcpp::ServiceEndpointInfo::node_namespace() const
{
return node_namespace_;
}
std::string &
rclcpp::ServiceEndpointInfo::service_type()
{
return service_type_;
}
const std::string &
rclcpp::ServiceEndpointInfo::service_type() const
{
return service_type_;
}
rclcpp::EndpointType &
rclcpp::ServiceEndpointInfo::endpoint_type()
{
return endpoint_type_;
}
const rclcpp::EndpointType &
rclcpp::ServiceEndpointInfo::endpoint_type() const
{
return endpoint_type_;
}
size_t &
rclcpp::ServiceEndpointInfo::endpoint_count()
{
return endpoint_count_;
}
const size_t &
rclcpp::ServiceEndpointInfo::endpoint_count() const
{
return endpoint_count_;
}
std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
rclcpp::ServiceEndpointInfo::endpoint_gids()
{
return endpoint_gids_;
}
const std::vector<std::array<uint8_t, RMW_GID_STORAGE_SIZE>> &
rclcpp::ServiceEndpointInfo::endpoint_gids() const
{
return endpoint_gids_;
}
std::vector<rclcpp::QoS> &
rclcpp::ServiceEndpointInfo::qos_profiles()
{
return qos_profiles_;
}
const std::vector<rclcpp::QoS> &
rclcpp::ServiceEndpointInfo::qos_profiles() const
{
return qos_profiles_;
}
rosidl_type_hash_t &
rclcpp::ServiceEndpointInfo::service_type_hash()
{
return service_type_hash_;
}
const rosidl_type_hash_t &
rclcpp::ServiceEndpointInfo::service_type_hash() const
{
return service_type_hash_;
}

View File

@@ -65,11 +65,11 @@ public:
: logger_(node_logging->get_logger()),
node_base_(node_base)
{
rclcpp::ParameterValue enable_param;
const std::string enable_param_name = "start_type_description_service";
bool enabled = false;
try {
auto enable_param = node_parameters->declare_parameter(
if (!node_parameters->has_parameter(enable_param_name)) {
enable_param = node_parameters->declare_parameter(
enable_param_name,
rclcpp::ParameterValue(true),
rcl_interfaces::msg::ParameterDescriptor()
@@ -77,13 +77,21 @@ public:
.set__type(rclcpp::PARAMETER_BOOL)
.set__description("Start the ~/get_type_description service for this node.")
.set__read_only(true));
enabled = enable_param.get<bool>();
} catch (const rclcpp::exceptions::InvalidParameterTypeException & exc) {
RCLCPP_ERROR(logger_, "%s", exc.what());
throw;
} else {
enable_param = node_parameters->get_parameter(enable_param_name).get_parameter_value();
}
if (enable_param.get_type() != rclcpp::PARAMETER_BOOL) {
RCLCPP_ERROR(
logger_,
"Invalid type '%s' for parameter 'start_type_description_service', should be 'bool'",
rclcpp::to_string(enable_param.get_type()).c_str());
std::ostringstream ss;
ss << "Wrong parameter type, parameter {" << enable_param_name << "} is of type {bool}, "
<< "setting it to {" << to_string(enable_param.get_type()) << "} is not allowed.";
throw rclcpp::exceptions::InvalidParameterTypeException(enable_param_name, ss.str());
}
if (enabled) {
if (enable_param.get<bool>()) {
auto * rcl_node = node_base->get_rcl_node_handle();
std::shared_ptr<rcl_service_t> rcl_srv(
new rcl_service_t,

View File

@@ -0,0 +1,97 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/parameter_descriptor_wrapper.hpp"
namespace rclcpp
{
ParameterDescription::ParameterDescription()
{
// Need to set this in the constructor, but it doesn't necessarily need to be used
parameter_descriptor.type = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET;
}
rcl_interfaces::msg::ParameterDescriptor ParameterDescription::build() const
{
// Return some some sort message
return parameter_descriptor;
}
// Builder methods which set up the original class
// They all follow the same format of initing the value given within the base class
// then returning the current class
ParameterDescription & ParameterDescription::set_name(const std::string & name)
{
parameter_descriptor.name = name;
return *this;
}
ParameterDescription & ParameterDescription::set_type(std::uint8_t type)
{
parameter_descriptor.type = type;
return *this;
}
ParameterDescription & ParameterDescription::set_description_text(const std::string & description)
{
parameter_descriptor.description = description;
return *this;
}
ParameterDescription & ParameterDescription::set_additional_constraints(
const std::string & constraints)
{
parameter_descriptor.additional_constraints = constraints;
return *this;
}
ParameterDescription & ParameterDescription::set_read_only(bool read_only)
{
parameter_descriptor.read_only = read_only;
return *this;
}
ParameterDescription & ParameterDescription::set_dynamic_typing(bool dynamic_typing)
{
parameter_descriptor.dynamic_typing = dynamic_typing;
return *this;
}
// Here is the Specific range function for this parameter description
ParameterDescription & ParameterDescription::set_floating_point_description_range(
float min, float max, float step)
{
if (parameter_descriptor.type == rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
parameter_descriptor.floating_point_range.resize(1);
parameter_descriptor.floating_point_range.at(0).from_value = min;
parameter_descriptor.floating_point_range.at(0).to_value = max;
parameter_descriptor.floating_point_range.at(0).step = step;
}
return *this;
}
ParameterDescription & ParameterDescription::set_integer_description_range(
int min, int max, int step)
{
if (parameter_descriptor.type == rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER) {
parameter_descriptor.integer_range.resize(1);
parameter_descriptor.integer_range.at(0).from_value = min;
parameter_descriptor.integer_range.at(0).to_value = max;
parameter_descriptor.integer_range.at(0).step = step;
}
return *this;
}
} // namespace rclcpp

View File

@@ -74,6 +74,38 @@ ParameterEventHandler::add_parameter_callback(
return handle;
}
bool
ParameterEventHandler::configure_nodes_filter(const std::vector<std::string> & node_names)
{
if (node_names.empty()) {
// Clear content filter
event_subscription_->set_content_filter("");
if (event_subscription_->is_cft_enabled()) {
return false;
}
return true;
}
std::string filter_expression;
size_t total = node_names.size();
for (size_t i = 0; i < total; ++i) {
filter_expression += "node = %" + std::to_string(i);
if (i < total - 1) {
filter_expression += " OR ";
}
}
// Enclose each node name in "'".
std::vector<std::string> quoted_node_names;
for (const auto & name : node_names) {
quoted_node_names.push_back("'" + resolve_path(name) + "'");
}
event_subscription_->set_content_filter(filter_expression, quoted_node_names);
return event_subscription_->is_cft_enabled();
}
void
ParameterEventHandler::remove_parameter_callback(
ParameterCallbackHandle::SharedPtr callback_handle)

View File

@@ -81,7 +81,15 @@ rclcpp::parameter_map_from(const rcl_params_t * const c_params, const char * nod
throw InvalidParametersException(message);
}
const rcl_variant_t * const c_param_value = &(c_params_node->parameter_values[p]);
params_node.emplace_back(c_param_name, parameter_value_from(c_param_value));
ParameterValue value;
try {
value = parameter_value_from(c_param_value);
} catch (const InvalidParameterValueException & e) {
throw InvalidParameterValueException(
std::string("parameter_value_from failed for parameter '") +
c_param_name + "': " + e.what());
}
params_node.emplace_back(c_param_name, value);
}
}

View File

@@ -40,6 +40,12 @@ std::string qos_policy_name_from_kind(rmw_qos_policy_kind_t policy_kind)
return "HISTORY_QOS_POLICY";
case RMW_QOS_POLICY_LIFESPAN:
return "LIFESPAN_QOS_POLICY";
case RMW_QOS_POLICY_DEPTH:
return "DEPTH_QOS_POLICY";
case RMW_QOS_POLICY_LIVELINESS_LEASE_DURATION:
return "LIVELINESS_LEASE_DURATION_QOS_POLICY";
case RMW_QOS_POLICY_AVOID_ROS_NAMESPACE_CONVENTIONS:
return "AVOID_ROS_NAMESPACE_CONVENTIONS_QOS_POLICY";
default:
return "INVALID_QOS_POLICY";
}
@@ -69,8 +75,10 @@ QoSInitialization::from_rmw(const rmw_qos_profile_t & rmw_qos)
return KeepLast(rmw_qos.depth, false);
case RMW_QOS_POLICY_HISTORY_KEEP_LAST:
case RMW_QOS_POLICY_HISTORY_UNKNOWN:
default:
return KeepLast(rmw_qos.depth);
default:
throw std::invalid_argument(
"Invalid history policy enum value passed to QoSInitialization::from_rmw");
}
}

View File

@@ -67,7 +67,13 @@ Rate::sleep()
// Calculate the time to sleep
auto time_to_sleep = next_interval - now;
// Sleep (will get interrupted by ctrl-c, may not sleep full time)
clock_->sleep_for(time_to_sleep);
try {
// If the context is invalid, an exception will be thrown.
clock_->sleep_for(time_to_sleep);
} catch (const std::runtime_error & e) {
// If it didn't sleep the full time, return false
return false;
}
return true;
}

View File

@@ -26,8 +26,13 @@ namespace rclcpp
inline void copy_rcl_message(const rcl_serialized_message_t & from, rcl_serialized_message_t & to)
{
const auto ret = rmw_serialized_message_init(
&to, from.buffer_capacity, &from.allocator);
auto ret = RCL_RET_ERROR;
if (nullptr == to.buffer) {
ret = rmw_serialized_message_init(&to, from.buffer_capacity, &from.allocator);
} else {
ret = rmw_serialized_message_resize(&to, from.buffer_capacity);
}
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
@@ -78,7 +83,6 @@ SerializedMessage::SerializedMessage(rcl_serialized_message_t && other)
SerializedMessage & SerializedMessage::operator=(const SerializedMessage & other)
{
if (this != &other) {
serialized_message_ = rmw_get_zero_initialized_serialized_message();
copy_rcl_message(other.serialized_message_, serialized_message_);
}
@@ -88,7 +92,6 @@ SerializedMessage & SerializedMessage::operator=(const SerializedMessage & other
SerializedMessage & SerializedMessage::operator=(const rcl_serialized_message_t & other)
{
if (&serialized_message_ != &other) {
serialized_message_ = rmw_get_zero_initialized_serialized_message();
copy_rcl_message(other, serialized_message_);
}
@@ -98,6 +101,14 @@ SerializedMessage & SerializedMessage::operator=(const rcl_serialized_message_t
SerializedMessage & SerializedMessage::operator=(SerializedMessage && other)
{
if (this != &other) {
if (nullptr != serialized_message_.buffer) {
const auto fini_ret = rmw_serialized_message_fini(&serialized_message_);
if (RCL_RET_OK != fini_ret) {
RCLCPP_ERROR(
get_logger("rclcpp"),
"Failed to destroy serialized message: %s", rcl_get_error_string().str);
}
}
serialized_message_ =
std::exchange(other.serialized_message_, rmw_get_zero_initialized_serialized_message());
}
@@ -108,6 +119,14 @@ SerializedMessage & SerializedMessage::operator=(SerializedMessage && other)
SerializedMessage & SerializedMessage::operator=(rcl_serialized_message_t && other)
{
if (&serialized_message_ != &other) {
if (nullptr != serialized_message_.buffer) {
const auto fini_ret = rmw_serialized_message_fini(&serialized_message_);
if (RCL_RET_OK != fini_ret) {
RCLCPP_ERROR(
get_logger("rclcpp"),
"Failed to destroy serialized message: %s", rcl_get_error_string().str);
}
}
serialized_message_ =
std::exchange(other, rmw_get_zero_initialized_serialized_message());
}

View File

@@ -68,7 +68,6 @@ void
SignalHandler::signal_handler(
int signum, siginfo_t * siginfo, void * context)
{
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum);
auto & instance = SignalHandler::get_global_signal_handler();
auto old_signal_handler = instance.get_old_signal_handler(signum);
@@ -85,13 +84,12 @@ SignalHandler::signal_handler(
old_signal_handler.sa_handler(signum);
}
}
instance.signal_handler_common();
instance.signal_handler_common(signum);
}
#else
void
SignalHandler::signal_handler(int signum)
{
RCLCPP_INFO(SignalHandler::get_logger(), "signal_handler(signum=%d)", signum);
auto & instance = SignalHandler::get_global_signal_handler();
auto old_signal_handler = instance.get_old_signal_handler(signum);
if (
@@ -100,7 +98,7 @@ SignalHandler::signal_handler(int signum)
{
old_signal_handler(signum);
}
instance.signal_handler_common();
instance.signal_handler_common(signum);
}
#endif
@@ -245,13 +243,11 @@ SignalHandler::get_old_signal_handler(int signum)
}
void
SignalHandler::signal_handler_common()
SignalHandler::signal_handler_common(int signum)
{
auto & instance = SignalHandler::get_global_signal_handler();
instance.signal_number_.store(signum);
instance.signal_received_.store(true);
RCLCPP_DEBUG(
get_logger(),
"signal_handler(): notifying deferred signal handler");
instance.notify_signal_handler();
}
@@ -260,6 +256,8 @@ SignalHandler::deferred_signal_handler()
{
while (true) {
if (signal_received_.exchange(false)) {
int signum = signal_number_.load();
RCLCPP_INFO(get_logger(), "signal_handler(signum=%d)", signum);
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): shutting down");
for (auto context_ptr : rclcpp::get_contexts()) {
if (context_ptr->get_init_options().shutdown_on_signal) {

View File

@@ -116,7 +116,7 @@ private:
/// Common signal handler code between sigaction and non-sigaction versions.
void
signal_handler_common();
signal_handler_common(int signum);
#if defined(RCLCPP_HAS_SIGACTION)
/// Signal handler function.
@@ -189,6 +189,8 @@ private:
// Whether or not a signal has been received.
std::atomic_bool signal_received_ = false;
// The signal number that was received.
std::atomic_int signal_number_ = 0;
// A thread to which signal handling tasks are deferred.
std::thread signal_handler_thread_;

View File

@@ -577,3 +577,26 @@ SubscriptionBase::take_dynamic_message(
throw std::runtime_error("Unimplemented");
return false;
}
void
SubscriptionBase::disable_callbacks()
{
// Temporary remove the on_new_message_callback_ to prevent it from being called
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
if (on_new_message_callback_) {
set_on_new_message_callback(nullptr, nullptr);
}
}
void
SubscriptionBase::enable_callbacks()
{
// Set callback again if it was previously removed in disable_callbacks()
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
if (on_new_message_callback_) {
set_on_new_message_callback(
rclcpp::detail::cpp_callback_trampoline<
decltype(on_new_message_callback_), const void *, size_t>,
static_cast<const void *>(&on_new_message_callback_));
}
}

View File

@@ -40,3 +40,17 @@ SubscriptionIntraProcessBase::is_durability_transient_local() const
{
return qos_profile_.durability() == rclcpp::DurabilityPolicy::TransientLocal;
}
void
SubscriptionIntraProcessBase::disable_callbacks()
{
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
on_new_message_callback_disabled_ = true;
}
void
SubscriptionIntraProcessBase::enable_callbacks()
{
std::lock_guard<std::recursive_mutex> lock(on_new_message_callback_mutex_);
on_new_message_callback_disabled_ = false;
}

View File

@@ -15,6 +15,7 @@
#include "rclcpp/typesupport_helpers.hpp"
#include <algorithm>
#include <functional>
#include <memory>
#include <sstream>
@@ -35,62 +36,6 @@ namespace rclcpp
namespace
{
// Look for the library in the ament prefix paths.
std::string get_typesupport_library_path(
const std::string & package_name, const std::string & typesupport_identifier)
{
const char * dynamic_library_folder;
#ifdef _WIN32
dynamic_library_folder = "/bin/";
#elif __APPLE__
dynamic_library_folder = "/lib/";
#else
dynamic_library_folder = "/lib/";
#endif
std::string package_prefix;
try {
package_prefix = ament_index_cpp::get_package_prefix(package_name);
} catch (ament_index_cpp::PackageNotFoundError & e) {
throw std::runtime_error(e.what());
}
const std::string library_path = rcpputils::path_for_library(
package_prefix + dynamic_library_folder,
package_name + "__" + typesupport_identifier);
if (library_path.empty()) {
throw std::runtime_error(
"Typesupport library for " + package_name + " does not exist in '" + package_prefix +
"'.");
}
return library_path;
}
std::tuple<std::string, std::string, std::string>
extract_type_identifier(const std::string & full_type)
{
char type_separator = '/';
auto sep_position_back = full_type.find_last_of(type_separator);
auto sep_position_front = full_type.find_first_of(type_separator);
if (sep_position_back == std::string::npos ||
sep_position_back == 0 ||
sep_position_back == full_type.length() - 1)
{
throw std::runtime_error(
"Message type is not of the form package/type and cannot be processed");
}
std::string package_name = full_type.substr(0, sep_position_front);
std::string middle_module = "";
if (sep_position_back - sep_position_front > 0) {
middle_module =
full_type.substr(sep_position_front + 1, sep_position_back - sep_position_front - 1);
}
std::string type_name = full_type.substr(sep_position_back + 1);
return std::make_tuple(package_name, middle_module, type_name);
}
const void * get_typesupport_handle_impl(
const std::string & type,
const std::string & typesupport_identifier,
@@ -129,8 +74,80 @@ const void * get_typesupport_handle_impl(
}
}
// Trim leading and trailing whitespace from the string.
std::string string_trim(std::string_view str_v)
{
auto begin = std::find_if_not(str_v.begin(), str_v.end(), [](unsigned char ch) {
return std::isspace(ch);
});
auto end = std::find_if_not(str_v.rbegin(), str_v.rend(), [](unsigned char ch) {
return std::isspace(ch);
}).base();
if (begin >= end) {
return {};
}
return std::string(begin, end);
}
} // anonymous namespace
std::tuple<std::string, std::string, std::string>
extract_type_identifier(const std::string & full_type)
{
char type_separator = '/';
auto sep_position_back = full_type.find_last_of(type_separator);
auto sep_position_front = full_type.find_first_of(type_separator);
if (sep_position_back == std::string::npos ||
sep_position_front == 0 ||
sep_position_back == 0 ||
sep_position_back == full_type.length() - 1)
{
throw std::runtime_error(
"Message type is not of the form package/type and cannot be processed");
}
std::string package_name = full_type.substr(0, sep_position_front);
std::string middle_module = "";
if (sep_position_back - sep_position_front > 0) {
middle_module =
full_type.substr(sep_position_front + 1, sep_position_back - sep_position_front - 1);
}
std::string type_name = full_type.substr(sep_position_back + 1);
return std::make_tuple(
string_trim(package_name), string_trim(middle_module), string_trim(type_name));
}
std::string get_typesupport_library_path(
const std::string & package_name, const std::string & typesupport_identifier)
{
const char * dynamic_library_folder;
#ifdef _WIN32
dynamic_library_folder = "bin";
#elif __APPLE__
dynamic_library_folder = "lib";
#else
dynamic_library_folder = "lib";
#endif
std::filesystem::path package_prefix;
try {
ament_index_cpp::get_package_prefix(package_name, package_prefix);
} catch (ament_index_cpp::PackageNotFoundError & e) {
throw std::runtime_error(e.what());
}
const std::string library_path = rcpputils::path_for_library(
(package_prefix / dynamic_library_folder).string(),
package_name + "__" + typesupport_identifier);
if (library_path.empty()) {
throw std::runtime_error(
"Typesupport library for " + package_name + " does not exist in '" +
package_prefix.string() + "'.");
}
return library_path;
}
std::shared_ptr<rcpputils::SharedLibrary>
get_typesupport_library(const std::string & type, const std::string & typesupport_identifier)
{

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <chrono>
#include <memory>
#include <string>
#include <utility>

View File

@@ -125,11 +125,13 @@ BENCHMARK_F(ServicePerformanceTest, async_send_response)(benchmark::State & stat
auto service = node->create_service<test_msgs::srv::Empty>(empty_service_name, callback);
reset_heap_counters();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node->get_node_base_interface());
for (auto _ : state) {
(void)_;
state.PauseTiming();
// Clear executor queue
rclcpp::spin_some(node->get_node_base_interface());
executor.spin_some();
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
auto future = empty_client->async_send_request(request);
@@ -137,7 +139,7 @@ BENCHMARK_F(ServicePerformanceTest, async_send_response)(benchmark::State & stat
benchmark::DoNotOptimize(service);
benchmark::ClobberMemory();
rclcpp::spin_until_future_complete(node->get_node_base_interface(), future);
executor.spin_until_future_complete(future);
}
if (callback_count == 0) {
state.SkipWithError("Service callback was not called");

View File

@@ -529,13 +529,6 @@ if(TARGET test_executors_warmup)
target_link_libraries(test_executors_warmup ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_static_single_threaded_executor executors/test_static_single_threaded_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
ament_add_test_label(test_static_single_threaded_executor mimick)
if(TARGET test_static_single_threaded_executor)
target_link_libraries(test_static_single_threaded_executor ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_multi_threaded_executor executors/test_multi_threaded_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_multi_threaded_executor)
@@ -669,32 +662,24 @@ endif()
function(test_on_all_rmws)
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
# If the rmw_implementation is rmw_zenoh_cpp, run the tests with multicast discovery enabled.
# Note: This is a temporary change that will be reverted before we branch into Kilted.
if(rmw_implementation STREQUAL "rmw_zenoh_cpp")
list(APPEND rmw_implementation_env_var
ZENOH_CONFIG_OVERRIDE=scouting/multicast/enabled=true
)
endif()
ament_add_gmock_test(test_qos_event
ament_add_ros_isolated_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
ament_add_ros_isolated_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
ament_add_ros_isolated_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
)
ament_add_gmock_test(test_subscription_content_filter
ament_add_ros_isolated_gmock_test(test_subscription_content_filter
TEST_NAME test_subscription_content_filter${target_suffix}
ENV ${rmw_implementation_env_var}
TIMEOUT 120

View File

@@ -22,40 +22,13 @@
#include "rclcpp/experimental/executors/events_executor/events_executor.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#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,
DeprecatedStaticSingleThreadedExecutor,
rclcpp::experimental::executors::EventsExecutor>;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
class ExecutorTypeNames
{
@@ -70,16 +43,6 @@ public:
if (std::is_same<T, rclcpp::executors::MultiThreadedExecutor>()) {
return "MultiThreadedExecutor";
}
#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";
@@ -89,8 +52,6 @@ public:
}
};
// StaticSingleThreadedExecutor is not included in these tests for now, due to:
// https://github.com/ros2/rclcpp/issues/1219
using StandardExecutors =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <chrono>
#include <memory>
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"

View File

@@ -804,8 +804,10 @@ TEST(TestExecutors, testSpinUntilFutureCompleteNodePtr)
}
// Check spin functions with non default context
TEST(TestExecutors, testSpinWithNonDefaultContext)
TYPED_TEST(TestExecutors, testSpinWithNonDefaultContext)
{
using ExecutorType = TypeParam;
auto non_default_context = std::make_shared<rclcpp::Context>();
non_default_context->init(0, nullptr);
@@ -813,9 +815,14 @@ TEST(TestExecutors, testSpinWithNonDefaultContext)
auto node =
std::make_unique<rclcpp::Node>("node", rclcpp::NodeOptions().context(non_default_context));
EXPECT_NO_THROW(rclcpp::spin_some(node->get_node_base_interface()));
rclcpp::ExecutorOptions options;
options.context = non_default_context;
ExecutorType executor(options);
EXPECT_NO_THROW(executor.add_node(node->get_node_base_interface()));
EXPECT_NO_THROW(rclcpp::spin_all(node->get_node_base_interface(), 1s));
EXPECT_NO_THROW(executor.spin_some());
EXPECT_NO_THROW(executor.spin_all(1s));
auto check_spin_until_future_complete = [&]() {
std::promise<bool> promise;
@@ -823,8 +830,7 @@ TEST(TestExecutors, testSpinWithNonDefaultContext)
promise.set_value(true);
auto shared_future = future.share();
auto ret = rclcpp::spin_until_future_complete(
node->get_node_base_interface(), shared_future, 1s);
auto ret = executor.spin_until_future_complete(shared_future, 1s);
EXPECT_EQ(rclcpp::FutureReturnCode::SUCCESS, ret);
};
EXPECT_NO_THROW(check_spin_until_future_complete());

View File

@@ -285,20 +285,11 @@ 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,
DeprecatedStaticSingleThreadedExecutor>;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
rclcpp::executors::MultiThreadedExecutor>;
// 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.

View File

@@ -1,192 +0,0 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <chrono>
#include <future>
#include <memory>
#include <stdexcept>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/executors.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
#include "test_msgs/srv/empty.hpp"
#include "./executor_types.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
using namespace std::chrono_literals;
class TestStaticSingleThreadedExecutor : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
}
void TearDown()
{
rclcpp::shutdown();
}
};
TEST_F(TestStaticSingleThreadedExecutor, add_callback_group_trigger_guard_failed) {
#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);
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
executor.add_callback_group(cb_group, node->get_node_base_interface(), true),
std::runtime_error("Failed to handle entities update on callback group add: error not set"));
}
}
TEST_F(TestStaticSingleThreadedExecutor, add_node_trigger_guard_failed) {
#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");
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
executor.add_node(node),
std::runtime_error("Failed to handle entities update on node add: error not set"));
}
}
TEST_F(TestStaticSingleThreadedExecutor, remove_callback_group_trigger_guard_failed) {
#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);
executor.add_callback_group(cb_group, node->get_node_base_interface(), true);
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
executor.remove_callback_group(cb_group, true),
std::runtime_error(
"Failed to handle entities update on callback group remove: error not set"));
}
}
TEST_F(TestStaticSingleThreadedExecutor, remove_node_failed) {
#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");
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
executor.remove_node(node, true),
std::runtime_error("Node '/ns/node' needs to be associated with an executor."));
}
}
TEST_F(TestStaticSingleThreadedExecutor, remove_node_trigger_guard_failed) {
#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);
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_trigger_guard_condition, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
executor.remove_node(node, true),
std::runtime_error("Failed to handle entities update on node remove: error not set"));
}
}
TEST_F(TestStaticSingleThreadedExecutor, execute_service) {
#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);
auto service =
node->create_service<test_msgs::srv::Empty>(
"service",
[](
const test_msgs::srv::Empty::Request::SharedPtr,
test_msgs::srv::Empty::Response::SharedPtr) {});
auto client = node->create_client<test_msgs::srv::Empty>("service");
std::promise<void> promise;
std::future<void> future = promise.get_future();
EXPECT_EQ(
rclcpp::FutureReturnCode::TIMEOUT,
executor.spin_until_future_complete(future, std::chrono::milliseconds(1)));
executor.remove_node(node);
executor.spin_until_future_complete(future, std::chrono::milliseconds(1));
}

View File

@@ -13,6 +13,7 @@
// limitations under the License.
#include <atomic>
#include <chrono>
#include <functional>
#include <memory>
#include <thread>

View File

@@ -510,7 +510,9 @@ TEST_F(TestNodeGraph, get_node_names_and_namespaces_fini_errors)
"lib:rclcpp", rcutils_string_array_fini, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->get_node_names_and_namespaces(),
std::runtime_error("could not destroy node names, could not destroy node namespaces"));
std::runtime_error(
"could not destroy node names: error not set, "
"could not destroy node namespaces: error not set"));
}
TEST_F(TestNodeGraph, get_node_names_with_enclaves_fini_errors)

View File

@@ -192,7 +192,7 @@ TEST_F(TestNodeInterfaces, ni_init) {
/*
Testing macro'ed getters.
*/
TEST_F(TestNodeInterfaces, ni_all_init) {
TEST_F(TestNodeInterfaces, ni_all_init_non_const) {
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
using rclcpp::node_interfaces::NodeInterfaces;
@@ -252,3 +252,64 @@ TEST_F(TestNodeInterfaces, ni_all_init) {
time_source = ni.get_node_time_source_interface();
}
}
TEST_F(TestNodeInterfaces, ni_all_init_const) {
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
using rclcpp::node_interfaces::NodeInterfaces;
using rclcpp::node_interfaces::NodeBaseInterface;
using rclcpp::node_interfaces::NodeClockInterface;
using rclcpp::node_interfaces::NodeGraphInterface;
using rclcpp::node_interfaces::NodeLoggingInterface;
using rclcpp::node_interfaces::NodeTimersInterface;
using rclcpp::node_interfaces::NodeTopicsInterface;
using rclcpp::node_interfaces::NodeServicesInterface;
using rclcpp::node_interfaces::NodeWaitablesInterface;
using rclcpp::node_interfaces::NodeParametersInterface;
using rclcpp::node_interfaces::NodeTimeSourceInterface;
const auto ni = rclcpp::node_interfaces::NodeInterfaces<ALL_RCLCPP_NODE_INTERFACES>(*node);
{
auto base = ni.get<NodeBaseInterface>();
base = ni.get_node_base_interface();
EXPECT_STREQ(base->get_name(), "my_node"); // Test for functionality
}
{
auto clock = ni.get<NodeClockInterface>();
clock = ni.get_node_clock_interface();
clock->get_clock();
}
{
auto graph = ni.get<NodeGraphInterface>();
graph = ni.get_node_graph_interface();
}
{
auto logging = ni.get<NodeLoggingInterface>();
logging = ni.get_node_logging_interface();
}
{
auto timers = ni.get<NodeTimersInterface>();
timers = ni.get_node_timers_interface();
}
{
auto topics = ni.get<NodeTopicsInterface>();
topics = ni.get_node_topics_interface();
}
{
auto services = ni.get<NodeServicesInterface>();
services = ni.get_node_services_interface();
}
{
auto waitables = ni.get<NodeWaitablesInterface>();
waitables = ni.get_node_waitables_interface();
}
{
auto parameters = ni.get<NodeParametersInterface>();
parameters = ni.get_node_parameters_interface();
}
{
auto time_source = ni.get<NodeTimeSourceInterface>();
time_source = ni.get_node_time_source_interface();
}
}

View File

@@ -128,7 +128,7 @@ TEST_F(TestNodeParameters, list_parameters)
list_result5.names.end());
}
TEST_F(TestNodeParameters, parameter_overrides)
TEST_F(TestNodeParameters, parameter_overrides_with_value)
{
rclcpp::NodeOptions node_options;
node_options.automatically_declare_parameters_from_overrides(true);
@@ -146,6 +146,24 @@ TEST_F(TestNodeParameters, parameter_overrides)
EXPECT_EQ(2u, parameter_overrides.size());
}
TEST_F(TestNodeParameters, parameter_overrides_with_parameter)
{
rclcpp::NodeOptions node_options;
node_options.automatically_declare_parameters_from_overrides(true);
node_options.append_parameter_override(rclcpp::Parameter("param1", true));
node_options.append_parameter_override(rclcpp::Parameter("param2", 42));
std::shared_ptr<rclcpp::Node> node2 = std::make_shared<rclcpp::Node>("node2", "ns", node_options);
auto * node_parameters_interface =
dynamic_cast<rclcpp::node_interfaces::NodeParameters *>(
node2->get_node_parameters_interface().get());
ASSERT_NE(nullptr, node_parameters_interface);
const auto & parameter_overrides = node_parameters_interface->get_parameter_overrides();
EXPECT_EQ(2u, parameter_overrides.size());
}
TEST_F(TestNodeParameters, set_parameters) {
rclcpp::NodeOptions node_options;
node_options.allow_undeclared_parameters(true);

View File

@@ -94,6 +94,8 @@ public:
void handle_dynamic_message(
const DynamicMessage::SharedPtr &,
const rclcpp::MessageInfo &) override {}
void disable_callbacks() override {}
void enable_callbacks() override {}
};
class TestNodeTopics : public ::testing::Test

View File

@@ -37,6 +37,38 @@ TEST_F(TestNodeTypeDescriptions, interface_created)
ASSERT_NE(nullptr, node.get_node_type_descriptions_interface());
}
TEST_F(TestNodeTypeDescriptions, automatically_declare_parameters_from_overrides)
{
rclcpp::NodeOptions node_options;
node_options.automatically_declare_parameters_from_overrides(true);
node_options.append_parameter_override("start_type_description_service", false);
ASSERT_NO_THROW(
{
auto node = std::make_shared<rclcpp::Node>("node", "ns", node_options);
(void) node;
});
}
TEST_F(TestNodeTypeDescriptions, bad_parameter_type)
{
rclcpp::NodeOptions node_options;
node_options.append_parameter_override("start_type_description_service", "unexpected_type");
ASSERT_THROW(
{
node_options.automatically_declare_parameters_from_overrides(false);
auto node = std::make_shared<rclcpp::Node>("node", "ns", node_options);
(void) node;
}, rclcpp::exceptions::InvalidParameterTypeException);
ASSERT_THROW(
{
node_options.automatically_declare_parameters_from_overrides(true);
auto node = std::make_shared<rclcpp::Node>("node", "ns", node_options);
(void) node;
}, rclcpp::exceptions::InvalidParameterTypeException);
}
TEST_F(TestNodeTypeDescriptions, disabled_no_service)
{
rclcpp::NodeOptions node_options;

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <atomic>
#include <functional>
#include <memory>
#include <string>
@@ -207,6 +208,126 @@ TEST_F(TestAnySubscriptionCallback, unset_dispatch_throw) {
std::runtime_error);
}
//
// Testing disable and enable callbacks
//
TEST_F(TestAnySubscriptionCallback, disable_enable_callbacks) {
std::atomic<int> callback_count{0};
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
asc.set([&callback_count](const test_msgs::msg::Empty &) {
++callback_count;
});
// Callback should work initially
asc.dispatch(msg_shared_ptr_, message_info_);
EXPECT_EQ(1, callback_count.load());
// Disable and verify callback is not called
asc.disable();
asc.dispatch(msg_shared_ptr_, message_info_);
EXPECT_EQ(1, callback_count.load()); // Count should not increase
// Enable and verify callback works again
asc.enable();
asc.dispatch(msg_shared_ptr_, message_info_);
EXPECT_EQ(2, callback_count.load());
}
TEST_F(TestAnySubscriptionCallback, disable_enable_callbacks_intra_process) {
std::atomic<int> callback_count{0};
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
asc.set([&callback_count](std::shared_ptr<const test_msgs::msg::Empty>) {
++callback_count;
});
// Callback should work initially
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
EXPECT_EQ(1, callback_count.load());
// Disable and verify callback is not called
asc.disable();
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
EXPECT_EQ(1, callback_count.load());
// Enable and verify callback works again
asc.enable();
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
EXPECT_EQ(2, callback_count.load());
}
TEST_F(TestAnySubscriptionCallback, disable_enable_callbacks_unique_ptr) {
std::atomic<int> callback_count{0};
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
asc.set([&callback_count](std::unique_ptr<test_msgs::msg::Empty>) {
++callback_count;
});
// Callback should work initially
asc.dispatch_intra_process(get_unique_ptr_msg(), message_info_);
EXPECT_EQ(1, callback_count.load());
// Disable and verify callback is not called
asc.disable();
asc.dispatch_intra_process(get_unique_ptr_msg(), message_info_);
EXPECT_EQ(1, callback_count.load());
// Enable and verify callback works again
asc.enable();
asc.dispatch_intra_process(get_unique_ptr_msg(), message_info_);
EXPECT_EQ(2, callback_count.load());
}
TEST_F(TestAnySubscriptionCallback, disable_enable_serialized_message) {
std::atomic<int> callback_count{0};
rclcpp::AnySubscriptionCallback<test_msgs::msg::Empty> asc;
asc.set([&callback_count](const rclcpp::SerializedMessage &) {
++callback_count;
});
auto serialized_msg = std::make_shared<rclcpp::SerializedMessage>();
// Callback should work initially
asc.dispatch(serialized_msg, message_info_);
EXPECT_EQ(1, callback_count.load());
// Disable and verify callback is not called
asc.disable();
asc.dispatch(serialized_msg, message_info_);
EXPECT_EQ(1, callback_count.load());
// Enable and verify callback works again
asc.enable();
asc.dispatch(serialized_msg, message_info_);
EXPECT_EQ(2, callback_count.load());
}
TEST_F(TestAnySubscriptionCallbackTA, disable_enable_callbacks_type_adapter) {
std::atomic<int> callback_count{0};
rclcpp::AnySubscriptionCallback<MyTA> asc;
asc.set([&callback_count](const MyEmpty &) {
++callback_count;
});
// Callback should work initially
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
EXPECT_EQ(1, callback_count.load());
// Disable and verify callback is not called
asc.disable();
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
EXPECT_EQ(1, callback_count.load());
// Enable and verify callback works again
asc.enable();
asc.dispatch_intra_process(msg_shared_ptr_, message_info_);
EXPECT_EQ(2, callback_count.load());
}
//
// Parameterized test to test across all callback types and dispatch types.
//

View File

@@ -112,10 +112,12 @@ protected:
auto req_id = client->async_send_request(request, std::move(callback));
auto start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
while (!received_response &&
(std::chrono::steady_clock::now() - start) < timeout)
{
rclcpp::spin_some(node);
executor.spin_some();
}
if (!received_response) {
@@ -356,10 +358,12 @@ TYPED_TEST(TestAllClientTypesWithServer, on_new_response_callback)
this->template async_send_request<ClientType, test_msgs::srv::Empty::Request>(client, request);
auto start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(server_node);
while (server_requests_count == 0 &&
(std::chrono::steady_clock::now() - start) < std::chrono::seconds(10))
{
rclcpp::spin_some(server_node);
executor.spin_some();
}
ASSERT_EQ(server_requests_count, 1u);
@@ -380,7 +384,7 @@ TYPED_TEST(TestAllClientTypesWithServer, on_new_response_callback)
while (server_requests_count == 1 &&
(std::chrono::steady_clock::now() - start) < std::chrono::seconds(10))
{
rclcpp::spin_some(server_node);
executor.spin_some();
}
ASSERT_EQ(server_requests_count, 2u);
@@ -402,7 +406,7 @@ TYPED_TEST(TestAllClientTypesWithServer, on_new_response_callback)
while (server_requests_count < 5 &&
(std::chrono::steady_clock::now() - start) < std::chrono::seconds(10))
{
rclcpp::spin_some(server_node);
executor.spin_some();
}
ASSERT_EQ(server_requests_count, 5u);
@@ -492,10 +496,12 @@ void client_async_send_request_callback_with_request(
auto req_id = client->async_send_request(request, std::move(callback));
auto start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
while (!received_response &&
(std::chrono::steady_clock::now() - start) < std::chrono::seconds(1))
{
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_TRUE(received_response);
EXPECT_FALSE(client->remove_pending_request(req_id));
@@ -553,25 +559,29 @@ void client_qos_depth(rclcpp::Node::SharedPtr node)
}
auto start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor server_executor;
server_executor.add_node(server_node);
while ((server_cb_count_ < client_requests) &&
(std::chrono::steady_clock::now() - start) < 2s)
{
rclcpp::spin_some(server_node);
server_executor.spin_some();
std::this_thread::sleep_for(2ms);
}
EXPECT_GT(server_cb_count_, client_qos_profile.depth());
start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor client_executor;
client_executor.add_node(node);
while ((client_cb_count_ < client_qos_profile.depth()) &&
(std::chrono::steady_clock::now() - start) < 1s)
{
rclcpp::spin_some(node);
client_executor.spin_some();
}
// Spin an extra time to check if client QoS depth has been ignored,
// so more client callbacks might be called than expected.
rclcpp::spin_some(node);
client_executor.spin_some();
EXPECT_EQ(client_cb_count_, client_qos_profile.depth());
}

View File

@@ -42,7 +42,9 @@ TEST(TestCreateTimer, timer_executes)
timer->cancel();
});
rclcpp::spin_some(node);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
executor.spin_some();
ASSERT_TRUE(got_callback);
rclcpp::shutdown();

View File

@@ -15,16 +15,20 @@
#include <gmock/gmock.h>
#include <atomic>
#include <chrono>
#include <future>
#include <memory>
#include <string>
#include <vector>
#include <thread>
#include "test_msgs/message_fixtures.hpp"
#include "test_msgs/msg/basic_types.hpp"
#include "rcl/graph.h"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/generic_publisher.hpp"
#include "rclcpp/generic_subscription.hpp"
#include "rclcpp/rclcpp.hpp"
@@ -33,6 +37,7 @@
using namespace ::testing; // NOLINT
using namespace rclcpp; // NOLINT
using namespace std::chrono_literals; // NOLINT
class RclcppGenericNodeFixture : public Test
{
@@ -77,8 +82,10 @@ public:
counter++;
});
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node_);
while (counter < expected_recv_msg_count) {
rclcpp::spin_some(node_);
executor.spin_some();
}
return messages;
}
@@ -106,11 +113,13 @@ public:
{
using clock = std::chrono::system_clock;
auto start = clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node_);
while (!condition()) {
if ((clock::now() - start) > timeout) {
return false;
}
rclcpp::spin_some(node_);
executor.spin_some();
}
return true;
}
@@ -309,3 +318,133 @@ TEST_F(RclcppGenericNodeFixture, generic_subscription_different_callbacks)
ASSERT_TRUE(wait_for(connected, 5s));
}
}
TEST_F(RclcppGenericNodeFixture, disable_enable_subscription_callbacks)
{
const std::string topic_name = "test_disable_topic";
const std::string type = "test_msgs/msg/Strings";
const auto qos = rclcpp::QoS(10U);
std::atomic<int> callback_count{0};
auto callback = [&callback_count](std::shared_ptr<const rclcpp::SerializedMessage>) {
++callback_count;
};
auto subscription = node_->create_generic_subscription(topic_name, type, qos, callback);
auto publisher = node_->create_generic_publisher(topic_name, type, qos);
// Wait for discovery
auto connected = [publisher, subscription]() -> bool {
return publisher->get_subscription_count() && subscription->get_publisher_count();
};
ASSERT_TRUE(wait_for(connected, 10s));
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node_);
// Publish and verify callback is called
publisher->publish(serialize_message<std::string, test_msgs::msg::Strings>("test1"));
auto start = std::chrono::steady_clock::now();
while (callback_count.load() == 0 && std::chrono::steady_clock::now() - start < 30s) {
executor.spin_some();
std::this_thread::sleep_for(10ms);
}
EXPECT_EQ(1, callback_count.load());
// Disable callbacks
EXPECT_NO_THROW(subscription->disable_callbacks());
// Publish again and verify callback is NOT called
publisher->publish(serialize_message<std::string, test_msgs::msg::Strings>("test2"));
start = std::chrono::steady_clock::now();
auto initial_count = callback_count.load();
while (std::chrono::steady_clock::now() - start < 500ms) {
executor.spin_some();
std::this_thread::sleep_for(10ms);
}
EXPECT_EQ(initial_count, callback_count.load()); // Should not increase
// Enable callbacks
EXPECT_NO_THROW(subscription->enable_callbacks());
// Publish again and verify callback IS called
publisher->publish(serialize_message<std::string, test_msgs::msg::Strings>("test3"));
start = std::chrono::steady_clock::now();
while (callback_count.load() == initial_count && std::chrono::steady_clock::now() - start < 30s) {
executor.spin_some();
std::this_thread::sleep_for(10ms);
}
EXPECT_EQ(callback_count.load(), initial_count + 1);
}
TEST_F(RclcppGenericNodeFixture, disable_enable_subscription_event_callbacks)
{
if (std::string(rmw_get_implementation_identifier()).find("rmw_zenoh_cpp") == 0) {
GTEST_SKIP() << "rmw_zenoh doesn't support deadline and liveliness events";
}
const std::string topic_name = "test_event_callbacks_topic";
const std::string type = "test_msgs/msg/Strings";
const auto qos = rclcpp::QoS(10U).deadline(100ms);
std::atomic<int> deadline_callback_count{0};
std::atomic<int> liveliness_callback_count{0};
rclcpp::SubscriptionOptions sub_options;
sub_options.event_callbacks.deadline_callback =
[&deadline_callback_count](rclcpp::QOSDeadlineRequestedInfo &) {
++deadline_callback_count;
};
sub_options.event_callbacks.liveliness_callback =
[&liveliness_callback_count](rclcpp::QOSLivelinessChangedInfo &) {
++liveliness_callback_count;
};
auto do_nothing = [](std::shared_ptr<const rclcpp::SerializedMessage>) {};
auto subscription =
node_->create_generic_subscription(topic_name, type, qos, do_nothing, sub_options);
const auto & event_handlers = subscription->get_event_handlers();
// Initially, both callbacks counters should be zero
EXPECT_EQ(deadline_callback_count, 0);
EXPECT_EQ(liveliness_callback_count, 0);
// Execute events and disable all event handlers
for (const auto & [event_type, handler] : event_handlers) {
if (handler) {
const std::shared_ptr<void> data = handler->take_data();
handler->execute(data);
EXPECT_NO_THROW(handler->disable());
}
}
// Expect both callbacks to have been called once since they should be enabled by default
EXPECT_EQ(deadline_callback_count, 1);
EXPECT_EQ(liveliness_callback_count, 1);
// Execute events one more time and enable all event handlers
for (const auto & [event_type, handler] : event_handlers) {
if (handler) {
const std::shared_ptr<void> data = handler->take_data();
handler->execute(data);
EXPECT_NO_THROW(handler->enable());
}
}
// Expect no change since they were disabled
EXPECT_EQ(deadline_callback_count, 1);
EXPECT_EQ(liveliness_callback_count, 1);
// Execute events one more time
for (const auto & [event_type, handler] : event_handlers) {
if (handler) {
const std::shared_ptr<void> data = handler->take_data();
handler->execute(data);
}
}
EXPECT_EQ(deadline_callback_count, 2);
EXPECT_EQ(liveliness_callback_count, 2);
}

View File

@@ -372,16 +372,18 @@ TEST_F(TestGenericService, generic_service_qos_depth) {
}
auto start = std::chrono::steady_clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(server_node);
while ((server_cb_count_ < server_qos_profile.depth()) &&
(std::chrono::steady_clock::now() - start) < 1s)
{
rclcpp::spin_some(server_node);
executor.spin_some();
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);
executor.spin_some();
EXPECT_EQ(server_cb_count_, server_qos_profile.depth());
}

View File

@@ -43,9 +43,7 @@ public:
node_graph_ = node_->get_node_graph_interface();
ASSERT_NE(nullptr, node_graph_);
graph_listener_ =
std::make_shared<rclcpp::graph_listener::GraphListener>(
rclcpp::contexts::get_global_default_context());
graph_listener_ = rclcpp::contexts::get_global_default_context()->get_graph_listener();
}
void TearDown()

View File

@@ -535,7 +535,7 @@ TEST(TestIntraProcessManager, add_pub_sub) {
* - Remove the first subscription from ipm and add a new one.
* - Publishes a unique_ptr message with a subscription not requesting ownership.
* - The received message is expected to be the same, the first subscription do not receive it.
* - Publishes a shared_ptr message with a subscription not requesting ownership.
* - Publishes a unique_ptr message with a subscription not requesting ownership.
* - The received message is expected to be the same.
*/
TEST(TestIntraProcessManager, single_subscription) {
@@ -587,9 +587,9 @@ TEST(TestIntraProcessManager, single_subscription) {
* - One is expected to receive the published message, while the other will receive a copy.
* - Publishes a unique_ptr message with 2 subscriptions not requesting ownership.
* - Both received messages are expected to be the same as the published one.
* - Publishes a shared_ptr message with 2 subscriptions requesting ownership.
* - Publishes a unique_ptr message with 2 subscriptions requesting ownership.
* - Both received messages are expected to be a copy of the published one.
* - Publishes a shared_ptr message with 2 subscriptions not requesting ownership.
* - Publishes a unique_ptr message with 2 subscriptions not requesting ownership.
* - Both received messages are expected to be the same as the published one.
*/
TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
@@ -694,9 +694,9 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
* - The 2 subscriptions not requesting ownership are expected to both receive the same copy
* of the message, one of the subscription requesting ownership is expected to receive a
* different copy, while the last is expected to receive the published message.
* - Publishes a shared_ptr message with 1 subscription requesting ownership and 1 not.
* - The subscription requesting ownership is expected to receive a copy of the message, while
* the other is expected to receive the published message
* - Publishes a unique_ptr message with 1 subscription requesting ownership and 1 not.
* - The subscription requesting ownership is expected to receive the published message, while
* the other is expected to receive a copy of the message
*/
TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <chrono>
#include <memory>
#include <string>
#include <vector>

View File

@@ -253,9 +253,19 @@ bool log_function_const_ref(const rclcpp::Logger & logger)
return true;
}
class DerivedLogger : public rclcpp::Logger
{
public:
explicit DerivedLogger(const rclcpp::Logger & logger)
: rclcpp::Logger(logger) {}
};
TEST_F(TestLoggingMacros, test_log_from_node) {
auto logger = rclcpp::get_logger("test_logging_logger");
EXPECT_TRUE(log_function(logger));
EXPECT_TRUE(log_function_const(logger));
EXPECT_TRUE(log_function_const_ref(logger));
DerivedLogger derived_logger(logger);
RCLCPP_INFO(derived_logger, "successful log from derived logger");
}

View File

@@ -129,6 +129,8 @@ TEST_F(TestMemoryStrategy, get_subscription_by_handle) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_subscription_by_handle(subscription_handle, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -181,6 +183,8 @@ TEST_F(TestMemoryStrategy, get_service_by_handle) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_service_by_handle(service_handle, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -228,6 +232,11 @@ TEST_F(TestMemoryStrategy, get_client_by_handle) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_client_by_handle(client_handle, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
// reset client handle before node destruction, otherwise it tries to access node
// via weak_ptr in its deleter
client_handle.reset();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -276,6 +285,8 @@ TEST_F(TestMemoryStrategy, get_timer_by_handle) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_timer_by_handle(timer_handle, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -375,6 +386,8 @@ TEST_F(TestMemoryStrategy, get_group_by_subscription) {
EXPECT_EQ(
callback_group,
memory_strategy()->get_group_by_subscription(subscription, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
// NodeBase(SubscriptionBase->rcl_node_t->NodeBase) is still alive.
EXPECT_EQ(
@@ -420,6 +433,8 @@ TEST_F(TestMemoryStrategy, get_group_by_service) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_group_by_service(service, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -459,6 +474,8 @@ TEST_F(TestMemoryStrategy, get_group_by_client) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_group_by_client(client, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -498,6 +515,8 @@ TEST_F(TestMemoryStrategy, get_group_by_timer) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_group_by_timer(timer, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,
@@ -536,6 +555,8 @@ TEST_F(TestMemoryStrategy, get_group_by_waitable) {
EXPECT_EQ(
nullptr,
memory_strategy()->get_group_by_waitable(waitable, weak_groups_to_nodes));
// Clear all handles to cleanup when node is destroyed
memory_strategy()->clear_handles();
} // Node goes out of scope
EXPECT_EQ(
nullptr,

View File

@@ -3689,6 +3689,115 @@ TEST_F(TestNode, get_publishers_subscriptions_info_by_topic) {
}, rclcpp::exceptions::InvalidTopicNameError);
}
// test that calling get_clients_info_by_service and get_servers_info_by_service
TEST_F(TestNode, get_clients_servers_info_by_service) {
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
std::string service_name = "test_service_info";
std::string fq_service_name = rclcpp::expand_topic_or_service_name(
service_name, node->get_name(), node->get_namespace(), true);
// Lists should be empty
EXPECT_TRUE(node->get_clients_info_by_service(fq_service_name).empty());
EXPECT_TRUE(node->get_servers_info_by_service(fq_service_name).empty());
// Add a client
rclcpp::QoSInitialization qos_initialization =
{
RMW_QOS_POLICY_HISTORY_KEEP_ALL,
10
};
rmw_qos_profile_t rmw_qos_profile_default =
{
RMW_QOS_POLICY_HISTORY_KEEP_ALL,
10,
RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
{1, 12345},
{20, 9887665},
RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC,
{5, 23456},
false
};
rclcpp::QoS qos = rclcpp::QoS(qos_initialization, rmw_qos_profile_default);
auto client = node->create_client<test_msgs::srv::Empty>(service_name, qos);
// Wait for the underlying RMW implementation to catch up with graph changes
auto client_is_generated =
[&]() {return node->get_clients_info_by_service(fq_service_name).size() == 1u;};
ASSERT_TRUE(wait_for_event(node, client_is_generated));
// List should have one item
auto clients_info = node->get_clients_info_by_service(fq_service_name);
ASSERT_EQ(clients_info.size(), (size_t) 1);
// Server list should be empty
EXPECT_TRUE(node->get_servers_info_by_service(fq_service_name).empty());
// Verify client info has the right data.
EXPECT_EQ(node->get_name(), clients_info[0].node_name());
EXPECT_EQ(node->get_namespace(), clients_info[0].node_namespace());
EXPECT_EQ("test_msgs/srv/Empty", clients_info[0].service_type());
EXPECT_EQ(rclcpp::EndpointType::Client, clients_info[0].endpoint_type());
EXPECT_TRUE(clients_info[0].endpoint_count() == 1 || clients_info[0].endpoint_count() == 2);
for(size_t i = 0; i < clients_info[0].endpoint_count(); i++) {
auto qos_profile = clients_info[0].qos_profiles()[i].get_rmw_qos_profile();
expect_qos_profile_eq(qos.get_rmw_qos_profile(), qos_profile, false);
}
// Add a service server
rclcpp::QoSInitialization qos_initialization2 =
{
RMW_QOS_POLICY_HISTORY_KEEP_LAST,
0
};
rmw_qos_profile_t rmw_qos_profile_default2 =
{
RMW_QOS_POLICY_HISTORY_KEEP_LAST,
0,
RMW_QOS_POLICY_RELIABILITY_RELIABLE,
RMW_QOS_POLICY_DURABILITY_VOLATILE,
{15, 1678},
{29, 2345},
RMW_QOS_POLICY_LIVELINESS_AUTOMATIC,
{5, 23456},
false
};
rclcpp::QoS qos2 = rclcpp::QoS(qos_initialization2, rmw_qos_profile_default2);
auto callback = [](test_msgs::srv::Empty_Request::ConstSharedPtr req,
test_msgs::srv::Empty_Response::ConstSharedPtr resp) {
(void)req;
(void)resp;
};
auto server = node->create_service<test_msgs::srv::Empty>(service_name, callback, qos2);
// Wait for the underlying RMW implementation to catch up with graph changes
auto server_is_generated =
[&]() {return node->get_servers_info_by_service(fq_service_name).size() == 1u;};
ASSERT_TRUE(wait_for_event(node, server_is_generated));
// List should have one item
auto servers_info = node->get_servers_info_by_service(fq_service_name);
ASSERT_EQ(servers_info.size(), (size_t) 1);
// Verify server info has the right data.
EXPECT_EQ(node->get_name(), servers_info[0].node_name());
EXPECT_EQ(node->get_namespace(), servers_info[0].node_namespace());
EXPECT_EQ("test_msgs/srv/Empty", servers_info[0].service_type());
EXPECT_EQ(rclcpp::EndpointType::Server, servers_info[0].endpoint_type());
EXPECT_TRUE(servers_info[0].endpoint_count() == 1 || servers_info[0].endpoint_count() == 2);
for(size_t i = 0; i < servers_info[0].endpoint_count(); i++) {
auto qos_profile = servers_info[0].qos_profiles()[i].get_rmw_qos_profile();
expect_qos_profile_eq(qos2.get_rmw_qos_profile(), qos_profile, false);
}
// Error cases
EXPECT_THROW(
{
clients_info = node->get_clients_info_by_service("13");
}, rclcpp::exceptions::InvalidServiceNameError);
EXPECT_THROW(
{
servers_info = node->get_servers_info_by_service("13");
}, rclcpp::exceptions::InvalidServiceNameError);
}
TEST_F(TestNode, callback_groups) {
auto node = std::make_shared<rclcpp::Node>("node", "ns");
size_t num_callback_groups_in_basic_node = 0;

View File

@@ -258,8 +258,10 @@ TEST(TestNodeOptions, append_parameter_override) {
auto options = rclcpp::NodeOptions().arguments(expected_args).use_global_arguments(false);
rclcpp::Parameter parameter("some_parameter", 10);
options.append_parameter_override("some_parameter", 10);
EXPECT_EQ(1u, options.parameter_overrides().size());
options.append_parameter_override(parameter);
EXPECT_EQ(2u, options.parameter_overrides().size());
EXPECT_EQ(std::string("some_parameter"), options.parameter_overrides()[0].get_name());
EXPECT_EQ(std::string("some_parameter"), options.parameter_overrides()[1].get_name());
}
TEST(TestNodeOptions, rcl_node_options_fini_error) {

View File

@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <atomic>
#include <chrono>
#include <memory>
#include <string>
@@ -20,6 +21,8 @@
#include "gtest/gtest.h"
#include "rclcpp/rclcpp.hpp"
using namespace std::chrono_literals;
class TestParameterEventHandler : public rclcpp::ParameterEventHandler
{
public:
@@ -443,3 +446,199 @@ TEST_F(TestNode, LastInFirstCallForParameterEventCallbacks)
param_handler->remove_parameter_event_callback(h2);
EXPECT_EQ(param_handler->num_event_callbacks(), 0UL);
}
TEST_F(TestNode, ConfigureNodesFilterAndCheckAddParameterEventCallback)
{
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
if (rmw_implementation_str != "rmw_fastrtps_cpp" && rmw_implementation_str != "rmw_connextdds") {
GTEST_SKIP() << "Content filter is now only supported in FastDDS and ConnextDDS.";
}
const std::string remote_node_name1 = "remote_node1";
const std::string remote_node_name2 = "remote_node2";
rclcpp::NodeOptions options;
options.enable_rosout(false);
auto remote_node1 = std::make_shared<rclcpp::Node>(
remote_node_name1, options);
auto remote_node2 = std::make_shared<rclcpp::Node>(
remote_node_name2, options);
const std::string remote_node1_param_name = "param_node1";
const std::string remote_node2_param_name = "param_node2";
remote_node1->declare_parameter(remote_node1_param_name, 10);
remote_node2->declare_parameter(remote_node2_param_name, "Default");
std::atomic_bool received_from_remote_node1{false};
std::atomic_bool received_from_remote_node2{false};
auto cb =
[&remote_node_name1, &remote_node_name2, &received_from_remote_node1,
&received_from_remote_node2]
(const rcl_interfaces::msg::ParameterEvent & parm) {
if (parm.node == "/" + remote_node_name1) {
received_from_remote_node1 = true;
} else if (parm.node == "/" + remote_node_name2) {
received_from_remote_node2 = true;
}
};
// Configure to only receive parameter events from remote_node_name2
ASSERT_TRUE(param_handler->configure_nodes_filter({remote_node_name2}));
auto handler = param_handler->add_parameter_event_callback(cb);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
executor.add_node(remote_node1);
executor.add_node(remote_node2);
auto wait_param_event = [&executor]
(std::chrono::seconds timeout, std::function<bool()> condition = nullptr) {
auto start = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start < timeout) {
executor.spin_some();
if (condition && condition()) {
break;
}
std::this_thread::sleep_for(100ms);
}
};
{
std::thread thread(wait_param_event, 1s, nullptr);
std::this_thread::sleep_for(100ms);
remote_node1->set_parameter(rclcpp::Parameter(remote_node1_param_name, 20));
remote_node2->set_parameter(rclcpp::Parameter(remote_node2_param_name, "abc"));
thread.join();
}
EXPECT_EQ(received_from_remote_node1, false);
EXPECT_EQ(received_from_remote_node2, true);
// Clear node filter and all parameter events from remote nodes should be received
ASSERT_TRUE(param_handler->configure_nodes_filter({}));
received_from_remote_node1 = false;
received_from_remote_node2 = false;
{
std::thread thread(
wait_param_event,
2s,
[&received_from_remote_node1, &received_from_remote_node2]() {
if (received_from_remote_node1 && received_from_remote_node2) {
return true;
}
return false;
});
std::this_thread::sleep_for(100ms);
remote_node1->set_parameter(rclcpp::Parameter(remote_node1_param_name, 30));
remote_node2->set_parameter(rclcpp::Parameter(remote_node2_param_name, "def"));
thread.join();
}
EXPECT_EQ(received_from_remote_node1, true);
EXPECT_EQ(received_from_remote_node2, true);
}
TEST_F(TestNode, ConfigureNodesFilterAndCheckAddParameterCallback)
{
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
if (rmw_implementation_str != "rmw_fastrtps_cpp" && rmw_implementation_str != "rmw_connextdds") {
GTEST_SKIP() << "Content filter is now only supported in FastDDS and ConnextDDS.";
}
const std::string remote_node_name1 = "remote_node1";
const std::string remote_node_name2 = "remote_node2";
rclcpp::NodeOptions options;
options.enable_rosout(false);
auto remote_node1 = std::make_shared<rclcpp::Node>(
remote_node_name1, options);
auto remote_node2 = std::make_shared<rclcpp::Node>(
remote_node_name2, options);
const std::string remote_node1_param_name = "param_node1";
const std::string remote_node2_param_name = "param_node2";
remote_node1->declare_parameter(remote_node1_param_name, 10);
remote_node2->declare_parameter(remote_node2_param_name, "Default");
std::atomic_bool received_from_remote_node1{false};
std::atomic_bool received_from_remote_node2{false};
auto cb_remote_node1 =
[&remote_node1_param_name, &received_from_remote_node1]
(const rclcpp::Parameter & parm) {
if (parm.get_name() == remote_node1_param_name) {
received_from_remote_node1 = true;
}
};
auto cb_remote_node2 =
[&remote_node2_param_name, &received_from_remote_node2]
(const rclcpp::Parameter & parm) {
if (parm.get_name() == remote_node2_param_name) {
received_from_remote_node2 = true;
}
};
// Configure to only receive parameter events from remote_node_name2
ASSERT_TRUE(param_handler->configure_nodes_filter({remote_node_name2}));
auto handler1 = param_handler->add_parameter_callback(
remote_node1_param_name, cb_remote_node1, remote_node_name1);
auto handler2 = param_handler->add_parameter_callback(
remote_node2_param_name, cb_remote_node2, remote_node_name2);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
executor.add_node(remote_node1);
executor.add_node(remote_node2);
auto wait_param_event = [&executor]
(std::chrono::seconds timeout, std::function<bool()> condition = nullptr) {
auto start = std::chrono::steady_clock::now();
while (std::chrono::steady_clock::now() - start < timeout) {
executor.spin_some();
if (condition && condition()) {
break;
}
std::this_thread::sleep_for(100ms);
}
};
{
std::thread thread(wait_param_event, 1s, nullptr);
std::this_thread::sleep_for(100ms);
remote_node1->set_parameter(rclcpp::Parameter(remote_node1_param_name, 20));
remote_node2->set_parameter(rclcpp::Parameter(remote_node2_param_name, "abc"));
thread.join();
}
EXPECT_EQ(received_from_remote_node1, false);
EXPECT_EQ(received_from_remote_node2, true);
// Clear node filter and all parameter events from remote nodes should be received
ASSERT_TRUE(param_handler->configure_nodes_filter({}));
received_from_remote_node1 = false;
received_from_remote_node2 = false;
{
std::thread thread(
wait_param_event,
2s,
[&received_from_remote_node1, &received_from_remote_node2]() {
if (received_from_remote_node1 && received_from_remote_node2) {
return true;
}
return false;
});
std::this_thread::sleep_for(100ms);
remote_node1->set_parameter(rclcpp::Parameter(remote_node1_param_name, 30));
remote_node2->set_parameter(rclcpp::Parameter(remote_node2_param_name, "def"));
thread.join();
}
EXPECT_EQ(received_from_remote_node1, true);
EXPECT_EQ(received_from_remote_node2, true);
}

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <chrono>
#include <memory>
#include <string>
#include <utility>
@@ -119,3 +120,32 @@ TEST_F(TestParameterService, describe_parameters) {
EXPECT_EQ(0u, parameter_descs.size());
}
}
TEST_F(TestParameterService, parameter_descriptor) {
{
rclcpp::ParameterDescription param_description;
rclcpp::ParameterValue param_value(1);
param_description.set_name("int_parameter");
param_description.set_type(2);
param_description.set_description_text("description");
param_description.set_additional_constraints("constraints");
param_description.set_read_only(false);
param_description.set_integer_description_range(0, 10, 1);
auto param = param_description.build();
param_description.declare_parameter(param_value, node);
EXPECT_EQ("int_parameter", param.name);
EXPECT_EQ(rclcpp::ParameterType::PARAMETER_INTEGER, param.type);
EXPECT_EQ("description", param.description);
EXPECT_EQ("constraints", param.additional_constraints);
EXPECT_EQ(0u, param.read_only);
EXPECT_EQ(0u, param.dynamic_typing);
EXPECT_EQ(0u, param.integer_range.at(0).from_value);
EXPECT_EQ(10, param.integer_range.at(0).to_value);
EXPECT_EQ(1, param.integer_range.at(0).step);
ASSERT_EQ(1, client->get_parameter("int_parameter", 0));
}
}

View File

@@ -199,7 +199,7 @@ TEST_F(TestPublisher, test_publisher_with_system_default_qos) {
using test_msgs::msg::Empty;
ASSERT_NO_THROW(
{
auto publisher = node->create_publisher<Empty>("topic", rclcpp::SystemDefaultsQoS());
auto publisher = node->create_publisher<Empty>("topic", rclcpp::SystemDefaultsQoS(), options);
});
}

View File

@@ -219,6 +219,18 @@ TEST(TestQoS, policy_name_from_kind) {
EXPECT_EQ(
"LIFESPAN_QOS_POLICY",
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_LIFESPAN));
EXPECT_EQ(
"DEPTH_QOS_POLICY",
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_DEPTH));
EXPECT_EQ(
"LIVELINESS_LEASE_DURATION_QOS_POLICY",
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_LIVELINESS_LEASE_DURATION));
EXPECT_EQ(
"AVOID_ROS_NAMESPACE_CONVENTIONS_QOS_POLICY",
rclcpp::qos_policy_name_from_kind(RMW_QOS_POLICY_AVOID_ROS_NAMESPACE_CONVENTIONS));
}
TEST(TestQoS, qos_check_compatible)
@@ -272,3 +284,15 @@ TEST(TestQoS, qos_check_compatible)
}
}
}
TEST(TestQoS, from_rmw_validity)
{
rmw_qos_profile_t invalid_qos;
memset(&invalid_qos, 0, sizeof(invalid_qos));
unsigned int n = 999;
memcpy(&invalid_qos.history, &n, sizeof(n));
EXPECT_THROW({
rclcpp::QoSInitialization::from_rmw(invalid_qos);
}, std::invalid_argument);
}

View File

@@ -173,3 +173,12 @@ TEST_F(TestRate, incorrect_constuctor) {
rclcpp::Rate rate(rclcpp::Duration(-1, 0)),
std::invalid_argument("period must be greater than 0"));
}
TEST(TestRateBasic, invalid_context) {
rclcpp::init(0, nullptr);
rclcpp::Rate rate(1.0);
ASSERT_TRUE(rate.sleep());
rclcpp::shutdown();
EXPECT_NO_THROW(rate.sleep());
ASSERT_FALSE(rate.sleep());
}

View File

@@ -164,3 +164,15 @@ TEST(TestRingBufferImplementation, test_buffer_clear) {
EXPECT_EQ('c', c);
EXPECT_EQ('d', d);
}
TEST(TestRingBufferImplementation, handle_nullptr_deletion) {
rclcpp::experimental::buffers::RingBufferImplementation<std::unique_ptr<int>> rb(3);
rb.enqueue(std::make_unique<int>(42));
rb.enqueue(nullptr); // intentionally enqueuing nullptr
rb.enqueue(std::make_unique<int>(84));
auto all_data = rb.get_all_data();
EXPECT_EQ(3u, all_data.size());
EXPECT_EQ(42, *(all_data[0]));
EXPECT_EQ(nullptr, all_data[1]);
EXPECT_EQ(84, *(all_data[2]));
}

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <chrono>
#include <string>
#include <memory>
#include <utility>
@@ -385,17 +386,20 @@ TEST_F(TestService, server_qos_depth) {
std::this_thread::sleep_for(10ms);
}
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(server_node);
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);
executor.spin_some();
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);
executor.spin_some();
EXPECT_EQ(server_cb_count_, server_qos_profile.depth());
}

View File

@@ -15,6 +15,7 @@
#include <rcl/service_introspection.h>
#include <rmw/rmw.h>
#include <chrono>
#include <map>
#include <string>
@@ -86,6 +87,9 @@ protected:
TEST_F(TestServiceIntrospection, service_introspection_nominal)
{
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
request->set__int64_value(42);
@@ -105,7 +109,7 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
auto future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
BasicTypes::Response::SharedPtr response = future.get();
ASSERT_EQ(response->bool_value, false);
@@ -114,7 +118,7 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
// wrap up work to get all the service_event messages
auto start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
std::map<uint8_t, std::shared_ptr<const BasicTypes::Event>> event_map;
@@ -173,6 +177,9 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
service->configure_introspection(
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
ASSERT_EQ(sub->get_publisher_count(), 0);
auto request = std::make_shared<BasicTypes::Request>();
@@ -181,10 +188,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
auto future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
auto start = std::chrono::steady_clock::now();
while ((std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 0U);
@@ -205,10 +212,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 2 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 2U);
@@ -229,10 +236,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 2 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 2U);
@@ -253,10 +260,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 4U);
}
@@ -268,6 +275,9 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
service->configure_introspection(
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
@@ -281,10 +291,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
auto future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
auto start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {
@@ -309,10 +319,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {
@@ -349,10 +359,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {
@@ -389,10 +399,10 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
future = client->async_send_request(request);
ASSERT_EQ(
rclcpp::FutureReturnCode::SUCCESS,
rclcpp::spin_until_future_complete(node, future, timeout));
executor.spin_until_future_complete(future, timeout));
start = std::chrono::steady_clock::now();
while (events.size() < 4 && (std::chrono::steady_clock::now() - start) < timeout) {
rclcpp::spin_some(node);
executor.spin_some();
}
EXPECT_EQ(events.size(), 4U);
for (const auto & event : events) {

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <functional>
#include <memory>
@@ -24,6 +25,7 @@
#include <vector>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../mocking_utils/patch.hpp"
@@ -668,3 +670,212 @@ TEST_P(TestSubscriptionInvalidIntraprocessQos, test_subscription_throws_intrapro
options);},
std::runtime_error("Unrecognized IntraProcessSetting value"));
}
/*
Testing disable and enable callbacks on subscriptions
*/
TEST_F(TestSubscription, disable_enable_callbacks)
{
const std::string topic_name = "~/test_disable";
initialize();
std::atomic<int> callback_count{0};
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {
++callback_count;
};
rclcpp::SubscriptionOptions sub_options;
sub_options.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
auto sub = node_->create_subscription<test_msgs::msg::Empty>(
topic_name, 10U, callback, sub_options);
rclcpp::PublisherOptions pub_options;
pub_options.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
auto pub = node_->create_publisher<test_msgs::msg::Empty>(topic_name, 10U, pub_options);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node_);
// Wait for discovery
auto start = std::chrono::steady_clock::now();
while ((pub->get_subscription_count() == 0U || sub->get_publisher_count() == 0U) &&
std::chrono::steady_clock::now() - start < 10s)
{
executor.spin_some();
std::this_thread::sleep_for(10ms);
}
// Publish and verify callback is called
{
test_msgs::msg::Empty msg;
pub->publish(msg);
}
start = std::chrono::steady_clock::now();
while (callback_count.load() == 0 && std::chrono::steady_clock::now() - start < 30s) {
executor.spin_some();
std::this_thread::sleep_for(10ms);
}
EXPECT_EQ(1, callback_count.load());
// Disable callbacks
sub->disable_callbacks();
// Publish again and verify callback is NOT called
{
test_msgs::msg::Empty msg;
pub->publish(msg);
}
start = std::chrono::steady_clock::now();
auto initial_count = callback_count.load();
while (std::chrono::steady_clock::now() - start < 500ms) {
executor.spin_some();
std::this_thread::sleep_for(10ms);
}
EXPECT_EQ(initial_count, callback_count.load()); // Should not increase
// Enable callbacks
sub->enable_callbacks();
// Publish again and verify callback IS called
{
test_msgs::msg::Empty msg;
pub->publish(msg);
}
start = std::chrono::steady_clock::now();
while (callback_count.load() == initial_count && std::chrono::steady_clock::now() - start < 30s) {
executor.spin_some();
std::this_thread::sleep_for(10ms);
}
EXPECT_EQ(callback_count.load(), initial_count + 1);
}
TEST_F(TestSubscription, disable_enable_callbacks_intra_process)
{
const std::string topic_name = "~/test_disable_ipc";
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
std::atomic<int> callback_count{0};
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {
++callback_count;
};
auto sub = node_->create_subscription<test_msgs::msg::Empty>(topic_name, 10U, callback);
auto pub = node_->create_publisher<test_msgs::msg::Empty>(topic_name, 10U);
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node_);
// Publish and verify callback is called
{
test_msgs::msg::Empty msg;
pub->publish(msg);
}
auto start = std::chrono::steady_clock::now();
while (callback_count.load() == 0 && std::chrono::steady_clock::now() - start < 30s) {
executor.spin_some();
std::this_thread::sleep_for(10ms);
}
EXPECT_EQ(1, callback_count.load());
// Disable callbacks
sub->disable_callbacks();
// Publish again and verify callback is NOT called
{
test_msgs::msg::Empty msg;
pub->publish(msg);
}
start = std::chrono::steady_clock::now();
auto initial_count = callback_count.load();
while (std::chrono::steady_clock::now() - start < 500ms) {
executor.spin_some();
std::this_thread::sleep_for(10ms);
}
EXPECT_EQ(initial_count, callback_count.load());
// Enable callbacks
sub->enable_callbacks();
// Publish again and verify callback IS called
{
test_msgs::msg::Empty msg;
pub->publish(msg);
}
start = std::chrono::steady_clock::now();
while (callback_count.load() == initial_count && std::chrono::steady_clock::now() - start < 30s) {
executor.spin_some();
std::this_thread::sleep_for(10ms);
}
EXPECT_EQ(callback_count.load(), initial_count + 1);
}
TEST_F(TestSubscription, disable_enable_event_callbacks)
{
initialize();
std::atomic<int> deadline_callback_count{0};
std::atomic<int> liveliness_callback_count{0};
rclcpp::SubscriptionOptions subscription_options;
subscription_options.event_callbacks.deadline_callback =
[&deadline_callback_count](rclcpp::QOSDeadlineRequestedInfo &) {
++deadline_callback_count;
};
subscription_options.event_callbacks.liveliness_callback =
[&liveliness_callback_count](rclcpp::QOSLivelinessChangedInfo &) {
++liveliness_callback_count;
};
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
const auto qos = rclcpp::QoS(10U).deadline(100ms);
auto subscription = node_->create_subscription<test_msgs::msg::Empty>(
"test_event_callbacks_topic", qos, do_nothing, subscription_options);
const auto & event_handlers = subscription->get_event_handlers();
// Initially, both callbacks counters should be zero
EXPECT_EQ(deadline_callback_count, 0);
EXPECT_EQ(liveliness_callback_count, 0);
// Execute events and disable all event handlers
for (const auto & [event_type, handler] : event_handlers) {
if (handler) {
const std::shared_ptr<void> data = handler->take_data();
handler->execute(data);
EXPECT_NO_THROW(handler->disable());
}
}
// Expect both callbacks to have been called once since they should be enabled by default
EXPECT_EQ(deadline_callback_count, 1);
EXPECT_EQ(liveliness_callback_count, 1);
// Execute events one more time and enable all event handlers
for (const auto & [event_type, handler] : event_handlers) {
if (handler) {
const std::shared_ptr<void> data = handler->take_data();
handler->execute(data);
EXPECT_NO_THROW(handler->enable());
}
}
// Expect no change since they were disabled
EXPECT_EQ(deadline_callback_count, 1);
EXPECT_EQ(liveliness_callback_count, 1);
// Execute events one more time
for (const auto & [event_type, handler] : event_handlers) {
if (handler) {
const std::shared_ptr<void> data = handler->take_data();
handler->execute(data);
}
}
EXPECT_EQ(deadline_callback_count, 2);
EXPECT_EQ(liveliness_callback_count, 2);
}

View File

@@ -69,11 +69,13 @@ public:
{
using clock = std::chrono::system_clock;
auto start = clock::now();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
while (!condition()) {
if ((clock::now() - start) > timeout) {
return false;
}
rclcpp::spin_some(node);
executor.spin_some();
}
return true;
}

View File

@@ -158,3 +158,15 @@ TEST(TypesupportHelpersTest, test_throw_exception_with_invalid_type) {
rclcpp::get_action_typesupport_handle(invalid_type, "rosidl_typesupport_cpp", *library),
std::runtime_error);
}
TEST(TypesupportHelpersTest, throws_exception_if_filetype_has_multiple_slashes_at_start) {
EXPECT_ANY_THROW(rclcpp::extract_type_identifier("//name_with_slashes_at_start"));
}
TEST(TypesupportHelpersTestV3, ProcessesValidTypeWithWhitespace) {
std::string package, middle, name;
std::tie(package, middle, name) = rclcpp::extract_type_identifier(" package/ name ");
EXPECT_EQ(package, "package");
EXPECT_TRUE(middle.empty());
EXPECT_EQ(name, "name");
}

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <chrono>
#include <memory>
#include <string>
#include <vector>
@@ -107,3 +108,29 @@ TEST(TestUtilities, wait_for_message_twice_one_sub) {
rclcpp::shutdown();
}
TEST(TestUtilities, wait_for_last_message) {
rclcpp::init(0, nullptr);
auto node = std::make_shared<rclcpp::Node>("wait_for_last_message_node");
auto qos = rclcpp::QoS(1).reliable().transient_local();
using MsgT = test_msgs::msg::Strings;
auto pub = node->create_publisher<MsgT>("wait_for_last_message_topic", qos);
pub->publish(*get_messages_strings()[0]);
MsgT out;
auto received = false;
auto wait = std::async(
[&]() {
auto ret = rclcpp::wait_for_message(out, node, "wait_for_last_message_topic", 5s, qos);
EXPECT_TRUE(ret);
received = true;
});
ASSERT_NO_THROW(wait.get());
ASSERT_TRUE(received);
EXPECT_EQ(out, *get_messages_strings()[0]);
rclcpp::shutdown();
}

View File

@@ -3,6 +3,39 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
30.1.4 (2025-12-23)
-------------------
30.1.3 (2025-11-18)
-------------------
* Fix REP url locations (`#2987 <https://github.com/ros2/rclcpp/issues/2987>`_)
* Contributors: Tim Clephas
30.1.2 (2025-10-21)
-------------------
* it misses the iterator second to lock the weakptr. (`#2958 <https://github.com/ros2/rclcpp/issues/2958>`_)
* try aborting before canceling 1st on dtor of ServerGoalHandle. (`#2953 <https://github.com/ros2/rclcpp/issues/2953>`_)
* Contributors: Tomoya Fujita
30.1.1 (2025-09-11)
-------------------
* deprecate rclcpp::spin_some and rclcpp::spin_all (`#2848 <https://github.com/ros2/rclcpp/issues/2848>`_)
* Contributors: Alberto Soragna
30.1.0 (2025-07-29)
-------------------
* fix cmake deprecation (`#2914 <https://github.com/ros2/rclcpp/issues/2914>`_)
* Contributors: mosfet80
30.0.0 (2025-07-01)
-------------------
29.6.1 (2025-06-23)
-------------------
* Replace std::default_random_engine with std::mt19937 (rolling) (`#2843 <https://github.com/ros2/rclcpp/issues/2843>`_)
* Added missing chrono includes (`#2854 <https://github.com/ros2/rclcpp/issues/2854>`_)
* Contributors: Alejandro Hernández Cordero, keeponoiro
29.6.0 (2025-04-25)
-------------------

View File

@@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.5)
cmake_minimum_required(VERSION 3.20)
project(rclcpp_action)

View File

@@ -1,10 +1,10 @@
This document is a declaration of software quality for the `rclcpp_action` package, based on the guidelines in [REP-2004](https://www.ros.org/reps/rep-2004.html).
This document is a declaration of software quality for the `rclcpp_action` package, based on the guidelines in [REP-2004](https://reps.openrobotics.org/rep-2004/).
# rclcpp_action Quality Declaration
The package `rclcpp_action` claims to be in the **Quality Level 1** category when it is used with a **Quality Level 1** middleware.
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://www.ros.org/reps/rep-2004.html).
Below are the rationales, notes, and caveats for this claim, organized by each requirement listed in the [Package Requirements for Quality Level 1 in REP-2004](https://reps.openrobotics.org/rep-2004/).
## Version Policy [1]
@@ -53,7 +53,7 @@ All pull requests will be peer-reviewed, check [ROS 2 Developer Guide](https://d
### Continuous Integration [2.iv]
All pull requests must pass CI on all [tier 1 platforms](https://www.ros.org/reps/rep-2000.html#support-tiers)
All pull requests must pass CI on all [tier 1 platforms](https://reps.openrobotics.org/rep-2000/#support-tiers)
Currently nightly results can be seen here:
@@ -179,7 +179,7 @@ It is **Quality Level 1**, see its [Quality Declaration document](https://github
## Platform Support [6]
`rclcpp_action` supports all of the tier 1 platforms as described in [REP-2000](https://www.ros.org/reps/rep-2000.html#support-tiers), and tests each change against all of them.
`rclcpp_action` supports all of the tier 1 platforms as described in [REP-2000](https://reps.openrobotics.org/rep-2000/#support-tiers), and tests each change against all of them.
Currently nightly build status can be seen here:
* [linux-aarch64_release](https://ci.ros2.org/view/nightly/job/nightly_linux-aarch64_release/lastBuild/rclcpp_action/)
@@ -191,4 +191,4 @@ Currently nightly build status can be seen here:
### Vulnerability Disclosure Policy [7.i]
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://www.ros.org/reps/rep-2006.html).
This package conforms to the Vulnerability Disclosure Policy in [REP-2006](https://reps.openrobotics.org/rep-2006/).

View File

@@ -350,7 +350,7 @@ public:
return;
}
goal_handle = it->lock();
goal_handle = it->second.lock();
}
if (goal_handle) {

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