Compare commits

...

81 Commits

Author SHA1 Message Date
Tomoya Fujita
546f3ae655 use Reader/Writer lock for Ring Buffer.
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-05-07 10:44:30 -07:00
Alejandro Hernández Cordero
4f507751e1 Removed deprecated rcpputils Path (#2834)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-05-06 00:11:49 +02:00
Alex Youngs
7bd14d812c Add range constraints for applicable array parameters (#2828)
Signed-off-by: Alex Youngs <alexyoungs@hatchbed.com>
2025-05-05 14:39:12 +02:00
Michael Carlstrom
e97d569f75 Update RingBufferImplementation to clear internal data. (#2837)
* Fix clear()

Signed-off-by: Michael Carlstrom <rmc@carlstrom.com>

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

Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
Signed-off-by: Michael Carlstrom <rmc@carlstrom.com>

---------

Signed-off-by: Michael Carlstrom <rmc@carlstrom.com>
Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2025-05-04 12:49:21 -07:00
Alejandro Hernández Cordero
f81caaca49 Removed deprecated cancel_sleep_or_wait (#2836)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-05-02 15:10:07 +02:00
Tomoya Fujita
127a10e8c8 introduce rcl_lifecycle_get_transition_label_by_id(). (#2827)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-05-01 13:13:05 -07:00
Christophe Bedard
b1ec85df16 Add missing 's' to 'NodeParametersInterface' in doc/comment (#2831)
Signed-off-by: Christophe Bedard <bedard.christophe@gmail.com>
2025-04-30 09:41:39 -07:00
Tomoya Fujita
c89a2b1013 subordinate node consistent behavior and update docstring. (#2822)
* subordinate node consistent behavior and update docstring.

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

* add subnode_parameter_operation test.

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

* typo fixes.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-04-29 13:41:52 -07:00
Scott K Logan
1a282064a9 29.6.0 2025-04-25 15:04:18 -05:00
Tomoya Fujita
8b9691f42d throws std::invalid_argument if ParameterEvent is NULL. (#2814)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-04-24 10:30:20 -07:00
Alejandro Hernández Cordero
40e3fb78db Removed clang warnings (#2823)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-04-24 19:24:29 +02:00
Scott K Logan
11afffad50 29.5.0 2025-04-18 16:01:12 -05:00
Pedro de Azeredo
8e49befce9 Fix race condition (#2819)
Signed-off-by: pedroazeredo04 <pedro_azeredo@usp.br>
Signed-off-by: Pedro Nogueira <pedro_azeredo@usp.br>
2025-04-18 22:31:36 +02:00
Tanishq Chaudhary
f78ed952b2 remove redundant typesupport check in serialization module (#2808)
Signed-off-by: Tanishq Chaudhary <tanishqchaudhary101010@gmail.com>
2025-04-14 23:37:54 +02:00
Tomoya Fujita
2420c48514 remove get_typesupport_handle implementation. (#2806)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-04-13 21:47:18 -07:00
Tomoya Fujita
c01602f7a6 use NodeParameterInterface instead of /parameter_event to update "use… (#2378)
* use NodeParameterInterface instead of /parameter_event to update "use_sim_time"

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

* re-enable ParameterMutationRecursionGuard.

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

* adjust test cases.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-04-11 09:01:48 -07:00
Tomoya Fujita
6ad551a5cc use std::recursive_mutex for action requests. (#2798)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-04-10 20:43:47 -07:00
Tomoya Fujita
0162861fa1 remove cancel_clock_executor_promise_. (#2797)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
2025-04-10 14:26:34 -07:00
Tomoya Fujita
6040228d13 enable parameter update recursively only when QoS override parameters. (#2742)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-04-09 11:32:11 +02:00
Michael Carroll
892cae9915 29.4.0 2025-04-04 13:42:38 +00:00
Michael Carroll
14c8dd1072 Changelog.
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2025-04-04 13:42:12 +00:00
Leander Stephen D'Souza
011b878554 Removed trailing whitespace from the codebase. (#2791)
Signed-off-by: Leander Stephen D'Souza <leanderdsouza1234@gmail.com>
2025-04-03 20:33:36 -07:00
Tomoya Fujita
de4c7fcd77 remove .github/ISSUE_TEMPLATE.md (old version of templates) (#2792)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-03-31 13:42:36 -07:00
Alejandro Hernández Cordero
fb8e1dda25 Remove warning (#2790)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-03-31 15:22:47 +02:00
Abhishek Kashyap
1dfefe5b63 Expanded docstring of get_rmw_qos_profile() (#2787)
* expanded docstring to include info on profile

Signed-off-by: Abhishek Kashyap <abhishek47kashyap@gmail.com>

* more succinct docstring, also restores one-line summary

Signed-off-by: Abhishek Kashyap <abhishek47kashyap@gmail.com>

---------

Signed-off-by: Abhishek Kashyap <abhishek47kashyap@gmail.com>
2025-03-30 11:32:05 -07:00
Tomoya Fujita
ce86ef7e62 Harden rclcpp_action::convert(). (#2786)
* Harden rclcpp_action::convert().

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

* update docstring.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-03-29 21:00:41 -07:00
Tomoya Fujita
7b6ee8a2e7 should pull valid transition before trying to change the state. (#2774)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-03-28 13:14:29 +01:00
Alejandro Hernández Cordero
aae375be91 Set envars to run tests with rmw_zenoh_cpp with multicast discovery (#2776)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
Co-authored-by: Yadunund <yadunund@gmail.com>
2025-03-26 11:00:32 +01:00
Janosch Machowinski
1564fc23c6 fix: Compilefix for clang (#2775)
clang does not like :
    __attribute__ ((visibility("default")))
    [[deprecated("Don't like'")]]
while
    [[deprecated("Don't like'")]]
    __attribute__ ((visibility("default")))
is fine...

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
2025-03-20 21:49:17 +01:00
Tomoya Fujita
9ff1201ded add exception doc for configure_introspection. (#2773)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-03-19 08:58:41 -07:00
Janosch Machowinski
b9b46c5871 feat: Add ClockWaiter and ClockConditionalVariable (#2691)
* feat: Add ClockWaiter and ClockConditionalVariable

Added two new synchronization primitives to perform waits
using an rclcpp::Clock.

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>

* fix: Deprecate broken API


Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>

---------

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
2025-03-19 14:40:01 +01:00
Janosch Machowinski
30e61c955d doc: Added warning to not instantiate Clock directly with RCL_ROS_TIME (#2768)
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: Janosch Machowinski <jmachowinski@users.noreply.github.com>
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-03-14 15:22:47 -07:00
Barry Xu
687057ffb6 Add new interfaces to enable introspection for action (#2743)
* Add new interfaces to enable intropsection for action

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

* Correct some comments

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

---------

Signed-off-by: Barry Xu <barry.xu@sony.com>
2025-03-12 08:27:06 -07:00
Alejandro Hernández Cordero
9db7fa2ccb Use rmw_event_type_is_supported in test_qos_event (#2761)
Signed-off-by: Alejandro Hernandez Cordero <ahcorde@gmail.com>
2025-03-11 19:39:16 +01:00
mergify[bot]
387bf7bb51 add NO_UNDEFINED_SYMBOLS to rclcpp_components_register_node cmake macro (#2746) (#2764)
Signed-off-by: Jonas Otto <jonas.otto@ipa.fraunhofer.de>
Signed-off-by: Jonas Otto <jonas@jonasotto.com>
(cherry picked from commit c31daa608d)

Co-authored-by: Jonas Otto <jonas@jonasotto.com>
2025-03-11 16:35:49 +01:00
Barry Xu
9db7659dab Implement action generic client (#2759)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2025-03-07 18:01:25 +01:00
Barry Xu
48a4761faa Support action typesupport helper (#2750)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2025-02-28 16:18:49 +01:00
Tomoya Fujita
6c10f941d3 use maybe_unused attribute for the portability. (#2758)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-02-27 10:44:29 +01:00
Janosch Machowinski
9c493c4615 Executor strong reference fix (#2745)
https://github.com/ros2/rclcpp/issues/2678 reported that the executor
was holding strong references to entities during execution. This commit
adds a regression test for this.

* fix(Executor): Don't hold strong references to entities during spin

This fixes a bug, were dropping an entity during a callback would
not prevent further callbacks. Note, there might still be a race
in the case of the mutithreaded executor.

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
2025-02-19 18:12:09 -06:00
Janosch Machowinski
34cc960124 Cleanup of https://github.com/ros2/rclcpp/pull/2683 (#2714)
Signed-off-by: Janosch Machowinski <j.machowinski@cellumation.com>
2025-02-19 14:23:17 +01:00
Barry Xu
06d400d795 Fix typo in doc section for get_service_typesupport_handle (#2751)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2025-02-19 13:40:31 +01:00
Janosch Machowinski
605251bd71 Test case and fix for for https://github.com/ros2/rclcpp/issues/2652 (#2713)
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Signed-off-by: Francisco Martín Rico <fmrico@gmail.com>
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Francisco Martín Rico <fmrico@gmail.com>
2025-02-11 11:16:02 -06:00
Janosch Machowinski
f6b80abe4a fix(timer): Delete node, after executor thread terminated (#2737)
This fixes a potential race leading to a segfault. The segfault would
happen, if the tear down of the test would occur before the timer thread
stopped the spinning of the executor.

Also simplifies the test code, as the cancel of the executor is now in
the TearDown().

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
2025-02-10 12:45:30 -06:00
Tomoya Fujita
2cc43b3274 update doc section for spin_xxx methods. (#2730)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: William Woodall <william@osrfoundation.org>
2025-02-04 17:37:13 -06:00
Janosch Machowinski
6069c3df10 fix: Expose timers used by rclcpp::Waitables (#2699)
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
2025-01-30 18:04:41 +01:00
Tomoya Fujita
687adf494b use rmw_qos_profile_rosout_default instead of rcl. (#2663)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-01-30 11:12:30 +01:00
Janosch Machowinski
e7afaa636a fix(Executor): Fixed entities not beeing executed after just beeing added (#2724)
Fixes https://github.com/ros2/rclcpp/issues/2589

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Janosch Machowinski <J.Machowinski@cellumation.com>
2025-01-27 08:53:41 -08:00
Yuyuan Yuan
2d1b770e85 fix: make the loop condition align with the description (#2726)
Signed-off-by: yuanyuyuan <az6980522@gmail.com>
2025-01-14 10:32:17 -08:00
Tomoya Fujita
80768ed14e ComponentManager should just ignore unknown extra argument in the bas… (#2723)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-01-10 10:07:27 +01:00
Tomoya Fujita
9cabd69412 Collect log messages from rcl, and reset. (#2720)
* Collect log messages from rcl, and reset.

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

* call rcl_reset_error once the error message is collected.

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

* address CI failure, error is already collected and reset.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2025-01-07 19:28:02 -08:00
Chris Lalancette
a0a2a067d8 29.3.0 2024-12-20 16:16:13 +00:00
Chris Lalancette
094618a044 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-12-20 16:16:06 +00:00
Jeffery Hsu
f5e08c2d0c Fix transient local IPC publish (#2708)
* Fix transient local publish when inter and intra process communications are both present.

* Apply the fix to TypeAdapted signature

* Add an executor to intra_process_inter_process_mix_transient_local test case to enable inter process publishing test

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-20 09:10:32 -05:00
Tomoya Fujita
016cfeac99 apply actual QoS from rmw to the IPC publisher. (#2707)
* apply actual QoS from rmw to the IPC publisher.

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

* address uncrustify warning.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-15 21:46:12 -08:00
Steve Macenski
a13e16e2cb Adding in topic name to logging on IPC issues (#2706)
* Adding in topic name to logging on IPC issues

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

* Update test matching output logging

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

* adding in single quotes

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>

---------

Signed-off-by: Steve Macenski <stevenmacenski@gmail.com>
2024-12-14 09:04:55 -08:00
rcp1
aad8cb53ad Add parsing for rest of obvious boolean extra arguments and throw for unsupported ones (#2685)
Signed-off-by: Robin Petereit <robin.petereit@tum.de>
2024-12-13 08:18:41 -06:00
Tomoya Fujita
8c0161a07f fix TestTimeSource.ROS_time_valid_attach_detach. (#2700)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-12-09 13:15:40 -08:00
Patrick Roncagliolo
a7f05a904a Update docstring for rclcpp::Node::now() (#2696)
Signed-off-by: Patrick Roncagliolo <ronca.pat@gmail.com>
2024-12-04 09:24:50 -08:00
Chris Lalancette
d7245365ed Re-enable executor test on rmw_connextdds. (#2693)
It supports the events executor now, so re-enable the test.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-12-03 00:03:03 -08:00
Chris Lalancette
3310f9eaed Fix warnings on Windows. (#2692)
For reasons I admit I do not understand, the deprecation
warnings for StaticSingleThreadedExecutor on Windows
happen when we construct a shared_ptr for it in the tests.
If we construct a regular object, then it is fine.  Luckily
this test does not require a shared_ptr, so just make it
a regular object here, which rixes the warning.

While we are in here, make all of the tests camel case to
be consistent.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-12-03 00:02:37 -08:00
Nathan Wiebe Neufeldt
785a70d604 Make ament_cmake a buildtool dependency (#2689)
* Make ament_cmake a buildtool dependency

The `ament_cmake` package isn't needed at runtime and so should only be
listed as a `buildtool_dependency`, as is done in most other packages.

* Remove the ament_cmake dependency entirely

Since there's already a buildtool dependency on ament_cmake_ros, having
ament_cmake as well is redundant.

Signed-off-by: Nathan Wiebe Neufeldt <nwiebeneufeldt@clearpath.ai>
2024-12-01 19:49:35 -05:00
Chris Lalancette
9984197c29 Omnibus fixes for running tests with Connext. (#2684)
* Omnibus fixes for running tests with Connext.

When running the tests with RTI Connext as the default
RMW, some of the tests are failing.  There are three
different failures fixed here:

1.  Setting the liveliness duration to a value smaller than
a microsecond causes Connext to throw an error.  Set it to
a millisecond.

2.  Using the SystemDefaultsQoS sets the QoS to KEEP_LAST 1.
Connext is somewhat slow in this regard, so it can be the case
that we are overwriting a previous service introspection event
with the next one.  Switch to the ServicesDefaultQoS in the test,
which ensures we will not lose events.

3.  Connext is slow to match publishers and subscriptions.  Thus,
when creating a subscription "on-the-fly", we should wait for the
publisher to match it before expecting the subscription to actually
receive data from it.

With these fixes in place, the test_client_common, test_generic_service,
test_service_introspection, and test_executors tests all pass for
me with rmw_connextdds.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* Fixes for executors.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* One more fix for services.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* More fixes for service_introspection.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* More fixes for introspection.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

---------

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-11-30 11:25:06 -08:00
jmachowinski
e9b1004247 fix(Executor): Fix segfault if callback group is deleted during rmw_wait (#2683)
Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
2024-11-30 11:04:52 -08:00
Chris Lalancette
e64627004f Remove CODEOWNERS and the rolling-to-master job. (#2686)
They are both legacy from times past, and are both
no longer serving their intended purposes.  Just remove them.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-11-26 15:51:01 -05:00
Chris Lalancette
83c282a161 29.2.0 2024-11-25 14:38:16 +00:00
Chris Lalancette
226044022a Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-11-25 14:38:05 +00:00
Tomoya Fujita
7e9ff5f4c7 accept custom allocator for LoanedMessage. (#2672)
* accept custom allocator for LoanedMessage.

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

* move message allocator using directives to public.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-11-21 13:50:59 -08:00
Chris Lalancette
64dd644469 29.1.0 2024-11-20 15:53:57 +00:00
Chris Lalancette
322b5f5d79 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-11-20 15:53:48 +00:00
Tomoya Fujita
e854bb29cd a couple of typo fixes in doc section for LoanedMessage. (#2676)
* rephrase doc section of LoanedMessage Class.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com>
2024-11-19 07:14:45 -05:00
Christophe Bedard
88ebea94e9 Make sure callback_end tracepoint is triggered in AnyServiceCallback (#2670)
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-11-11 13:39:38 -08:00
YR
1e6767ac13 Fix documentation typo in server_goal_handle.hpp (#2669)
Signed-off-by: Yurii Rovinskyi <yurii.rovinskyi.19@pnu.edu.ua>
2024-11-10 07:54:41 -08:00
Barry Xu
37e3688026 Correct the incorrect comments in generic_client.hpp (#2662)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2024-11-05 08:07:02 -05:00
Romain DESILLE
9b654942f9 Fix NodeOptions assignment operator (#2656)
* Fix NodeOptions assignment operator

Also copy the enable_logger_service_ member during the assignment operation

* Add more checks for NodeOptions copy test

* Set non default values by avoiding the copy-assignement

Signed-off-by: Romain DESILLE <r.desille@gmail.com>
Co-authored-by: Christophe Bedard <bedard.christophe@gmail.com>
2024-10-30 07:45:19 -04:00
Tomoya Fujita
f12e3c69dc set QoS History KEEP_ALL explicitly for statistics publisher. (#2650)
* set QoS History KEEP_ALL explicitly for statistics publisher.

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

* test_subscription_options adjustment.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-10-19 14:21:20 -07:00
Chris Lalancette
bcc14755f9 Fix test_intra_process_manager.cpp with rmw_zenoh_cpp (#2653)
* Cleanup of test_intra_process_manager.cpp

In particular, make every pub and sub have to
pass in both a topic name and a QoS when they
are constructing mock pubs and subs for the
intra-process manager test.  This just makes it
easier to tell whether the pubs and subs should be
matched or not.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* Make sure to check QoS compatibility in the intra_process_manager tests.

It turns out that the intra_process_manager will attempt to match
QoS between publishers and subscriptions as they are added to the IPM.
This is exactly correct, but the tests were not following the same
pattern.

Thus, when running these tests under Zenoh, the tests would fail
because more things would match than the tests were expecting.
Update the test to take the differences in QoS into account,
which fixes the test under rmw_zenoh_cpp (and keeps it working
for the existing DDS RMWs).

Signed-off-by: Chris Lalancette <clalancette@gmail.com>

* Added feedback

Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>

---------

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
Co-authored-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2024-10-19 00:55:25 +02:00
Alejandro Hernández Cordero
4567b6ec80 Fixed test_events_executors in zenoh (#2643)
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2024-10-17 18:20:47 +02:00
Tomoya Fujita
0be8aa013e rmw_fastrtps supports service event gid uniqueness test. (#2638)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-10-14 15:24:42 -04:00
Tomoya Fujita
41d3a27437 print warning if event callback is not supported instead of passing exception. (#2648)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-10-09 15:22:46 +02:00
Christophe Bedard
c50f0c9c3d Fix error message in rclcpp_lifecycle::State::reset() (#2647)
The function that may not complete successfully is
`rcl_lifecycle_state_fini()`, not `rcl_lifecycle_transition_fini()`.

Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-10-09 12:26:26 +02:00
Barry Xu
f8aea8cc51 Implement callback support of async_send_request for service generic client (#2614)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2024-10-04 12:12:00 +02:00
149 changed files with 6281 additions and 1249 deletions

View File

@@ -1,45 +0,0 @@
<!--
For general questions, please ask on ROS answers: https://answers.ros.org, make sure to include at least the `ros2` tag and the rosdistro version you are running, e.g. `ardent`.
For general design discussions, please post on discourse: https://discourse.ros.org/c/ng-ros
Not sure if this is the right repository? Open an issue on https://github.com/ros2/ros2/issues
For Bug report or feature requests, please fill out the relevant category below
-->
## Bug report
**Required Info:**
- Operating System:
- <!-- OS and version (e.g. Windows 10, Ubuntu 16.04...) -->
- Installation type:
- <!-- binaries or from source -->
- Version or commit hash:
- <!-- Output of git rev-parse HEAD, release version, or repos file -->
- DDS implementation:
- <!-- rmw_implementation used (e.g. Fast-RTPS, RTI Connext, etc -->
- Client library (if applicable):
- <!-- e.g. rclcpp, rclpy, or N/A -->
#### Steps to reproduce issue
<!-- Detailed instructions on how to reliably reproduce this issue http://sscce.org/
``` code that can be copy-pasted is preferred ``` -->
```
```
#### Expected behavior
#### Actual behavior
#### Additional information
<!-- If you are reporting a bug delete everything below
If you are requesting a feature deleted everything above this line -->
----
## Feature request
#### Feature description
<!-- Description in a few sentences what the feature consists of and what problem it will solve -->
#### Implementation considerations
<!-- Relevant information on how the feature could be implemented and pros and cons of the different solutions -->

View File

@@ -1,13 +0,0 @@
name: Mirror rolling to master
on:
push:
branches: [ rolling ]
jobs:
mirror-to-master:
runs-on: ubuntu-latest
steps:
- uses: zofrex/mirror-branch@v1
with:
target-branch: master

View File

@@ -1,2 +0,0 @@
# This file was generated by https://github.com/audrow/update-ros2-repos
* @ivanpauno @hidmic @wjwwood

View File

@@ -2,6 +2,79 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29.6.0 (2025-04-25)
-------------------
* throws std::invalid_argument if ParameterEvent is NULL. (`#2814 <https://github.com/ros2/rclcpp/issues/2814>`_)
* Removed clang warnings (`#2823 <https://github.com/ros2/rclcpp/issues/2823>`_)
* Contributors: Alejandro Hernández Cordero, Tomoya Fujita
29.5.0 (2025-04-18)
-------------------
* Fix a race condition (`#2819 <https://github.com/ros2/rclcpp/issues/2819>`_)
* Remove redundant typesupport check in serialization module (`#2808 <https://github.com/ros2/rclcpp/issues/2808>`_)
* Remove get_typesupport_handle implementation. (`#2806 <https://github.com/ros2/rclcpp/issues/2806>`_)
* Use NodeParameterInterface instead of /parameter_event to update "use_sim_time" (`#2378 <https://github.com/ros2/rclcpp/issues/2378>`_)
* Remove cancel_clock_executor_promise\_. (`#2797 <https://github.com/ros2/rclcpp/issues/2797>`_)
* Enable parameter update recursively only when QoS override parameters. (`#2742 <https://github.com/ros2/rclcpp/issues/2742>`_)
* Contributors: Pedro de Azeredo, Tanishq Chaudhary, Tomoya Fujita
29.4.0 (2025-04-04)
-------------------
* Removed trailing whitespace from the codebase. (`#2791 <https://github.com/ros2/rclcpp/issues/2791>`_)
* Expanded docstring of `get_rmw_qos_profile()` (`#2787 <https://github.com/ros2/rclcpp/issues/2787>`_)
* Set envars to run tests with rmw_zenoh_cpp with multicast discovery (`#2776 <https://github.com/ros2/rclcpp/issues/2776>`_)
* fix: Compilefix for clang (`#2775 <https://github.com/ros2/rclcpp/issues/2775>`_)
* add exception doc for configure_introspection. (`#2773 <https://github.com/ros2/rclcpp/issues/2773>`_)
* feat: Add ClockWaiter and ClockConditionalVariable (`#2691 <https://github.com/ros2/rclcpp/issues/2691>`_)
* doc: Added warning to not instantiate Clock directly with RCL_ROS_TIME (`#2768 <https://github.com/ros2/rclcpp/issues/2768>`_)
* Use rmw_event_type_is_supported in test_qos_event (`#2761 <https://github.com/ros2/rclcpp/issues/2761>`_)
* Support action typesupport helper (`#2750 <https://github.com/ros2/rclcpp/issues/2750>`_)
* use maybe_unused attribute for the portability. (`#2758 <https://github.com/ros2/rclcpp/issues/2758>`_)
* Executor strong reference fix (`#2745 <https://github.com/ros2/rclcpp/issues/2745>`_)
* Cleanup of https://github.com/ros2/rclcpp/pull/2683 (`#2714 <https://github.com/ros2/rclcpp/issues/2714>`_)
* Fix typo in doc section for get_service_typesupport_handle (`#2751 <https://github.com/ros2/rclcpp/issues/2751>`_)
* Test case and fix for for https://github.com/ros2/rclcpp/issues/2652 (`#2713 <https://github.com/ros2/rclcpp/issues/2713>`_)
* fix(timer): Delete node, after executor thread terminated (`#2737 <https://github.com/ros2/rclcpp/issues/2737>`_)
* update doc section for spin_xxx methods. (`#2730 <https://github.com/ros2/rclcpp/issues/2730>`_)
* fix: Expose timers used by rclcpp::Waitables (`#2699 <https://github.com/ros2/rclcpp/issues/2699>`_)
* use rmw_qos_profile_rosout_default instead of rcl. (`#2663 <https://github.com/ros2/rclcpp/issues/2663>`_)
* fix(Executor): Fixed entities not beeing executed after just beeing added (`#2724 <https://github.com/ros2/rclcpp/issues/2724>`_)
* fix: make the loop condition align with the description (`#2726 <https://github.com/ros2/rclcpp/issues/2726>`_)
* Collect log messages from rcl, and reset. (`#2720 <https://github.com/ros2/rclcpp/issues/2720>`_)
* Contributors: Abhishek Kashyap, Alejandro Hernández Cordero, Barry Xu, Janosch Machowinski, Leander Stephen D'Souza, Tomoya Fujita, Yuyuan Yuan
29.3.0 (2024-12-20)
-------------------
* Fix transient local IPC publish (`#2708 <https://github.com/ros2/rclcpp/issues/2708>`_)
* apply actual QoS from rmw to the IPC publisher. (`#2707 <https://github.com/ros2/rclcpp/issues/2707>`_)
* Adding in topic name to logging on IPC issues (`#2706 <https://github.com/ros2/rclcpp/issues/2706>`_)
* fix TestTimeSource.ROS_time_valid_attach_detach. (`#2700 <https://github.com/ros2/rclcpp/issues/2700>`_)
* Update docstring for `rclcpp::Node::now()` (`#2696 <https://github.com/ros2/rclcpp/issues/2696>`_)
* Re-enable executor test on rmw_connextdds. (`#2693 <https://github.com/ros2/rclcpp/issues/2693>`_)
* Fix warnings on Windows. (`#2692 <https://github.com/ros2/rclcpp/issues/2692>`_)
* Omnibus fixes for running tests with Connext. (`#2684 <https://github.com/ros2/rclcpp/issues/2684>`_)
* fix(Executor): Fix segfault if callback group is deleted during rmw_wait (`#2683 <https://github.com/ros2/rclcpp/issues/2683>`_)
* Contributors: Chris Lalancette, Jeffery Hsu, Patrick Roncagliolo, Steve Macenski, Tomoya Fujita, jmachowinski
29.2.0 (2024-11-25)
-------------------
* accept custom allocator for LoanedMessage. (`#2672 <https://github.com/ros2/rclcpp/issues/2672>`_)
* Contributors: Tomoya Fujita
29.1.0 (2024-11-20)
-------------------
* a couple of typo fixes in doc section for LoanedMessage. (`#2676 <https://github.com/ros2/rclcpp/issues/2676>`_)
* Make sure callback_end tracepoint is triggered in AnyServiceCallback (`#2670 <https://github.com/ros2/rclcpp/issues/2670>`_)
* Correct the incorrect comments in generic_client.hpp (`#2662 <https://github.com/ros2/rclcpp/issues/2662>`_)
* Fix NodeOptions assignment operator (`#2656 <https://github.com/ros2/rclcpp/issues/2656>`_)
* set QoS History KEEP_ALL explicitly for statistics publisher. (`#2650 <https://github.com/ros2/rclcpp/issues/2650>`_)
* Fix test_intra_process_manager.cpp with rmw_zenoh_cpp (`#2653 <https://github.com/ros2/rclcpp/issues/2653>`_)
* Fixed test_events_executors in zenoh (`#2643 <https://github.com/ros2/rclcpp/issues/2643>`_)
* rmw_fastrtps supports service event gid uniqueness test. (`#2638 <https://github.com/ros2/rclcpp/issues/2638>`_)
* print warning if event callback is not supported instead of passing exception. (`#2648 <https://github.com/ros2/rclcpp/issues/2648>`_)
* Implement callback support of async_send_request for service generic client (`#2614 <https://github.com/ros2/rclcpp/issues/2614>`_)
* Contributors: Alejandro Hernández Cordero, Barry Xu, Chris Lalancette, Christophe Bedard, Romain DESILLE, Tomoya Fujita
29.0.0 (2024-10-03)
-------------------
* Fixed test qos rmw zenoh (`#2639 <https://github.com/ros2/rclcpp/issues/2639>`_)

View File

@@ -434,4 +434,3 @@
- (tfoote) There should be no jumps in steady time. If there's a big change in system time, it doesn't necessarily mean that time jumped, just that you might have been sleeping for a long time. Most ntp systems adjust the slew rate these days instead of jumping but still that's an external process and I don't know of any APIs to introspect the state of the clock. I'm not sure that we have a way to detect jumps in time for system or steady time. To that end I think that we should be clear that we only provide callbacks when simulation time starts or stops, or simulation time jumps. We should also strongly recommend that operators not actively adjust their system clocks while running ROS nodes.
- (jacobperron) I agree with Tully, if we don't have a way to detect system time jumps then I think we should just document that this only works with ROS time. In addition to documentation, we could log an info or warning message if the user registers jump callback with steady or system time, but it may be unnecessarily noisy.

View File

@@ -2,15 +2,15 @@
## Introduction:
The original requirement came in **gazebo_ros_pkgs** for setting individual wheel slip parameters based on global wheel slip value [link to original issue](https://github.com/ros-simulation/gazebo_ros_pkgs/pull/1365).
The original requirement came in **gazebo_ros_pkgs** for setting individual wheel slip parameters based on global wheel slip value [link to original issue](https://github.com/ros-simulation/gazebo_ros_pkgs/pull/1365).
The main requirement is to set one or more parameters after another parameter is set successfully.
The main requirement is to set one or more parameters after another parameter is set successfully.
Additionally, it would be nice if users could be notified locally (via a callback) when parameters have been set successfully (i.e. post validation).
Related discussion can be found in [#609](https://github.com/ros2/rclcpp/issues/609) [#1789](https://github.com/ros2/rclcpp/pull/1789)
With the current parameters API, the `add_on_set_parameters_callback` is intended for validation of parameter values before they are set, it should **not** cause any side-effects.
With the current parameters API, the `add_on_set_parameters_callback` is intended for validation of parameter values before they are set, it should **not** cause any side-effects.
There is also the `ParameterEventHandler` that publishes changes to node parameters on `/parameter_events` topic for external nodes to see. Though the node could subscribe to the `/parameter_events` topic to be notified of changes to its own parameters, it is less than ideal since there is a delay caused by waiting for an executor to process the callback.
@@ -19,11 +19,11 @@ We propose adding a `PostSetParametersCallbackHandle` for successful parameter s
The validation callback is often abused to trigger side effects in the code, for instance updating class attributes even before a parameter has been set successfully. Instead of relying on the `/parameter_events` topic to be notified of parameter changes, users can register a callback with a new API, `add_post_set_parameters_callback`.
It is possible to use the proposed `add_post_set_parameters_callback` for setting additional parameters, but this might result in infinite recursion and does not allow those additional parameters to be set atomically with the original parameter(s) changed.
To workaround these issues, we propose adding a "pre set" callback type that can be registered with `add_pre_set_parameters_callback`, which will be triggered before the validation callbacks and can be used to modify the parameter list.
To workaround these issues, we propose adding a "pre set" callback type that can be registered with `add_pre_set_parameters_callback`, which will be triggered before the validation callbacks and can be used to modify the parameter list.
![Desgin API](https://github.com/ros2/rclcpp/blob/deepanshu/local-param-changed-callback-support/rclcpp/doc/param_callback_design.png?raw=true)
## Alternatives
* Users could call `set_parameter` while processing a message from the `/parameter_events` topic, however, there is extra overhead in having to create subscription (as noted earlier).
* Users could call `set_parameter` inside the "on set" parameters callback, however it is not well-defined how side-effects should handle cases where parameter validation fails.
* Users could call `set_parameter` inside the "on set" parameters callback, however it is not well-defined how side-effects should handle cases where parameter validation fails.

View File

@@ -69,18 +69,17 @@ private:
};
template<typename Alloc, typename T, typename D>
void set_allocator_for_deleter(D * deleter, Alloc * alloc)
void set_allocator_for_deleter([[maybe_unused]] D * deleter, [[maybe_unused]] Alloc * alloc)
{
(void) alloc;
(void) deleter;
throw std::runtime_error("Reached unexpected template specialization");
}
template<typename T, typename U>
void set_allocator_for_deleter(std::default_delete<T> * deleter, std::allocator<U> * alloc)
void set_allocator_for_deleter(
[[maybe_unused]] std::default_delete<T> * deleter,
[[maybe_unused]] std::allocator<U> * alloc)
{
(void) deleter;
(void) alloc;
// This function is intentionally left empty.
}
template<typename Alloc, typename T>

View File

@@ -165,11 +165,13 @@ public:
if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
cb(request_header, std::move(request));
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
return nullptr;
}
if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
cb(service_handle, request_header, std::move(request));
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
return nullptr;
}
// auto response = allocate_shared<typename ServiceT::Response, Allocator>();

View File

@@ -789,6 +789,9 @@ public:
* \param[in] clock clock to use to generate introspection timestamps
* \param[in] qos_service_event_pub QoS settings to use when creating the introspection publisher
* \param[in] introspection_state the state to set introspection to
*
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw if
* it failed to configure introspection.
*/
void
configure_introspection(

View File

@@ -60,6 +60,13 @@ public:
/**
* Initializes the clock instance with the given clock_type.
*
* WARNING Don't instantiate a clock using RCL_ROS_TIME directly,
* unless you really know what you are doing. By default no TimeSource
* is attached to a new clock. This will lead to the unexpected behavior,
* that your RCL_ROS_TIME will run always on system time. If you want
* a RCL_ROS_TIME use Node::get_clock(), or make sure to attach a
* TimeSource yourself.
*
* \param clock_type type of the clock.
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
*/
@@ -193,16 +200,6 @@ public:
bool
ros_time_is_active();
/**
* Cancels an ongoing or future sleep operation of one thread.
*
* This function can be used by one thread, to wakeup another thread that is
* blocked using any of the sleep_ or wait_ methods of this class.
*/
RCLCPP_PUBLIC
void
cancel_sleep_or_wait();
/// Return the rcl_clock_t clock handle
RCLCPP_PUBLIC
rcl_clock_t *
@@ -260,6 +257,117 @@ private:
std::shared_ptr<Impl> impl_;
};
/**
* A synchronization primitive, equal to std::conditional_variable,
* that works with the rclcpp::Clock.
*
* For more information on the API see https://en.cppreference.com/w/cpp/thread/condition_variable.
*
* Note, this class does not handle shutdowns, if you want to
* haven them handles as well, use ClockConditionalVariable.
*/
class ClockWaiter
{
private:
class ClockWaiterImpl;
std::unique_ptr<ClockWaiterImpl> impl_;
public:
RCLCPP_SMART_PTR_DEFINITIONS(ClockWaiter)
RCLCPP_PUBLIC
explicit ClockWaiter(const rclcpp::Clock::SharedPtr & clock);
RCLCPP_PUBLIC
~ClockWaiter();
/**
* Calling this function will block the current thread, until abs_time is reached,
* or pred returns true.
* @param lock A locked lock. The lock must be locked at call time, or this method will throw.
* The lock will be atomically released and this thread will blocked.
* @param abs_time The time until which this thread shall be blocked.
* @param pred may be called in cased of spurious wakeups, but must be called every time
* notify_one() was called. During the call to pred, the given lock will be locked.
* This method will return, if pred returns true.
*/
RCLCPP_PUBLIC
bool
wait_until(
std::unique_lock<std::mutex> & lock,
const rclcpp::Time & abs_time, const std::function<bool ()> & pred);
/**
* Notify the blocked thread, that it should reevaluate the wakeup condition.
* The given pred function in wait_until will be reevaluated and wait_until
* will return if it evaluates to true.
*/
RCLCPP_PUBLIC
void
notify_one();
};
/**
* A synchronization primitive, similar to std::conditional_variable,
* that works with the rclcpp::Clock.
*
* For more information on the API see https://en.cppreference.com/w/cpp/thread/condition_variable.
*
* This primitive will wake up if the context was shut down.
*/
class ClockConditionalVariable
{
class Impl;
std::unique_ptr<Impl> impl_;
public:
RCLCPP_SMART_PTR_DEFINITIONS(ClockConditionalVariable)
RCLCPP_PUBLIC
ClockConditionalVariable(
const rclcpp::Clock::SharedPtr & clock,
rclcpp::Context::SharedPtr context = rclcpp::contexts::get_global_default_context());
RCLCPP_PUBLIC
~ClockConditionalVariable();
/**
* Calling this function will block the current thread, until abs_time is reached,
* or pred returns true.
* @param lock A locked lock. The lock must be locked at call time, or this method will throw.
* The lock will be atomically released and this thread will blocked.
* The given lock must be created using the mutex returned my mutex().
* @param abs_time The time until which this thread shall be blocked.
* @param pred may be called in cased of spurious wakeups, but must be called every time
* notify_one() was called. During the call to pred, the given lock will be locked.
* This method will return, if pred returns true.
*
* @return true if until was reached.
*/
RCLCPP_PUBLIC
bool
wait_until(
std::unique_lock<std::mutex> & lock, rclcpp::Time until,
const std::function<bool ()> & pred);
/**
* Notify the blocked thread, that is should reevaluate the wakeup condition.
* E.g. the given pred function in wait_until shall be reevaluated.
*/
RCLCPP_PUBLIC
void
notify_one();
/**
* Returns the internal mutex. In order to be race free with the context shutdown,
* this mutex must be used for the wait_until call.
*/
RCLCPP_PUBLIC
std::mutex &
mutex();
};
} // namespace rclcpp
#endif // RCLCPP__CLOCK_HPP_

View File

@@ -97,6 +97,9 @@ declare_parameter_or_get(
rcl_interfaces::msg::ParameterDescriptor descriptor)
{
try {
// enable parameter modification to make it possible
// to declare QoS override parameters during parameter callbacks.
parameters_interface.enable_parameter_modification();
return parameters_interface.declare_parameter(
param_name, param_value, descriptor);
} catch (const rclcpp::exceptions::ParameterAlreadyDeclaredException &) {

View File

@@ -20,6 +20,7 @@
#include <mutex>
#include <stdexcept>
#include <string>
#include <vector>
#include "rcl/error_handling.h"
#include "rcl/event_callback.h"
@@ -221,6 +222,13 @@ public:
}
}
RCLCPP_PUBLIC
std::vector<std::shared_ptr<rclcpp::TimerBase>>
get_timers() const override
{
return {};
}
protected:
RCLCPP_PUBLIC
void
@@ -278,15 +286,15 @@ public:
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't take event info: %s", rcl_get_error_string().str);
rcl_reset_error();
return nullptr;
}
return std::static_pointer_cast<void>(std::make_shared<EventCallbackInfoT>(callback_info));
}
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override
take_data_by_entity_id([[maybe_unused]] size_t id) override
{
(void)id;
return take_data();
}

View File

@@ -287,6 +287,18 @@ public:
* Adding subscriptions, timers, services, etc. with blocking or long running
* callbacks may cause the function exceed the max_duration significantly.
*
* Work that is ready to be done is collected only once, and when collecting that work
* entities which may have multiple pieces of work ready will only be executed at most
* one time.
* The reason for this is that it is not possible to tell if, for example, a ready
* subscription has only one message ready or multiple without checking again.
* Because, in order to find out if there are multiple messages, one message must
* be taken and executed before checking again if that subscription is still ready.
* However, this function only checks for ready entities to work on once,
* and so it will never execute a single entity more than once per call to this function.
* See spin_all() variants for a function that will repeatedly work on a single entity
* in a single call.
*
* If there is no work to be done when this called, it will return immediately
* because the collecting of available work is non-blocking.
* Before each piece of ready work is executed this function checks if the

View File

@@ -19,6 +19,7 @@
#include <memory>
#include <mutex>
#include <set>
#include <vector>
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/waitable.hpp"
@@ -41,7 +42,9 @@ public:
* of this waitable has signaled the wait_set.
*/
RCLCPP_PUBLIC
explicit ExecutorNotifyWaitable(std::function<void(void)> on_execute_callback = {});
explicit ExecutorNotifyWaitable(
std::function<void(void)> on_execute_callback = {}, const rclcpp::Context::SharedPtr & context =
rclcpp::contexts::get_global_default_context());
// Destructor
RCLCPP_PUBLIC
@@ -146,6 +149,14 @@ public:
size_t
get_number_of_ready_guard_conditions() override;
/// Returns the number of used Timers
/**
* Will always return an empty vector.
*/
RCLCPP_PUBLIC
std::vector<std::shared_ptr<rclcpp::TimerBase>>
get_timers() const override;
private:
/// Callback to run when waitable executes
std::function<void(void)> execute_callback_;
@@ -158,8 +169,17 @@ private:
std::function<void(size_t)> on_ready_callback_;
/// The collection of guard conditions to be waited on.
std::set<rclcpp::GuardCondition::WeakPtr,
std::owner_less<rclcpp::GuardCondition::WeakPtr>> notify_guard_conditions_;
std::set<rclcpp::GuardCondition::SharedPtr> notify_guard_conditions_;
/// The indixes were our guard conditions were stored in the
/// rcl waitset
std::vector<size_t> idxs_of_added_guard_condition_;
/// set to true, if we got a pending trigger
bool needs_processing = false;
/// A guard condition needed to generate wakeups
rclcpp::GuardCondition::SharedPtr guard_condition_;
};
} // namespace executors

View File

@@ -17,6 +17,7 @@
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <stdexcept>
#include <utility>
#include <vector>
@@ -69,7 +70,7 @@ public:
*/
void enqueue(BufferT request) override
{
std::lock_guard<std::mutex> lock(mutex_);
std::unique_lock lock(mutex_);
write_index_ = next_(write_index_);
ring_buffer_[write_index_] = std::move(request);
@@ -95,7 +96,7 @@ public:
*/
BufferT dequeue() override
{
std::lock_guard<std::mutex> lock(mutex_);
std::unique_lock lock(mutex_);
if (!has_data_()) {
return BufferT();
@@ -134,7 +135,7 @@ public:
*/
inline size_t next(size_t val)
{
std::lock_guard<std::mutex> lock(mutex_);
std::unique_lock lock(mutex_);
return next_(val);
}
@@ -146,7 +147,7 @@ public:
*/
inline bool has_data() const override
{
std::lock_guard<std::mutex> lock(mutex_);
std::shared_lock lock(mutex_);
return has_data_();
}
@@ -159,7 +160,7 @@ public:
*/
inline bool is_full() const
{
std::lock_guard<std::mutex> lock(mutex_);
std::shared_lock lock(mutex_);
return is_full_();
}
@@ -171,13 +172,15 @@ public:
*/
size_t available_capacity() const override
{
std::lock_guard<std::mutex> lock(mutex_);
std::shared_lock lock(mutex_);
return available_capacity_();
}
void clear() override
{
TRACETOOLS_TRACEPOINT(rclcpp_ring_buffer_clear, static_cast<const void *>(this));
std::unique_lock lock(mutex_);
clear_();
}
private:
@@ -227,6 +230,14 @@ private:
return capacity_ - size_;
}
inline void clear_()
{
ring_buffer_.clear();
size_ = 0;
read_index_ = 0;
write_index_ = capacity_ - 1;
}
/// Traits for checking if a type is std::unique_ptr
template<typename ...>
struct is_std_unique_ptr final : std::false_type {};
@@ -251,7 +262,7 @@ private:
void> * = nullptr>
std::vector<BufferT> get_all_data_impl()
{
std::lock_guard<std::mutex> lock(mutex_);
std::unique_lock lock(mutex_);
std::vector<BufferT> result_vtr;
result_vtr.reserve(size_);
for (size_t id = 0; id < size_; ++id) {
@@ -266,7 +277,7 @@ private:
std::is_copy_constructible<T>::value, void> * = nullptr>
std::vector<BufferT> get_all_data_impl()
{
std::lock_guard<std::mutex> lock(mutex_);
std::unique_lock lock(mutex_);
std::vector<BufferT> result_vtr;
result_vtr.reserve(size_);
for (size_t id = 0; id < size_; ++id) {
@@ -292,7 +303,7 @@ private:
return {};
}
size_t capacity_;
const size_t capacity_;
std::vector<BufferT> ring_buffer_;
@@ -300,7 +311,7 @@ private:
size_t read_index_;
size_t size_;
mutable std::mutex mutex_;
mutable std::shared_mutex mutex_;
};
} // namespace buffers

View File

@@ -19,6 +19,7 @@
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "rcl/wait.h"
#include "rmw/impl/cpp/demangle.hpp"
@@ -78,9 +79,8 @@ public:
take_data() override = 0;
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override
take_data_by_entity_id([[maybe_unused]] size_t id) override
{
(void)id;
return take_data();
}
@@ -180,6 +180,13 @@ public:
on_new_message_callback_ = nullptr;
}
RCLCPP_PUBLIC
std::vector<std::shared_ptr<rclcpp::TimerBase>>
get_timers() const override
{
return {};
}
protected:
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_message_callback_ {nullptr};

View File

@@ -110,9 +110,8 @@ public:
}
bool
is_ready(const rcl_wait_set_t & wait_set) override
is_ready([[maybe_unused]] const rcl_wait_set_t & wait_set) override
{
(void) wait_set;
return buffer_->has_data();
}

View File

@@ -19,6 +19,7 @@
#include <memory>
#include <future>
#include <string>
#include <tuple>
#include <vector>
#include <utility>
@@ -35,8 +36,8 @@ namespace rclcpp
class GenericClient : public ClientBase
{
public:
using Request = void *; // Serialized data pointer of request message
using Response = void *; // Serialized data pointer of response message
using Request = void *; // Deserialized data pointer of request message
using Response = void *; // Deserialized data pointer of response message
using SharedResponse = std::shared_ptr<void>;
@@ -46,6 +47,8 @@ public:
using Future = std::future<SharedResponse>;
using SharedFuture = std::shared_future<SharedResponse>;
using CallbackType = std::function<void (SharedFuture)>;
RCLCPP_SMART_PTR_DEFINITIONS(GenericClient)
/// A convenient GenericClient::Future and request id pair.
@@ -76,6 +79,20 @@ public:
~FutureAndRequestId() = default;
};
/// A convenient GenericClient::SharedFuture and request id pair.
/**
* Public members:
* - future: a std::shared_future<SharedResponse>.
* - request_id: the request id associated with the future.
*
* All the other methods are equivalent to the ones std::shared_future provides.
*/
struct SharedFutureAndRequestId
: detail::FutureAndRequestId<std::shared_future<SharedResponse>>
{
using detail::FutureAndRequestId<std::shared_future<SharedResponse>>::FutureAndRequestId;
};
GenericClient(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
@@ -106,16 +123,16 @@ public:
* If the future never completes,
* e.g. the call to Executor::spin_until_future_complete() times out,
* GenericClient::remove_pending_request() must be called to clean the client internal state.
* Not doing so will make the `Client` instance to use more memory each time a response is not
* received from the service server.
* Not doing so will make the `GenericClient` instance to use more memory each time a response is
* not received from the service server.
*
* ```cpp
* auto future = client->async_send_request(my_request);
* auto future = generic_client->async_send_request(my_request);
* if (
* rclcpp::FutureReturnCode::TIMEOUT ==
* executor->spin_until_future_complete(future, timeout))
* {
* client->remove_pending_request(future);
* generic_client->remove_pending_request(future);
* // handle timeout
* } else {
* handle_response(future.get());
@@ -129,6 +146,45 @@ public:
FutureAndRequestId
async_send_request(const Request request);
/// Send a request to the service server and schedule a callback in the executor.
/**
* Similar to the previous overload, but a callback will automatically be called when a response
* is received.
*
* If the callback is never called, because we never got a reply for the service server,
* remove_pending_request() has to be called with the returned request id or
* prune_pending_requests().
* Not doing so will make the `GenericClient` instance use more memory each time a response is not
* received from the service server.
* In this case, it's convenient to setup a timer to cleanup the pending requests.
*
* \param[in] request request to be send.
* \param[in] cb callback that will be called when we get a response for this request.
* \return the request id representing the request just sent.
*/
template<
typename CallbackT,
typename std::enable_if<
rclcpp::function_traits::same_arguments<
CallbackT,
CallbackType
>::value
>::type * = nullptr
>
SharedFutureAndRequestId
async_send_request(const Request request, CallbackT && cb)
{
Promise promise;
auto shared_future = promise.get_future().share();
auto req_id = async_send_request_impl(
request,
std::make_tuple(
CallbackType{std::forward<CallbackT>(cb)},
shared_future,
std::move(promise)));
return SharedFutureAndRequestId{std::move(shared_future), req_id};
}
/// Clean all pending requests older than a time_point.
/**
* \param[in] time_point Requests that were sent before this point are going to be removed.
@@ -149,15 +205,52 @@ public:
pruned_requests);
}
/// Clean all pending requests.
/**
* \return number of pending requests that were removed.
*/
RCLCPP_PUBLIC
size_t
prune_pending_requests();
/// Cleanup a pending request.
/**
* This notifies the client that we have waited long enough for a response from the server
* to come, we have given up and we are not waiting for a response anymore.
*
* Not calling this will make the client start using more memory for each request
* that never got a reply from the server.
*
* \param[in] request_id request id returned by async_send_request().
* \return true when a pending request was removed, false if not (e.g. a response was received).
*/
RCLCPP_PUBLIC
bool
remove_pending_request(
int64_t request_id);
/// Cleanup a pending request.
/**
* Convenient overload, same as:
*
* `GenericClient::remove_pending_request(this, future.request_id)`.
*/
RCLCPP_PUBLIC
bool
remove_pending_request(
const FutureAndRequestId & future);
/// Cleanup a pending request.
/**
* Convenient overload, same as:
*
* `GenericClient::remove_pending_request(this, future.request_id)`.
*/
RCLCPP_PUBLIC
bool
remove_pending_request(
const SharedFutureAndRequestId & future);
/// Take the next response for this client.
/**
* \sa ClientBase::take_type_erased_response().
@@ -179,9 +272,12 @@ public:
}
protected:
using CallbackTypeValueVariant = std::tuple<CallbackType, SharedFuture, Promise>;
using CallbackInfoVariant = std::variant<
std::promise<SharedResponse>>; // Use variant for extension
std::promise<SharedResponse>,
CallbackTypeValueVariant>; // Use variant for extension
RCLCPP_PUBLIC
int64_t
async_send_request_impl(
const Request request,

View File

@@ -31,21 +31,20 @@ namespace rclcpp
template<typename MessageT, typename AllocatorT = std::allocator<void>>
class LoanedMessage
{
public:
using MessageAllocatorTraits = rclcpp::allocator::AllocRebind<MessageT, AllocatorT>;
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
public:
/// Constructor of the LoanedMessage class.
/**
* The constructor of this class allocates memory for a given message type
* and associates this with a given publisher.
*
* Given the publisher instance, a case differentiation is being performaned
* which decides whether the underlying middleware is able to allocate the appropriate
* memory for this message type or not.
* In the case that the middleware can not loan messages, the passed in allocator instance
* is being used to allocate the message within the scope of this class.
* Otherwise, the allocator is being ignored and the allocation is solely performaned
* The underlying middleware is queried to determine whether it is able to allocate the
* appropriate memory for this message type or not.
* In the case that the middleware cannot loan messages, the passed in allocator instance
* is used to allocate the message within the scope of this class.
* Otherwise, the allocator is ignored and the allocation is solely performed
* in the underlying middleware with its appropriate allocation strategy.
* The need for this arises as the user code can be written explicitly targeting a middleware
* capable of loaning messages.
@@ -53,12 +52,12 @@ public:
* a middleware which doesn't support message loaning in which case the allocator will be used.
*
* \param[in] pub rclcpp::Publisher instance to which the memory belongs
* \param[in] allocator Allocator instance in case middleware can not allocate messages
* \param[in] allocator Allocator instance in case middleware cannot allocate messages
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
*/
LoanedMessage(
const rclcpp::PublisherBase & pub,
std::allocator<MessageT> allocator)
MessageAllocator allocator)
: pub_(pub),
message_(nullptr),
message_allocator_(std::move(allocator))

View File

@@ -78,34 +78,6 @@ RCLCPP_PUBLIC
Logger
get_node_logger(const rcl_node_t * node);
// TODO(ahcorde): Remove deprecated class on the next release (in Rolling after Kilted).
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
/// Get the current logging directory.
/**
* For more details of how the logging directory is determined,
* see rcl_logging_get_logging_directory().
*
* \returns the logging directory being used.
* \throws rclcpp::exceptions::RCLError if an unexpected error occurs.
*/
[[deprecated("use rclcpp::get_log_directory instead of rclcpp::get_logging_directory")]]
RCLCPP_PUBLIC
rcpputils::fs::path
get_logging_directory();
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
/// Get the current logging directory.
/**
* For more details of how the logging directory is determined,

View File

@@ -1430,7 +1430,7 @@ public:
rclcpp::Clock::ConstSharedPtr
get_clock() const;
/// Returns current time from the time source specified by clock_type.
/// Returns current time from the node clock.
/**
* \sa rclcpp::Clock::now
*/
@@ -1559,6 +1559,10 @@ public:
* which has been created using an existing instance of this class, but which
* has an additional sub-namespace (short for subordinate namespace)
* associated with it.
* A subordinate node and an instance of this class share all the node interfaces
* such as `rclcpp::node_interfaces::NodeParametersInterface`.
* Subordinate nodes are primarily used to organize namespaces and provide a
* hierarchical structure, but they are not meant to be completely independent nodes.
* The sub-namespace will extend the node's namespace for the purpose of
* creating additional entities, such as Publishers, Subscriptions, Service
* Clients and Servers, and so on.

View File

@@ -323,11 +323,9 @@ template<typename ParameterT>
bool
Node::get_parameter(const std::string & name, ParameterT & parameter) const
{
std::string sub_name = extend_name_with_sub_namespace(name, this->get_sub_namespace());
rclcpp::Parameter parameter_variant;
bool result = get_parameter(sub_name, parameter_variant);
bool result = get_parameter(name, parameter_variant);
if (result) {
parameter = static_cast<ParameterT>(parameter_variant.get_value<ParameterT>());
}
@@ -342,9 +340,7 @@ Node::get_parameter_or(
ParameterT & parameter,
const ParameterT & alternative_value) const
{
std::string sub_name = extend_name_with_sub_namespace(name, this->get_sub_namespace());
bool got_parameter = get_parameter(sub_name, parameter);
bool got_parameter = get_parameter(name, parameter);
if (!got_parameter) {
parameter = alternative_value;
}

View File

@@ -214,6 +214,10 @@ public:
const std::map<std::string, rclcpp::ParameterValue> &
get_parameter_overrides() const override;
RCLCPP_PUBLIC
void
enable_parameter_modification() override;
using PreSetCallbacksHandleContainer = std::list<PreSetParametersCallbackHandle::WeakPtr>;
using OnSetCallbacksHandleContainer = std::list<OnSetParametersCallbackHandle::WeakPtr>;
using PostSetCallbacksHandleContainer = std::list<PostSetParametersCallbackHandle::WeakPtr>;

View File

@@ -270,6 +270,26 @@ public:
virtual
const std::map<std::string, rclcpp::ParameterValue> &
get_parameter_overrides() const = 0;
/// Enable parameter modification recursively during parameter callbacks.
/**
* This function is used to enable parameter modification during parameter callbacks.
*
* There are times when it does not allow parameter modification, such as when the parameter
* callbacks are being called and tries to modify the parameters with set_parameter and
* declare_parameter to avoid recursive parameter modification.
* This is protected by rclcpp::node_interfaces::ParameterMutationRecursionGuard.
*
* This function is explicitly called to allow the recursive parameter operation during
* parameter callbacks by the application.
* Once it is enabled, the next parameter operation set/declare/undeclare_parameter are
* allowed to execute in the parameter callback. But, no more further recursive operation
* is allowed, unless user application calls this API again.
*/
RCLCPP_PUBLIC
virtual
void
enable_parameter_modification() = 0;
};
} // namespace node_interfaces

View File

@@ -45,6 +45,7 @@ public:
* \param[in] names A list of parameter names of interest.
* \param[in] types A list of the types of parameter events of iterest.
* EventType NEW, DELETED, or CHANGED
* \throws std::invalid_argument if event is NULL.
*
* Example Usage:
*

View File

@@ -143,34 +143,34 @@ public:
post_init_setup(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
[[maybe_unused]] const rclcpp::QoS & qos,
[[maybe_unused]] const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
{
// Topic is unused for now.
(void)topic;
(void)options;
// If needed, setup intra process communication.
if (rclcpp::detail::resolve_use_intra_process(options_, *node_base)) {
auto context = node_base->get_context();
// Get the intra process manager instance for this context.
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
// Register the publisher with the intra process manager.
if (qos.history() != rclcpp::HistoryPolicy::KeepLast) {
// Check if the QoS is compatible with intra-process.
auto qos_profile = get_actual_qos();
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
throw std::invalid_argument(
"intraprocess communication allowed only with keep last history qos policy");
"intraprocess communication on topic '" + topic +
"' allowed only with keep last history qos policy");
}
if (qos.depth() == 0) {
if (qos_profile.depth() == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value");
"intraprocess communication on topic '" + topic +
"' is not allowed with a zero qos history depth value");
}
if (qos.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
if (qos_profile.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
buffer_ = rclcpp::experimental::create_intra_process_buffer<
ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter>(
rclcpp::detail::resolve_intra_process_buffer_type(options_.intra_process_buffer_type),
qos,
qos_profile,
std::make_shared<ROSMessageTypeAllocator>(ros_message_type_allocator_));
}
// Register the publisher with the intra process manager.
uint64_t intra_process_publisher_id = ipm->add_publisher(this->shared_from_this(), buffer_);
this->setup_intra_process(
intra_process_publisher_id,
@@ -232,8 +232,12 @@ public:
// interprocess publish, resulting in lower publish-to-subscribe latency.
// It's not possible to do that with an unique_ptr,
// as do_intra_process_publish takes the ownership of the message.
// When durability is set to TransientLocal (i.e. there is a buffer),
// inter process publish should always take place to ensure
// late joiners receive past data.
bool inter_process_publish_needed =
get_subscription_count() > get_intra_process_subscription_count();
get_subscription_count() > get_intra_process_subscription_count() || buffer_;
if (inter_process_publish_needed) {
auto shared_msg =
@@ -310,8 +314,11 @@ public:
return;
}
// When durability is set to TransientLocal (i.e. there is a buffer),
// inter process publish should always take place to ensure
// late joiners receive past data.
bool inter_process_publish_needed =
get_subscription_count() > get_intra_process_subscription_count();
get_subscription_count() > get_intra_process_subscription_count() || buffer_;
if (inter_process_publish_needed) {
auto ros_msg_ptr = std::make_shared<ROSMessageType>();

View File

@@ -146,10 +146,18 @@ public:
QoS(size_t history_depth); // NOLINT(runtime/explicit): conversion constructor
/// Return the rmw qos profile.
/**
* The profile consists of various QoS policies such as history, reliability, and durability.
* Use the corresponding getter functions to retrieve individual policies.
*/
rmw_qos_profile_t &
get_rmw_qos_profile();
/// Return the rmw qos profile.
/**
* The profile consists of various QoS policies such as history, reliability, and durability.
* Use the corresponding getter functions to retrieve individual policies.
*/
const rmw_qos_profile_t &
get_rmw_qos_profile() const;
@@ -475,7 +483,7 @@ public:
explicit
RosoutQoS(
const QoSInitialization & rosout_qos_initialization = (
QoSInitialization::from_rmw(rcl_qos_profile_rosout_default)
QoSInitialization::from_rmw(rmw_qos_profile_rosout_default)
));
};

View File

@@ -504,6 +504,9 @@ public:
* \param[in] clock clock to use to generate introspection timestamps
* \param[in] qos_service_event_pub QoS settings to use when creating the introspection publisher
* \param[in] introspection_state the state to set introspection to
*
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw if
* it failed to configure introspection.
*/
void
configure_introspection(

View File

@@ -201,6 +201,7 @@ public:
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't add subscription to wait set: %s", rcl_get_error_string().str);
rcl_reset_error();
return false;
}
}
@@ -210,6 +211,7 @@ public:
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't add client to wait set: %s", rcl_get_error_string().str);
rcl_reset_error();
return false;
}
}
@@ -219,6 +221,7 @@ public:
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't add service to wait set: %s", rcl_get_error_string().str);
rcl_reset_error();
return false;
}
}
@@ -228,6 +231,7 @@ public:
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't add timer to wait set: %s", rcl_get_error_string().str);
rcl_reset_error();
return false;
}
}

View File

@@ -114,9 +114,9 @@ public:
* all references.
* \param[in] msg Shared pointer to the message to return.
*/
void return_message(std::shared_ptr<MessageT> & msg)
void return_message([[maybe_unused]] std::shared_ptr<MessageT> & msg)
{
(void)msg;
// This function is intentionally left empty.
}
protected:

View File

@@ -147,11 +147,13 @@ public:
auto qos_profile = get_actual_qos();
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
throw std::invalid_argument(
"intraprocess communication allowed only with keep last history qos policy");
"intraprocess communication on topic '" + topic_name +
"' allowed only with keep last history qos policy");
}
if (qos_profile.depth() == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with 0 depth qos policy");
"intraprocess communication on topic '" + topic_name +
"' is not allowed with 0 depth qos policy");
}
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
@@ -207,13 +209,11 @@ public:
/// Called after construction to continue setup that requires shared_from_this().
void
post_init_setup(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const rclcpp::QoS & qos,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
[[maybe_unused]] rclcpp::node_interfaces::NodeBaseInterface * node_base,
[[maybe_unused]] const rclcpp::QoS & qos,
[[maybe_unused]] const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
{
(void)node_base;
(void)qos;
(void)options;
// This function is intentionally left empty.
}
/// Take the next message from the inter-process subscription.
@@ -420,20 +420,17 @@ public:
void
return_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) override
[[maybe_unused]] rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) override
{
(void) message;
throw rclcpp::exceptions::UnimplementedError(
"return_dynamic_message is not implemented for Subscription");
}
void
handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info) override
[[maybe_unused]] const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
[[maybe_unused]] const rclcpp::MessageInfo & message_info) override
{
(void) message;
(void) message_info;
throw rclcpp::exceptions::UnimplementedError(
"handle_dynamic_message is not implemented for Subscription");
}

View File

@@ -20,8 +20,8 @@
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include <utility>
#include <vector>
#include "rcl/event_callback.h"
#include "rcl/subscription.h"

View File

@@ -80,7 +80,8 @@ struct SubscriptionOptionsBase
// An optional QoS which can provide topic_statistics with a stable QoS separate from
// the subscription's current QoS settings which could be unstable.
rclcpp::QoS qos = SystemDefaultsQoS();
// Explicitly set the enough depth to avoid missing the statistics messages.
rclcpp::QoS qos = SystemDefaultsQoS().keep_last(10);
};
TopicStatisticsOptions topic_stats_options;

View File

@@ -21,6 +21,7 @@
#include <tuple>
#include "rcpputils/shared_library.hpp"
#include "rosidl_runtime_cpp/action_type_support_decl.hpp"
#include "rosidl_runtime_cpp/message_type_support_decl.hpp"
#include "rosidl_runtime_cpp/service_type_support_decl.hpp"
@@ -57,7 +58,7 @@ get_message_typesupport_handle(
/// Extract the service type support handle from the library.
/**
* The library needs to match the topic type. The shared library must stay loaded for the lifetime of the result.
* 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"
@@ -72,6 +73,24 @@ 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
*/
RCLCPP_PUBLIC
const rosidl_action_type_support_t *
get_action_typesupport_handle(
const std::string & type,
const std::string & typesupport_identifier,
rcpputils::SharedLibrary & library);
} // namespace rclcpp
#endif // RCLCPP__TYPESUPPORT_HELPERS_HPP_

View File

@@ -176,7 +176,9 @@ public:
for (; ii < wait_set.size_of_timers(); ++ii) {
if (rcl_wait_set.timers[ii] != nullptr) {
ret = wait_set.timers(ii);
break;
if (ret) {
break;
}
}
}
}
@@ -217,7 +219,9 @@ public:
if (rcl_wait_set.subscriptions[ii] != nullptr) {
ret = wait_set.subscriptions(ii);
rcl_wait_set.subscriptions[ii] = nullptr;
break;
if (ret) {
break;
}
}
}
}
@@ -237,7 +241,9 @@ public:
if (rcl_wait_set.services[ii] != nullptr) {
ret = wait_set.services(ii);
rcl_wait_set.services[ii] = nullptr;
break;
if (ret) {
break;
}
}
}
}
@@ -257,7 +263,9 @@ public:
if (rcl_wait_set.clients[ii] != nullptr) {
ret = wait_set.clients(ii);
rcl_wait_set.clients[ii] = nullptr;
break;
if (ret) {
break;
}
}
}
}

View File

@@ -216,6 +216,11 @@ public:
shared_waitables_
);
if (this->needs_pruning_) {
this->storage_prune_deleted_entities();
this->needs_pruning_ = false;
}
this->storage_release_ownerships();
}
@@ -455,59 +460,60 @@ public:
size_t size_of_subscriptions() const
{
return shared_subscriptions_.size();
return subscriptions_.size();
}
size_t size_of_timers() const
{
return shared_timers_.size();
return timers_.size();
}
size_t size_of_clients() const
{
return shared_clients_.size();
return clients_.size();
}
size_t size_of_services() const
{
return shared_services_.size();
return services_.size();
}
size_t size_of_waitables() const
{
return shared_waitables_.size();
return waitables_.size();
}
std::shared_ptr<rclcpp::SubscriptionBase>
subscriptions(size_t ii) const
{
return shared_subscriptions_[ii].subscription;
return subscriptions_[ii].lock();
}
std::shared_ptr<rclcpp::TimerBase>
timers(size_t ii) const
{
return shared_timers_[ii];
return timers_[ii].lock();
}
std::shared_ptr<rclcpp::ClientBase>
clients(size_t ii) const
{
return shared_clients_[ii];
return clients_[ii].lock();
}
std::shared_ptr<rclcpp::ServiceBase>
services(size_t ii) const
{
return shared_services_[ii];
return services_[ii].lock();
}
std::shared_ptr<rclcpp::Waitable>
waitables(size_t ii) const
{
return shared_waitables_[ii].waitable;
return waitables_[ii].lock();
}
private:
size_t ownership_reference_counter_ = 0;
SequenceOfWeakSubscriptions subscriptions_;

View File

@@ -160,6 +160,15 @@ public:
services_,
waitables_
);
if(this->needs_pruning_) {
// we need to throw here, as the indexing of the rcl_waitset is broken,
// in case of invalid entries
throw std::runtime_error(
"StaticStorage::storage_rebuild_rcl_wait_set(): entity weak_ptr "
"unexpectedly expired in static entity storage");
}
}
// storage_add_subscription() explicitly not declared here

View File

@@ -734,8 +734,6 @@ private:
wait_result_dirty_ = false;
// this method comes from the SynchronizationPolicy
this->sync_wait_result_acquire();
// this method comes from the StoragePolicy
this->storage_acquire_ownerships();
}
/// Called by the WaitResult's destructor to release resources.
@@ -752,8 +750,6 @@ private:
}
wait_result_holding_ = false;
wait_result_dirty_ = false;
// this method comes from the StoragePolicy
this->storage_release_ownerships();
// this method comes from the SynchronizationPolicy
this->sync_wait_result_release();
}

View File

@@ -18,6 +18,7 @@
#include <atomic>
#include <functional>
#include <memory>
#include <vector>
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -27,6 +28,8 @@
namespace rclcpp
{
class TimerBase;
class Waitable
{
public:
@@ -258,6 +261,17 @@ public:
void
clear_on_ready_callback() = 0;
/// Returns all timers used by this waitable
/**
* Must return all timers used within the waitable.
* Note, it is not supported, that timers are added
* or removed over the lifetime of the waitable.
*/
RCLCPP_PUBLIC
virtual
std::vector<std::shared_ptr<rclcpp::TimerBase>>
get_timers() const = 0;
private:
std::atomic<bool> in_use_by_wait_set_{false};
}; // class Waitable

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.0.0</version>
<version>29.6.0</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>

View File

@@ -83,16 +83,6 @@ Clock::now() const
return now;
}
void
Clock::cancel_sleep_or_wait()
{
{
std::unique_lock lock(impl_->wait_mutex_);
impl_->stop_sleeping_ = true;
}
impl_->cv_.notify_one();
}
bool
Clock::sleep_until(
Time until,
@@ -367,4 +357,245 @@ Clock::create_jump_callback(
// *INDENT-ON*
}
class ClockWaiter::ClockWaiterImpl
{
private:
std::condition_variable cv_;
rclcpp::Clock::SharedPtr clock_;
bool time_source_changed_ = false;
std::function<void(const rcl_time_jump_t &)> post_time_jump_callback;
bool
wait_until_system_time(
std::unique_lock<std::mutex> & lock,
const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
{
auto system_time = std::chrono::system_clock::time_point(
// Cast because system clock resolution is too big for nanoseconds on some systems
std::chrono::duration_cast<std::chrono::system_clock::duration>(
std::chrono::nanoseconds(abs_time.nanoseconds())));
return cv_.wait_until(lock, system_time, pred);
}
bool
wait_until_steady_time(
std::unique_lock<std::mutex> & lock,
const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
{
// Synchronize because RCL steady clock epoch might differ from chrono::steady_clock epoch
const rclcpp::Time rcl_entry = clock_->now();
const std::chrono::steady_clock::time_point chrono_entry = std::chrono::steady_clock::now();
const rclcpp::Duration delta_t = abs_time - rcl_entry;
const std::chrono::steady_clock::time_point chrono_until =
chrono_entry + std::chrono::nanoseconds(delta_t.nanoseconds());
return cv_.wait_until(lock, chrono_until, pred);
}
bool
wait_until_ros_time(
std::unique_lock<std::mutex> & lock,
const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
{
// Install jump handler for any amount of time change, for two purposes:
// - if ROS time is active, check if time reached on each new clock sample
// - Trigger via on_clock_change to detect if time source changes, to invalidate sleep
rcl_jump_threshold_t threshold;
threshold.on_clock_change = true;
// 0 is disable, so -1 and 1 are smallest possible time changes
threshold.min_backward.nanoseconds = -1;
threshold.min_forward.nanoseconds = 1;
time_source_changed_ = false;
post_time_jump_callback = [this, &lock] (const rcl_time_jump_t & jump)
{
if (jump.clock_change != RCL_ROS_TIME_NO_CHANGE) {
std::lock_guard<std::mutex> lk(*lock.mutex());
time_source_changed_ = true;
}
cv_.notify_one();
};
// Note this is a trade-off. Adding the callback for every call
// is expensive for high frequency calls. For low frequency waits
// its more overhead to have the callback being called all the time.
// As we expect the use case to be low frequency calls to wait_until
// with relative big pauses between the calls, we install it on demand.
auto clock_handler = clock_->create_jump_callback(
nullptr,
post_time_jump_callback,
threshold);
if (!clock_->ros_time_is_active()) {
auto system_time = std::chrono::system_clock::time_point(
// Cast because system clock resolution is too big for nanoseconds on some systems
std::chrono::duration_cast<std::chrono::system_clock::duration>(
std::chrono::nanoseconds(abs_time.nanoseconds())));
return cv_.wait_until(lock, system_time, [this, &pred] () {
return time_source_changed_ || pred();
});
}
// RCL_ROS_TIME with ros_time_is_active.
// Just wait without "until" because installed
// jump callbacks wake the cv on every new sample.
cv_.wait(lock, [this, &pred, &abs_time] () {
return clock_->now() >= abs_time || time_source_changed_ || pred();
});
return clock_->now() < abs_time;
}
public:
explicit ClockWaiterImpl(const rclcpp::Clock::SharedPtr & clock)
:clock_(clock)
{
}
bool
wait_until(
std::unique_lock<std::mutex> & lock,
const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
{
switch(clock_->get_clock_type()) {
case RCL_CLOCK_UNINITIALIZED:
throw std::runtime_error("Error, wait on uninitialized clock called");
case RCL_ROS_TIME:
return wait_until_ros_time(lock, abs_time, pred);
break;
case RCL_STEADY_TIME:
return wait_until_steady_time(lock, abs_time, pred);
break;
case RCL_SYSTEM_TIME:
return wait_until_system_time(lock, abs_time, pred);
break;
}
return false;
}
void
notify_one()
{
cv_.notify_one();
}
};
ClockWaiter::ClockWaiter(const rclcpp::Clock::SharedPtr & clock)
:impl_(std::make_unique<ClockWaiterImpl>(clock))
{
}
ClockWaiter::~ClockWaiter() = default;
bool
ClockWaiter::wait_until(
std::unique_lock<std::mutex> & lock,
const rclcpp::Time & abs_time, const std::function<bool ()> & pred)
{
return impl_->wait_until(lock, abs_time, pred);
}
void
ClockWaiter::notify_one()
{
impl_->notify_one();
}
class ClockConditionalVariable::Impl
{
std::mutex pred_mutex_;
bool shutdown_ = false;
rclcpp::Context::SharedPtr context_;
rclcpp::OnShutdownCallbackHandle shutdown_cb_handle_;
ClockWaiter::UniquePtr clock_;
public:
Impl(const rclcpp::Clock::SharedPtr & clock, rclcpp::Context::SharedPtr context)
:context_(context),
clock_(std::make_unique<ClockWaiter>(clock))
{
if (!context_ || !context_->is_valid()) {
throw std::runtime_error("context cannot be slept with because it's invalid");
}
// Wake this thread if the context is shutdown
shutdown_cb_handle_ = context_->add_on_shutdown_callback(
[this]() {
{
std::unique_lock lock(pred_mutex_);
shutdown_ = true;
}
clock_->notify_one();
});
}
~Impl()
{
context_->remove_on_shutdown_callback(shutdown_cb_handle_);
}
bool
wait_until(
std::unique_lock<std::mutex> & lock, rclcpp::Time until,
const std::function<bool ()> & pred)
{
if(lock.mutex() != &pred_mutex_) {
throw std::runtime_error(
"ClockConditionalVariable::wait_until: Internal error, given lock does not use"
" mutex returned by this->mutex()");
}
clock_->wait_until(lock, until, [this, &pred] () -> bool {
return shutdown_ || pred();
});
return true;
}
void
notify_one()
{
clock_->notify_one();
}
std::mutex &
mutex()
{
return pred_mutex_;
}
};
ClockConditionalVariable::ClockConditionalVariable(
const rclcpp::Clock::SharedPtr & clock,
rclcpp::Context::SharedPtr context)
:impl_(std::make_unique<Impl>(clock, context))
{
}
ClockConditionalVariable::~ClockConditionalVariable() = default;
void
ClockConditionalVariable::notify_one()
{
impl_->notify_one();
}
bool
ClockConditionalVariable::wait_until(
std::unique_lock<std::mutex> & lock, rclcpp::Time until,
const std::function<bool ()> & pred)
{
return impl_->wait_until(lock, until, pred);
}
std::mutex &
ClockConditionalVariable::mutex()
{
return impl_->mutex();
}
} // namespace rclcpp

View File

@@ -23,10 +23,9 @@ namespace detail
void
RMWImplementationSpecificPublisherPayload::modify_rmw_publisher_options(
rmw_publisher_options_t & rmw_publisher_options) const
[[maybe_unused]] rmw_publisher_options_t & rmw_publisher_options) const
{
// By default, do not mutate the rmw publisher options.
(void)rmw_publisher_options;
}
} // namespace detail

View File

@@ -23,10 +23,9 @@ namespace detail
void
RMWImplementationSpecificSubscriptionPayload::modify_rmw_subscription_options(
rmw_subscription_options_t & rmw_subscription_options) const
[[maybe_unused]] rmw_subscription_options_t & rmw_subscription_options) const
{
// By default, do not mutate the rmw subscription options.
(void)rmw_subscription_options;
}
} // namespace detail

View File

@@ -31,11 +31,10 @@ namespace detail
std::vector<std::string>
get_unparsed_ros_arguments(
int argc, char const * const * argv,
[[maybe_unused]] int argc, char const * const * argv,
rcl_arguments_t * arguments,
rcl_allocator_t allocator)
{
(void)argc;
std::vector<std::string> unparsed_ros_arguments;
int unparsed_ros_args_count = rcl_arguments_get_count_unparsed_ros(arguments);
if (unparsed_ros_args_count > 0) {

View File

@@ -93,7 +93,9 @@ throw_from_rcl_error(
RCLErrorBase::RCLErrorBase(rcl_ret_t ret, const rcl_error_state_t * error_state)
: ret(ret), message(error_state->message), file(error_state->file), line(error_state->line_number),
formatted_message(rcl_get_error_string().str)
{}
{
rcl_reset_error();
}
RCLError::RCLError(
rcl_ret_t ret,

View File

@@ -66,7 +66,7 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
notify_waitable_(std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(
[this]() {
this->entities_need_rebuild_.store(true);
})),
}, options.context)),
entities_need_rebuild_(true),
collector_(notify_waitable_),
wait_set_({}, {}, {}, {}, {}, {}, options.context),
@@ -84,7 +84,9 @@ Executor::Executor(const rclcpp::ExecutorOptions & options)
notify_waitable_->add_guard_condition(interrupt_guard_condition_);
notify_waitable_->add_guard_condition(shutdown_guard_condition_);
wait_set_.add_waitable(notify_waitable_);
// we need to initially rebuild the collection,
// so that the notify_waitable_ is added
collect_entities();
}
Executor::~Executor()
@@ -167,10 +169,9 @@ Executor::get_automatically_added_callback_groups_from_nodes()
void
Executor::add_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
[[maybe_unused]] rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
bool notify)
{
(void) node_ptr;
this->collector_.add_callback_group(group_ptr);
try {
@@ -371,7 +372,14 @@ Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
// both spin_some and spin_all wait for work at the beginning
wait_result_.reset();
wait_for_work(std::chrono::milliseconds(0));
bool just_waited = true;
bool entity_states_fully_polled = true;
if (entities_need_rebuild_) {
// if the last wait triggered a collection rebuild, we need to call
// wait_for_work once more, in order to do a collection rebuild and collect
// events from the just added entities
entity_states_fully_polled = false;
}
// The logic of this while loop is as follows:
//
@@ -393,12 +401,14 @@ Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
AnyExecutable any_exec;
if (get_next_ready_executable(any_exec)) {
execute_any_executable(any_exec);
just_waited = false;
// during the execution some entity might got ready therefore we need
// to repoll the states of all entities
entity_states_fully_polled = false;
} else {
// if nothing is ready, reset the result to clear it
wait_result_.reset();
if (just_waited) {
if (entity_states_fully_polled) {
// there was no work after just waiting, always exit in this case
// before the exhaustive condition can be checked
break;
@@ -408,7 +418,13 @@ Executor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive)
// if exhaustive, wait for work again
// this only happens for spin_all; spin_some only waits at the start
wait_for_work(std::chrono::milliseconds(0));
just_waited = true;
entity_states_fully_polled = true;
if (entities_need_rebuild_) {
// if the last wait triggered a collection rebuild, we need to call
// wait_for_work once more, in order to do a collection rebuild and
// collect events from the just added entities
entity_states_fully_polled = false;
}
} else {
break;
}
@@ -568,6 +584,7 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
"rcl_return_loaned_message_from_subscription() failed for subscription on topic "
"'%s': %s",
subscription->get_topic_name(), rcl_get_error_string().str);
rcl_reset_error();
}
loaned_msg = nullptr;
}
@@ -731,11 +748,14 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
{
std::lock_guard<std::mutex> guard(mutex_);
if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
this->collect_entities();
}
}
this->wait_result_.emplace(wait_set_.wait(timeout));
if (!this->wait_result_ || this->wait_result_->kind() == WaitResultKind::Empty) {
RCUTILS_LOG_WARN_NAMED(
"rclcpp",

View File

@@ -20,8 +20,11 @@ namespace rclcpp
namespace executors
{
ExecutorNotifyWaitable::ExecutorNotifyWaitable(std::function<void(void)> on_execute_callback)
: execute_callback_(on_execute_callback)
ExecutorNotifyWaitable::ExecutorNotifyWaitable(
std::function<void(void)> on_execute_callback,
const rclcpp::Context::SharedPtr & context)
: execute_callback_(on_execute_callback),
guard_condition_(std::make_shared<rclcpp::GuardCondition>(context))
{
}
@@ -30,6 +33,9 @@ ExecutorNotifyWaitable::ExecutorNotifyWaitable(ExecutorNotifyWaitable & other)
std::lock_guard<std::mutex> lock(other.guard_condition_mutex_);
this->execute_callback_ = other.execute_callback_;
this->notify_guard_conditions_ = other.notify_guard_conditions_;
this->guard_condition_ = other.guard_condition_;
this->idxs_of_added_guard_condition_ = other.idxs_of_added_guard_condition_;
this->needs_processing = other.needs_processing;
}
ExecutorNotifyWaitable & ExecutorNotifyWaitable::operator=(ExecutorNotifyWaitable & other)
@@ -38,6 +44,9 @@ ExecutorNotifyWaitable & ExecutorNotifyWaitable::operator=(ExecutorNotifyWaitabl
std::lock_guard<std::mutex> lock(other.guard_condition_mutex_);
this->execute_callback_ = other.execute_callback_;
this->notify_guard_conditions_ = other.notify_guard_conditions_;
this->guard_condition_ = other.guard_condition_;
this->idxs_of_added_guard_condition_ = other.idxs_of_added_guard_condition_;
this->needs_processing = other.needs_processing;
}
return *this;
}
@@ -47,21 +56,42 @@ ExecutorNotifyWaitable::add_to_wait_set(rcl_wait_set_t & wait_set)
{
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
// Note: no guard conditions need to be re-triggered, since the guard
// conditions in this class are not tracking a stateful condition, but instead
// only serve to interrupt the wait set when new information is available to
// consider.
for (auto weak_guard_condition : this->notify_guard_conditions_) {
auto guard_condition = weak_guard_condition.lock();
if (!guard_condition) {continue;}
idxs_of_added_guard_condition_.clear();
idxs_of_added_guard_condition_.reserve(notify_guard_conditions_.size());
rcl_guard_condition_t * cond = &guard_condition->get_rcl_guard_condition();
rcl_ret_t ret = rcl_wait_set_add_guard_condition(&wait_set, cond, NULL);
if(needs_processing) {
rcl_guard_condition_t * cond = &guard_condition_->get_rcl_guard_condition();
size_t rcl_index;
rcl_ret_t ret = rcl_wait_set_add_guard_condition(&wait_set, cond, &rcl_index);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "failed to add guard condition to wait set");
}
idxs_of_added_guard_condition_.push_back(rcl_index);
// we want to directly wake up any way, not need to add the other guard conditions
guard_condition_->trigger();
return;
}
// Note: no guard conditions need to be re-triggered, since the guard
// conditions in this class are not tracking a stateful condition, but instead
// only serve to interrupt the wait set when new information is available to
// consider.
for (const auto & guard_condition : this->notify_guard_conditions_) {
rcl_guard_condition_t * cond = &guard_condition->get_rcl_guard_condition();
size_t rcl_index;
rcl_ret_t ret = rcl_wait_set_add_guard_condition(&wait_set, cond, &rcl_index);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "failed to add guard condition to wait set");
}
idxs_of_added_guard_condition_.push_back(rcl_index);
}
}
@@ -71,20 +101,23 @@ ExecutorNotifyWaitable::is_ready(const rcl_wait_set_t & wait_set)
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
bool any_ready = false;
for (size_t ii = 0; ii < wait_set.size_of_guard_conditions; ++ii) {
const auto * rcl_guard_condition = wait_set.guard_conditions[ii];
for (size_t rcl_index : idxs_of_added_guard_condition_) {
if(rcl_index >= wait_set.size_of_guard_conditions) {
throw std::runtime_error(
"ExecutorNotifyWaitable::is_ready: Internal error, got index out of range");
}
const auto * rcl_guard_condition = wait_set.guard_conditions[rcl_index];
if (nullptr == rcl_guard_condition) {
continue;
}
for (const auto & weak_guard_condition : this->notify_guard_conditions_) {
auto guard_condition = weak_guard_condition.lock();
if (guard_condition && &guard_condition->get_rcl_guard_condition() == rcl_guard_condition) {
any_ready = true;
break;
}
}
any_ready = true;
needs_processing = true;
break;
}
return any_ready;
}
@@ -92,6 +125,9 @@ void
ExecutorNotifyWaitable::execute(const std::shared_ptr<void> & /*data*/)
{
std::lock_guard<std::mutex> lock(execute_mutex_);
needs_processing = false;
this->execute_callback_();
}
@@ -102,9 +138,8 @@ ExecutorNotifyWaitable::take_data()
}
std::shared_ptr<void>
ExecutorNotifyWaitable::take_data_by_entity_id(size_t id)
ExecutorNotifyWaitable::take_data_by_entity_id([[maybe_unused]] size_t id)
{
(void) id;
return nullptr;
}
@@ -122,11 +157,7 @@ ExecutorNotifyWaitable::set_on_ready_callback(std::function<void(size_t, int)> c
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
on_ready_callback_ = gc_callback;
for (auto weak_gc : notify_guard_conditions_) {
auto gc = weak_gc.lock();
if (!gc) {
continue;
}
for (const auto & gc : notify_guard_conditions_) {
gc->set_on_trigger_callback(on_ready_callback_);
}
}
@@ -138,11 +169,7 @@ ExecutorNotifyWaitable::clear_on_ready_callback()
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
on_ready_callback_ = nullptr;
for (auto weak_gc : notify_guard_conditions_) {
auto gc = weak_gc.lock();
if (!gc) {
continue;
}
for (const auto & gc : notify_guard_conditions_) {
gc->set_on_trigger_callback(nullptr);
}
}
@@ -159,9 +186,9 @@ void
ExecutorNotifyWaitable::add_guard_condition(rclcpp::GuardCondition::WeakPtr weak_guard_condition)
{
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
auto guard_condition = weak_guard_condition.lock();
if (guard_condition && notify_guard_conditions_.count(weak_guard_condition) == 0) {
notify_guard_conditions_.insert(weak_guard_condition);
const auto & guard_condition = weak_guard_condition.lock();
if (guard_condition && notify_guard_conditions_.count(guard_condition) == 0) {
notify_guard_conditions_.insert(guard_condition);
if (on_ready_callback_) {
guard_condition->set_on_trigger_callback(on_ready_callback_);
}
@@ -172,11 +199,17 @@ void
ExecutorNotifyWaitable::remove_guard_condition(rclcpp::GuardCondition::WeakPtr weak_guard_condition)
{
std::lock_guard<std::mutex> lock(guard_condition_mutex_);
if (notify_guard_conditions_.count(weak_guard_condition) != 0) {
notify_guard_conditions_.erase(weak_guard_condition);
auto guard_condition = weak_guard_condition.lock();
const auto & guard_condition = weak_guard_condition.lock();
if (!guard_condition) {
// If the lock did not work, the guard condition can't be
// saved in the sets, therefore we don't need to remove it
return;
}
auto it = notify_guard_conditions_.find(guard_condition);
if (it != notify_guard_conditions_.end()) {
notify_guard_conditions_.erase(it);
// If this notify waitable doesn't have an on_ready_callback, then there's nothing to unset
if (guard_condition && on_ready_callback_) {
if (on_ready_callback_) {
guard_condition->set_on_trigger_callback(nullptr);
}
}
@@ -189,5 +222,12 @@ ExecutorNotifyWaitable::get_number_of_ready_guard_conditions()
return notify_guard_conditions_.size();
}
std::vector<std::shared_ptr<rclcpp::TimerBase>>
ExecutorNotifyWaitable::get_timers() const
{
return {};
}
} // namespace executors
} // namespace rclcpp

View File

@@ -79,9 +79,8 @@ MultiThreadedExecutor::get_number_of_threads()
}
void
MultiThreadedExecutor::run(size_t this_thread_number)
MultiThreadedExecutor::run([[maybe_unused]] size_t this_thread_number)
{
(void)this_thread_number;
while (rclcpp::ok(this->context_) && spinning.load()) {
rclcpp::AnyExecutable any_exec;
{

View File

@@ -110,10 +110,29 @@ StaticSingleThreadedExecutor::spin_once_impl(std::chrono::nanoseconds timeout)
std::optional<rclcpp::WaitResult<rclcpp::WaitSet>>
StaticSingleThreadedExecutor::collect_and_wait(std::chrono::nanoseconds timeout)
{
// we need to make sure that callback groups don't get out of scope
// during the wait. As in jazzy, they are not covered by the DynamicStorage,
// we explicitly hold them here as a bugfix
std::vector<rclcpp::CallbackGroup::SharedPtr> cbgs;
if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
this->collect_entities();
}
auto callback_groups = this->collector_.get_all_callback_groups();
cbgs.resize(callback_groups.size());
for(const auto & w_ptr : callback_groups) {
auto shr_ptr = w_ptr.lock();
if(shr_ptr) {
cbgs.push_back(std::move(shr_ptr));
}
}
auto wait_result = wait_set_.wait(std::chrono::nanoseconds(timeout));
// drop references to the callback groups, before trying to execute anything
cbgs.clear();
if (wait_result.kind() == WaitResultKind::Empty) {
RCUTILS_LOG_WARN_NAMED(
"rclcpp",

View File

@@ -318,10 +318,8 @@ EventsExecutor::execute_event(const ExecutorEvent & event)
}
void
EventsExecutor::handle_updated_entities(bool notify)
EventsExecutor::handle_updated_entities([[maybe_unused]] bool notify)
{
(void)notify;
// Do not rebuild if we don't need to.
// A rebuild event could be generated, but then
// this function could end up being called from somewhere else
@@ -408,8 +406,16 @@ EventsExecutor::refresh_current_collection(
[this](auto waitable) {
waitable->set_on_ready_callback(
this->create_waitable_callback(waitable.get()));
for (const auto & t : waitable->get_timers()) {
timers_manager_->add_timer(t);
}
},
[](auto waitable) {waitable->clear_on_ready_callback();});
[this](auto waitable) {
waitable->clear_on_ready_callback();
for (const auto & t : waitable->get_timers()) {
timers_manager_->remove_timer(t);
}
});
}
std::function<void(size_t)>

View File

@@ -109,6 +109,13 @@ GenericClient::handle_response(
if (std::holds_alternative<Promise>(value)) {
auto & promise = std::get<Promise>(value);
promise.set_value(std::move(response));
} else if (std::holds_alternative<CallbackTypeValueVariant>(value)) {
auto & inner = std::get<CallbackTypeValueVariant>(value);
const auto & callback = std::get<CallbackType>(inner);
auto & promise = std::get<Promise>(inner);
auto & future = std::get<SharedFuture>(inner);
promise.set_value(std::move(response));
callback(std::move(future));
}
}
@@ -128,6 +135,18 @@ GenericClient::remove_pending_request(int64_t request_id)
return pending_requests_.erase(request_id) != 0u;
}
bool
GenericClient::remove_pending_request(const FutureAndRequestId & future)
{
return this->remove_pending_request(future.request_id);
}
bool
GenericClient::remove_pending_request(const SharedFutureAndRequestId & future)
{
return this->remove_pending_request(future.request_id);
}
std::optional<GenericClient::CallbackInfoVariant>
GenericClient::get_and_erase_pending_request(int64_t request_number)
{

View File

@@ -56,10 +56,9 @@ GenericSubscription::handle_serialized_message(
void
GenericSubscription::handle_loaned_message(
void * message, const rclcpp::MessageInfo & message_info)
[[maybe_unused]] void * message,
[[maybe_unused]] const rclcpp::MessageInfo & message_info)
{
(void) message;
(void) message_info;
throw rclcpp::exceptions::UnimplementedError(
"handle_loaned_message is not implemented for GenericSubscription");
}
@@ -111,20 +110,17 @@ GenericSubscription::create_dynamic_message()
void
GenericSubscription::return_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message)
[[maybe_unused]] rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message)
{
(void) message;
throw rclcpp::exceptions::UnimplementedError(
"return_dynamic_message is not implemented for GenericSubscription");
}
void
GenericSubscription::handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info)
[[maybe_unused]] const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
[[maybe_unused]] const rclcpp::MessageInfo & message_info)
{
(void) message;
(void) message_info;
throw rclcpp::exceptions::UnimplementedError(
"handle_dynamic_message is not implemented for GenericSubscription");
}

View File

@@ -55,34 +55,6 @@ get_node_logger(const rcl_node_t * node)
return rclcpp::get_logger(logger_name);
}
// TODO(ahcorde): Remove deprecated class on the next release (in Rolling after Kilted).
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
rcpputils::fs::path
get_logging_directory()
{
char * log_dir = NULL;
auto allocator = rcutils_get_default_allocator();
rcl_logging_ret_t ret = rcl_logging_get_logging_directory(allocator, &log_dir);
if (RCL_LOGGING_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
std::string path{log_dir};
allocator.deallocate(log_dir, allocator.state);
return path;
}
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
std::filesystem::path
get_log_directory()
{

View File

@@ -689,7 +689,7 @@ Node::create_generic_client(
node_base_,
node_graph_,
node_services_,
service_name,
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
service_type,
qos,
group);

View File

@@ -132,6 +132,7 @@ NodeBase::NodeBase(
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Error in destruction of rosout publisher: %s", rcl_get_error_string().str);
rcl_reset_error();
}
}
}
@@ -139,6 +140,7 @@ NodeBase::NodeBase(
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Error in destruction of rcl node handle: %s", rcl_get_error_string().str);
rcl_reset_error();
}
delete node;
});

View File

@@ -192,6 +192,65 @@ format_range_reason(const std::string & name, const char * range_type)
return ss.str();
}
RCLCPP_LOCAL
rcl_interfaces::msg::SetParametersResult
__check_integer_range(
const rcl_interfaces::msg::ParameterDescriptor & descriptor,
const int64_t value)
{
rcl_interfaces::msg::SetParametersResult result;
result.successful = true;
auto integer_range = descriptor.integer_range.at(0);
if (value == integer_range.from_value || value == integer_range.to_value) {
return result;
}
if ((value < integer_range.from_value) || (value > integer_range.to_value)) {
result.successful = false;
result.reason = format_range_reason(descriptor.name, "integer");
return result;
}
if (integer_range.step == 0) {
return result;
}
if (((value - integer_range.from_value) % integer_range.step) == 0) {
return result;
}
result.successful = false;
result.reason = format_range_reason(descriptor.name, "integer");
return result;
}
RCLCPP_LOCAL
rcl_interfaces::msg::SetParametersResult
__check_double_range(
const rcl_interfaces::msg::ParameterDescriptor & descriptor,
const double value)
{
rcl_interfaces::msg::SetParametersResult result;
result.successful = true;
auto fp_range = descriptor.floating_point_range.at(0);
if (__are_doubles_equal(value, fp_range.from_value) || __are_doubles_equal(value,
fp_range.to_value))
{
return result;
}
if ((value < fp_range.from_value) || (value > fp_range.to_value)) {
result.successful = false;
result.reason = format_range_reason(descriptor.name, "floating point");
return result;
}
if (fp_range.step == 0.0) {
return result;
}
double rounded_div = std::round((value - fp_range.from_value) / fp_range.step);
if (__are_doubles_equal(value, fp_range.from_value + rounded_div * fp_range.step)) {
return result;
}
result.successful = false;
result.reason = format_range_reason(descriptor.name, "floating point");
return result;
}
RCLCPP_LOCAL
rcl_interfaces::msg::SetParametersResult
__check_parameter_value_in_range(
@@ -201,49 +260,39 @@ __check_parameter_value_in_range(
rcl_interfaces::msg::SetParametersResult result;
result.successful = true;
if (!descriptor.integer_range.empty() && value.get_type() == rclcpp::PARAMETER_INTEGER) {
int64_t v = value.get<int64_t>();
auto integer_range = descriptor.integer_range.at(0);
if (v == integer_range.from_value || v == integer_range.to_value) {
return result;
result = __check_integer_range(descriptor, value.get<int64_t>());
return result;
}
if (!descriptor.integer_range.empty() && value.get_type() == rclcpp::PARAMETER_INTEGER_ARRAY) {
std::vector<int64_t> val_array = value.get<std::vector<int64_t>>();
for (const int64_t & val : val_array) {
result = __check_integer_range(descriptor, val);
if (!result.successful) {
return result;
}
}
if ((v < integer_range.from_value) || (v > integer_range.to_value)) {
result.successful = false;
result.reason = format_range_reason(descriptor.name, "integer");
return result;
}
if (integer_range.step == 0) {
return result;
}
if (((v - integer_range.from_value) % integer_range.step) == 0) {
return result;
}
result.successful = false;
result.reason = format_range_reason(descriptor.name, "integer");
return result;
}
if (!descriptor.floating_point_range.empty() && value.get_type() == rclcpp::PARAMETER_DOUBLE) {
double v = value.get<double>();
auto fp_range = descriptor.floating_point_range.at(0);
if (__are_doubles_equal(v, fp_range.from_value) || __are_doubles_equal(v, fp_range.to_value)) {
return result;
}
if ((v < fp_range.from_value) || (v > fp_range.to_value)) {
result.successful = false;
result.reason = format_range_reason(descriptor.name, "floating point");
return result;
}
if (fp_range.step == 0.0) {
return result;
}
double rounded_div = std::round((v - fp_range.from_value) / fp_range.step);
if (__are_doubles_equal(v, fp_range.from_value + rounded_div * fp_range.step)) {
return result;
}
result.successful = false;
result.reason = format_range_reason(descriptor.name, "floating point");
result = __check_double_range(descriptor, value.get<double>());
return result;
}
if (!descriptor.floating_point_range.empty() &&
value.get_type() == rclcpp::PARAMETER_DOUBLE_ARRAY)
{
std::vector<double> val_array = value.get<std::vector<double>>();
for (const double & val : val_array) {
result = __check_double_range(descriptor, val);
if (!result.successful) {
return result;
}
}
return result;
}
return result;
}
@@ -1191,3 +1240,10 @@ NodeParameters::get_parameter_overrides() const
{
return parameter_overrides_;
}
void
NodeParameters::enable_parameter_modification()
{
std::lock_guard<std::recursive_mutex> lock(mutex_);
parameter_modification_enabled_ = true;
}

View File

@@ -105,6 +105,7 @@ public:
RCLCPP_ERROR(
logger_, "Failed to initialize ~/get_type_description service: %s",
rcl_get_error_string().str);
rcl_reset_error();
throw std::runtime_error(
"Failed to initialize ~/get_type_description service.");
}

View File

@@ -80,6 +80,7 @@ NodeOptions::operator=(const NodeOptions & other)
this->clock_type_ = other.clock_type_;
this->clock_qos_ = other.clock_qos_;
this->use_clock_thread_ = other.use_clock_thread_;
this->enable_logger_service_ = other.enable_logger_service_;
this->parameter_event_qos_ = other.parameter_event_qos_;
this->rosout_qos_ = other.rosout_qos_;
this->parameter_event_publisher_options_ = other.parameter_event_publisher_options_;

View File

@@ -28,6 +28,9 @@ ParameterEventsFilter::ParameterEventsFilter(
const std::vector<EventType> & types)
: event_(event)
{
if (!event) {
throw std::invalid_argument("event cannot be null");
}
if (std::find(types.begin(), types.end(), EventType::NEW) != types.end()) {
for (auto & new_parameter : event->new_parameters) {
if (std::find(names.begin(), names.end(), new_parameter.name) != names.end()) {

View File

@@ -135,15 +135,28 @@ void
PublisherBase::bind_event_callbacks(
const PublisherEventCallbacks & event_callbacks, bool use_default_callbacks)
{
if (event_callbacks.deadline_callback) {
this->add_event_handler(
event_callbacks.deadline_callback,
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
try {
if (event_callbacks.deadline_callback) {
this->add_event_handler(
event_callbacks.deadline_callback,
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for deadline; not supported");
}
if (event_callbacks.liveliness_callback) {
this->add_event_handler(
event_callbacks.liveliness_callback,
RCL_PUBLISHER_LIVELINESS_LOST);
try {
if (event_callbacks.liveliness_callback) {
this->add_event_handler(
event_callbacks.liveliness_callback,
RCL_PUBLISHER_LIVELINESS_LOST);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for liveliness; not supported");
}
QOSOfferedIncompatibleQoSCallbackType incompatible_qos_cb;
@@ -160,9 +173,9 @@ PublisherBase::bind_event_callbacks(
this->add_event_handler(incompatible_qos_cb, RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_DEBUG(
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for incompatible qos; wrong callback type");
"Failed to add event handler for incompatible qos; not supported");
}
IncompatibleTypeCallbackType incompatible_type_cb;
@@ -179,14 +192,21 @@ PublisherBase::bind_event_callbacks(
this->add_event_handler(incompatible_type_cb, RCL_PUBLISHER_INCOMPATIBLE_TYPE);
}
} catch (UnsupportedEventTypeException & /*exc*/) {
RCLCPP_DEBUG(
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for incompatible type; wrong callback type");
"Failed to add event handler for incompatible type; not supported");
}
if (event_callbacks.matched_callback) {
this->add_event_handler(
event_callbacks.matched_callback,
RCL_PUBLISHER_MATCHED);
try {
if (event_callbacks.matched_callback) {
this->add_event_handler(
event_callbacks.matched_callback,
RCL_PUBLISHER_MATCHED);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for matched; not supported");
}
}
@@ -347,10 +367,8 @@ PublisherBase::default_incompatible_qos_callback(
void
PublisherBase::default_incompatible_type_callback(
rclcpp::IncompatibleTypeInfo & event) const
[[maybe_unused]] rclcpp::IncompatibleTypeInfo & event) const
{
(void)event;
RCLCPP_WARN(
rclcpp::get_logger(rcl_node_get_logger_name(rcl_node_handle_.get())),
"Incompatible type on topic '%s', no messages will be sent to it.", get_topic_name());

View File

@@ -407,7 +407,7 @@ ParameterEventsQoS::ParameterEventsQoS(const QoSInitialization & qos_initializat
{}
RosoutQoS::RosoutQoS(const QoSInitialization & rosout_initialization)
: QoS(rosout_initialization, rcl_qos_profile_rosout_default)
: QoS(rosout_initialization, rmw_qos_profile_rosout_default)
{}
SystemDefaultsQoS::SystemDefaultsQoS(const QoSInitialization & qos_initialization)

View File

@@ -36,7 +36,6 @@ SerializationBase::SerializationBase(const rosidl_message_type_support_t * type_
void SerializationBase::serialize_message(
const void * ros_message, SerializedMessage * serialized_message) const
{
rcpputils::check_true(nullptr != type_support_, "Typesupport is nullpointer.");
rcpputils::check_true(nullptr != ros_message, "ROS message is nullpointer.");
rcpputils::check_true(nullptr != serialized_message, "Serialized message is nullpointer.");
@@ -52,7 +51,6 @@ void SerializationBase::serialize_message(
void SerializationBase::deserialize_message(
const SerializedMessage * serialized_message, void * ros_message) const
{
rcpputils::check_true(nullptr != type_support_, "Typesupport is nullpointer.");
rcpputils::check_true(nullptr != serialized_message, "Serialized message is nullpointer.");
rcpputils::check_true(
0u != serialized_message->capacity(),

View File

@@ -122,6 +122,7 @@ SerializedMessage::~SerializedMessage()
RCLCPP_ERROR(
get_logger("rclcpp"),
"Failed to destroy serialized message: %s", rcl_get_error_string().str);
rcl_reset_error();
}
}
}

View File

@@ -112,16 +112,28 @@ void
SubscriptionBase::bind_event_callbacks(
const SubscriptionEventCallbacks & event_callbacks, bool use_default_callbacks)
{
if (event_callbacks.deadline_callback) {
this->add_event_handler(
event_callbacks.deadline_callback,
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
try {
if (event_callbacks.deadline_callback) {
this->add_event_handler(
event_callbacks.deadline_callback,
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for deadline; not supported");
}
if (event_callbacks.liveliness_callback) {
this->add_event_handler(
event_callbacks.liveliness_callback,
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
try {
if (event_callbacks.liveliness_callback) {
this->add_event_handler(
event_callbacks.liveliness_callback,
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for liveliness; not supported");
}
QOSRequestedIncompatibleQoSCallbackType incompatible_qos_cb;
@@ -139,7 +151,9 @@ SubscriptionBase::bind_event_callbacks(
this->add_event_handler(incompatible_qos_cb, RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
// pass
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for incompatible qos; not supported");
}
IncompatibleTypeCallbackType incompatible_type_cb;
@@ -156,18 +170,33 @@ SubscriptionBase::bind_event_callbacks(
this->add_event_handler(incompatible_type_cb, RCL_SUBSCRIPTION_INCOMPATIBLE_TYPE);
}
} catch (UnsupportedEventTypeException & /*exc*/) {
// pass
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for incompatible type; not supported");
}
if (event_callbacks.message_lost_callback) {
this->add_event_handler(
event_callbacks.message_lost_callback,
RCL_SUBSCRIPTION_MESSAGE_LOST);
try {
if (event_callbacks.message_lost_callback) {
this->add_event_handler(
event_callbacks.message_lost_callback,
RCL_SUBSCRIPTION_MESSAGE_LOST);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for message lost; not supported");
}
if (event_callbacks.matched_callback) {
this->add_event_handler(
event_callbacks.matched_callback,
RCL_SUBSCRIPTION_MATCHED);
try {
if (event_callbacks.matched_callback) {
this->add_event_handler(
event_callbacks.matched_callback,
RCL_SUBSCRIPTION_MATCHED);
}
} catch (const UnsupportedEventTypeException & /*exc*/) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Failed to add event handler for matched; not supported");
}
}
@@ -352,10 +381,8 @@ SubscriptionBase::default_incompatible_qos_callback(
void
SubscriptionBase::default_incompatible_type_callback(
rclcpp::IncompatibleTypeInfo & event) const
[[maybe_unused]] rclcpp::IncompatibleTypeInfo & event) const
{
(void)event;
RCLCPP_WARN(
rclcpp::get_logger(rcl_node_get_logger_name(node_handle_.get())),
"Incompatible type on topic '%s', no messages will be sent to it.", get_topic_name());

View File

@@ -276,13 +276,8 @@ public:
on_set_parameters_callback_ = node_parameters_->add_on_set_parameters_callback(
std::bind(&TimeSource::NodeState::on_set_parameters, this, std::placeholders::_1));
// TODO(tfoote) use parameters interface not subscribe to events via topic ticketed #609
parameter_subscription_ = rclcpp::AsyncParametersClient::on_parameter_event(
node_topics_,
[this](std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event) {
this->on_parameter_event(event);
});
post_set_parameters_callback_ = node_parameters_->add_post_set_parameters_callback(
std::bind(&TimeSource::NodeState::post_set_parameters, this, std::placeholders::_1));
}
// Detach the attached node
@@ -296,8 +291,11 @@ public:
if (on_set_parameters_callback_) {
node_parameters_->remove_on_set_parameters_callback(on_set_parameters_callback_.get());
}
if (post_set_parameters_callback_) {
node_parameters_->remove_post_set_parameters_callback(post_set_parameters_callback_.get());
}
on_set_parameters_callback_.reset();
parameter_subscription_.reset();
post_set_parameters_callback_.reset();
node_base_.reset();
node_topics_.reset();
node_graph_.reset();
@@ -346,7 +344,6 @@ private:
std::mutex clock_sub_lock_;
rclcpp::CallbackGroup::SharedPtr clock_callback_group_;
rclcpp::executors::SingleThreadedExecutor::SharedPtr clock_executor_;
std::promise<void> cancel_clock_executor_promise_;
// The clock callback itself
void clock_cb(std::shared_ptr<const rosgraph_msgs::msg::Clock> msg)
@@ -392,12 +389,10 @@ private:
clock_executor_ =
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(exec_options);
if (!clock_executor_thread_.joinable()) {
cancel_clock_executor_promise_ = std::promise<void>{};
clock_executor_thread_ = std::thread(
[this]() {
auto future = cancel_clock_executor_promise_.get_future();
clock_executor_->add_callback_group(clock_callback_group_, node_base_);
clock_executor_->spin_until_future_complete(future);
clock_executor_->spin();
}
);
}
@@ -429,7 +424,6 @@ private:
{
std::lock_guard<std::mutex> guard(clock_sub_lock_);
if (clock_executor_thread_.joinable()) {
cancel_clock_executor_promise_.set_value();
clock_executor_->cancel();
clock_executor_thread_.join();
clock_executor_->remove_callback_group(clock_callback_group_);
@@ -440,9 +434,9 @@ private:
// On set Parameters callback handle
node_interfaces::OnSetParametersCallbackHandle::SharedPtr on_set_parameters_callback_{nullptr};
// Parameter Event subscription
using ParamSubscriptionT = rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>;
std::shared_ptr<ParamSubscriptionT> parameter_subscription_;
// Post set Parameters callback handle
node_interfaces::PostSetParametersCallbackHandle::SharedPtr
post_set_parameters_callback_{nullptr};
// Callback for parameter settings
rcl_interfaces::msg::SetParametersResult on_set_parameters(
@@ -465,52 +459,27 @@ private:
return result;
}
// Callback for parameter updates
void on_parameter_event(std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event)
// Callback for post parameter updates
void post_set_parameters(const std::vector<rclcpp::Parameter> & parameters)
{
std::lock_guard<std::mutex> guard(node_base_lock_);
if (node_base_ == nullptr) {
// Do nothing if node_base_ is nullptr because it means the TimeSource is now
// without an attached node
return;
}
// Filter out events on 'use_sim_time' parameter instances in other nodes.
if (event->node != node_base_->get_fully_qualified_name()) {
return;
}
// Filter for only 'use_sim_time' being added or changed.
rclcpp::ParameterEventsFilter filter(event, {"use_sim_time"},
{rclcpp::ParameterEventsFilter::EventType::NEW,
rclcpp::ParameterEventsFilter::EventType::CHANGED});
for (auto & it : filter.get_events()) {
if (it.second->value.type != ParameterType::PARAMETER_BOOL) {
RCLCPP_ERROR(logger_, "use_sim_time parameter cannot be set to anything but a bool");
continue;
// "use_sim_time" has been set, so just applys it to internal states
for (const auto & param : parameters) {
if (param.get_name() == "use_sim_time") {
if (param.as_bool()) {
parameter_state_ = SET_TRUE;
clocks_state_.enable_ros_time();
create_clock_sub();
} else {
parameter_state_ = SET_FALSE;
destroy_clock_sub();
clocks_state_.disable_ros_time();
}
}
if (it.second->value.bool_value) {
parameter_state_ = SET_TRUE;
clocks_state_.enable_ros_time();
create_clock_sub();
} else {
parameter_state_ = SET_FALSE;
destroy_clock_sub();
clocks_state_.disable_ros_time();
}
}
// Handle the case that use_sim_time was deleted.
rclcpp::ParameterEventsFilter deleted(event, {"use_sim_time"},
{rclcpp::ParameterEventsFilter::EventType::DELETED});
for (auto & it : deleted.get_events()) {
(void) it; // if there is a match it's already matched, don't bother reading it.
// If the parameter is deleted mark it as unset but don't change state.
parameter_state_ = UNSET;
}
}
// An enum to hold the parameter state
enum UseSimTimeParameterState {UNSET, SET_TRUE, SET_FALSE};
enum UseSimTimeParameterState {SET_TRUE, SET_FALSE};
UseSimTimeParameterState parameter_state_;
};

View File

@@ -139,14 +139,6 @@ get_typesupport_library(const std::string & type, const std::string & typesuppor
return std::make_shared<rcpputils::SharedLibrary>(library_path);
}
const rosidl_message_type_support_t * get_typesupport_handle(
const std::string & type,
const std::string & typesupport_identifier,
rcpputils::SharedLibrary & library)
{
return get_message_typesupport_handle(type, typesupport_identifier, library);
}
const rosidl_message_type_support_t * get_message_typesupport_handle(
const std::string & type,
const std::string & typesupport_identifier,
@@ -177,4 +169,19 @@ const rosidl_service_type_support_t * get_service_typesupport_handle(
));
}
const rosidl_action_type_support_t * get_action_typesupport_handle(
const std::string & type,
const std::string & typesupport_identifier,
rcpputils::SharedLibrary & library)
{
static const std::string typesupport_name = "action";
static const std::string symbol_part_name = "__get_action_type_support_handle__";
static const std::string middle_module_additional = "action";
return static_cast<const rosidl_action_type_support_t *>(get_typesupport_handle_impl(
type, typesupport_identifier, typesupport_name, symbol_part_name,
middle_module_additional, library
));
}
} // namespace rclcpp

View File

@@ -1 +1 @@
string data
string data

View File

@@ -57,10 +57,10 @@ ament_add_test_label(test_client mimick)
if(TARGET test_client)
target_link_libraries(test_client ${PROJECT_NAME} mimick ${rcl_interfaces_TARGETS} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_clock test_clock.cpp)
ament_add_test_label(test_clock mimick)
if(TARGET test_clock)
target_link_libraries(test_clock ${PROJECT_NAME} mimick ${rcl_interfaces_TARGETS} ${test_msgs_TARGETS})
ament_add_gtest(test_clock_conditional test_clock_conditional.cpp)
ament_add_test_label(test_clock_conditional mimick)
if(TARGET test_clock_conditional)
target_link_libraries(test_clock_conditional ${PROJECT_NAME} mimick ${rcl_interfaces_TARGETS} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_copy_all_parameter_values test_copy_all_parameter_values.cpp)
if(TARGET test_copy_all_parameter_values)
@@ -191,7 +191,7 @@ ament_add_gtest(test_node_interfaces__node_parameters
node_interfaces/test_node_parameters.cpp)
ament_add_test_label(test_node_interfaces__node_parameters mimick)
if(TARGET test_node_interfaces__node_parameters)
target_link_libraries(test_node_interfaces__node_parameters ${PROJECT_NAME} mimick rcpputils::rcpputils)
target_link_libraries(test_node_interfaces__node_parameters ${PROJECT_NAME} mimick rcpputils::rcpputils ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_node_interfaces__node_services
node_interfaces/test_node_services.cpp)
@@ -555,7 +555,7 @@ if(TARGET test_executor_notify_waitable)
target_link_libraries(test_executor_notify_waitable ${PROJECT_NAME} mimick rcpputils::rcpputils)
endif()
ament_add_gtest(test_events_executor executors/test_events_executor.cpp TIMEOUT 5)
ament_add_gtest(test_events_executor executors/test_events_executor.cpp TIMEOUT 60)
if(TARGET test_events_executor)
target_link_libraries(test_events_executor ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
@@ -669,6 +669,14 @@ 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
TEST_NAME test_qos_event${target_suffix}
ENV ${rmw_implementation_env_var}

View File

@@ -61,9 +61,8 @@ class ExecutorTypeNames
{
public:
template<typename T>
static std::string GetName(int idx)
static std::string GetName([[maybe_unused]] int idx)
{
(void)idx;
if (std::is_same<T, rclcpp::executors::SingleThreadedExecutor>()) {
return "SingleThreadedExecutor";
}

View File

@@ -48,9 +48,8 @@ TEST_F(TestEventsExecutor, run_pub_sub)
bool msg_received = false;
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
"topic", rclcpp::SensorDataQoS(),
[&msg_received](test_msgs::msg::Empty::ConstSharedPtr msg)
[&msg_received]([[maybe_unused]] test_msgs::msg::Empty::ConstSharedPtr msg)
{
(void)msg;
msg_received = true;
});
@@ -115,8 +114,8 @@ TEST_F(TestEventsExecutor, run_clients_servers)
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
client->async_send_request(
request,
[&response_received](rclcpp::Client<test_msgs::srv::Empty>::SharedFuture result_future) {
(void)result_future;
[&response_received]([[maybe_unused]] rclcpp::Client<test_msgs::srv::Empty>::SharedFuture
result_future){
response_received = true;
});
@@ -479,14 +478,140 @@ TEST_F(TestEventsExecutor, test_default_incompatible_qos_callbacks)
const auto timeout = std::chrono::seconds(10);
ex.spin_until_future_complete(log_msgs_future, timeout);
EXPECT_EQ(
"New subscription discovered on topic '/test_topic', requesting incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
pub_log_msg);
EXPECT_EQ(
"New publisher discovered on topic '/test_topic', offering incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
sub_log_msg);
rclcpp::QoSCheckCompatibleResult qos_compatible = rclcpp::qos_check_compatible(
publisher->get_actual_qos(), subscription->get_actual_qos());
if (qos_compatible.compatibility == rclcpp::QoSCompatibility::Error) {
EXPECT_EQ(
"New subscription discovered on topic '/test_topic', requesting incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
pub_log_msg);
EXPECT_EQ(
"New publisher discovered on topic '/test_topic', offering incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
sub_log_msg);
} else {
EXPECT_EQ("", pub_log_msg);
EXPECT_EQ("", sub_log_msg);
}
rcutils_logging_set_output_handler(original_output_handler);
}
class TestWaitableWithTimer : public rclcpp::Waitable
{
static constexpr int TimerCallbackType = 0;
public:
explicit TestWaitableWithTimer(const rclcpp::Context::SharedPtr & context)
{
auto timer_callback = [this] () {
timer_ready = true;
if (ready_callback) {
// inform executor that the waitable is ready to process a timer event
ready_callback(1, TimerCallbackType);
}
};
timer =
std::make_shared<rclcpp::WallTimer<decltype (timer_callback)>>(std::chrono::milliseconds(10),
std::move(timer_callback), context);
}
void
add_to_wait_set(rcl_wait_set_t & /*wait_set*/) override {}
bool
is_ready(const rcl_wait_set_t & /*wait_set*/) override
{
return false;
}
std::shared_ptr<void>
take_data() override
{
return std::shared_ptr<void>(nullptr);
}
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override
{
if (id != TimerCallbackType) {
throw std::runtime_error("Internal error, got wrong ID for take data");
}
return nullptr;
}
void
execute(const std::shared_ptr<void> &) override
{
if (timer_ready) {
timer_triggered_waitable_and_waitable_was_executed = true;
}
}
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override
{
ready_callback = callback;
}
void
clear_on_ready_callback() override
{
ready_callback = std::function<void(size_t, int)>();
}
size_t
get_number_of_ready_guard_conditions() override
{
return 0;
}
std::vector<std::shared_ptr<rclcpp::TimerBase>> get_timers() const override
{
return {timer};
}
bool timerTriggeredWaitable() const
{
return timer_triggered_waitable_and_waitable_was_executed;
}
private:
std::atomic_bool timer_triggered_waitable_and_waitable_was_executed = false;
std::atomic_bool timer_ready = false;
rclcpp::TimerBase::SharedPtr timer;
std::function<void(size_t, int)> ready_callback;
};
TEST_F(TestEventsExecutor, waitable_with_timer)
{
auto node = std::make_shared<rclcpp::Node>("node");
auto waitable =
std::make_shared<TestWaitableWithTimer>(rclcpp::contexts::get_global_default_context());
EventsExecutor executor;
executor.add_node(node);
node->get_node_waitables_interface()->add_waitable(waitable,
node->get_node_base_interface()->get_default_callback_group());
std::thread spinner([&executor]() {executor.spin();});
size_t cnt = 0;
while (!waitable->timerTriggeredWaitable()) {
std::this_thread::sleep_for(10ms);
cnt++;
// This should terminate after ~20 ms, so a timeout of 500ms should be plenty
EXPECT_LT(cnt, 51);
if (cnt > 50) {
// timeout case
break;
}
}
executor.cancel();
spinner.join();
EXPECT_TRUE(waitable->timerTriggeredWaitable());
}

View File

@@ -76,8 +76,7 @@ TEST_F(TestExecutorNotifyWaitable, wait) {
std::make_shared<rclcpp::executors::ExecutorNotifyWaitable>(on_execute_callback);
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto notify_guard_condition =
node->get_node_base_interface()->get_shared_notify_guard_condition();
auto notify_guard_condition = std::make_shared<rclcpp::GuardCondition>();
EXPECT_NO_THROW(waitable->add_guard_condition(notify_guard_condition));
auto default_cbg = node->get_node_base_interface()->get_default_callback_group();
@@ -86,12 +85,15 @@ TEST_F(TestExecutorNotifyWaitable, wait) {
auto waitables = node->get_node_waitables_interface();
waitables->add_waitable(std::static_pointer_cast<rclcpp::Waitable>(waitable), default_cbg);
// notify the guard condition, this should trigger the on_execute_callback
notify_guard_condition->trigger();
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
executor.spin_all(std::chrono::seconds(1));
EXPECT_EQ(1u, on_execute_calls);
// on_execute_callback doesn't change if the topology doesn't change
// no further trigger, therefore no further callback
executor.spin_all(std::chrono::seconds(1));
EXPECT_EQ(1u, on_execute_calls);
}

View File

@@ -388,7 +388,7 @@ to_nanoseconds_helper(DurationT duration)
// - works nominally (it can execute entities)
// - it can execute multiple items at once
// - it does not wait for work to be available before returning
TYPED_TEST(TestExecutors, spin_some)
TYPED_TEST(TestExecutors, spinSome)
{
using ExecutorType = TypeParam;
@@ -484,7 +484,7 @@ TYPED_TEST(TestExecutors, spin_some)
// do not properly implement max_duration (it seems), so disable this test
// for them in the meantime.
// see: https://github.com/ros2/rclcpp/issues/2462
TYPED_TEST(TestExecutorsStable, spin_some_max_duration)
TYPED_TEST(TestExecutorsStable, spinSomeMaxDuration)
{
using ExecutorType = TypeParam;
@@ -714,6 +714,13 @@ TYPED_TEST(TestExecutors, notifyTwiceWhileSpinning)
sub1_msg_count++;
});
// Wait for the subscription to be matched
size_t tries = 10000;
while (this->publisher->get_subscription_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(this->publisher->get_subscription_count(), 2);
// Publish a message and verify it's received
this->publisher->publish(test_msgs::msg::Empty());
auto start = std::chrono::steady_clock::now();
@@ -731,12 +738,18 @@ TYPED_TEST(TestExecutors, notifyTwiceWhileSpinning)
sub2_msg_count++;
});
// Wait for the subscription to be matched
tries = 10000;
while (this->publisher->get_subscription_count() < 3 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(this->publisher->get_subscription_count(), 3);
// Publish a message and verify it's received by both subscriptions
this->publisher->publish(test_msgs::msg::Empty());
start = std::chrono::steady_clock::now();
while (
sub1_msg_count == 1 &&
sub2_msg_count == 0 &&
(sub1_msg_count == 1 || sub2_msg_count == 0) &&
(std::chrono::steady_clock::now() - start) < 10s)
{
std::this_thread::sleep_for(1ms);
@@ -820,7 +833,7 @@ TEST(TestExecutors, testSpinWithNonDefaultContext)
rclcpp::shutdown(non_default_context);
}
TYPED_TEST(TestExecutors, release_ownership_entity_after_spinning_cancel)
TYPED_TEST(TestExecutors, releaseOwnershipEntityAfterSpinningCancel)
{
using ExecutorType = TypeParam;
ExecutorType executor;
@@ -843,3 +856,309 @@ TYPED_TEST(TestExecutors, release_ownership_entity_after_spinning_cancel)
EXPECT_EQ(server.use_count(), 1);
}
TYPED_TEST(TestExecutors, testRaceDropCallbackGroupFromSecondThread)
{
using ExecutorType = TypeParam;
// Create an executor
ExecutorType executor;
executor.add_node(this->node);
// Start spinning
auto executor_thread = std::thread(
[&executor]() {
executor.spin();
});
// As the problem is a race, we do this multiple times,
// to raise our chances of hitting the problem
for (size_t i = 0; i < 10; i++) {
auto cg = this->node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
auto timer = this->node->create_timer(1s, [] {}, cg);
// sleep a bit, so that the spin thread can pick up the callback group
// and add it to the executor
std::this_thread::sleep_for(5ms);
// At this point the callbackgroup should be used within the waitset of the executor
// as we leave the scope, the reference to cg will be dropped.
// If the executor has a race, we will experience a segfault at this point.
}
executor.cancel();
executor_thread.join();
}
TYPED_TEST(TestExecutors, dropSomeTimer)
{
using ExecutorType = TypeParam;
ExecutorType executor;
auto node = std::make_shared<rclcpp::Node>("test_node");
bool timer1_works = false;
bool timer2_works = false;
auto timer1 = node->create_timer(std::chrono::milliseconds(10), [&timer1_works]() {
timer1_works = true;
});
auto timer2 = node->create_timer(std::chrono::milliseconds(10), [&timer2_works]() {
timer2_works = true;
});
executor.add_node(node);
// first let's make sure that both timers work
auto max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
while(!timer1_works || !timer2_works) {
// let the executor pick up the node and the timers
executor.spin_all(std::chrono::milliseconds(10));
const auto cur_time = std::chrono::steady_clock::now();
ASSERT_LT(cur_time, max_end_time);
}
// delete timer 2. Note, the executor uses an unordered map internally, to order
// the entities added to the rcl waitset therefore the order is kind of undefined,
// and this test may be flaky. In case it triggers, something is most likely
// really broken.
timer2.reset();
timer1_works = false;
timer2_works = false;
max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
while(!timer1_works && !timer2_works) {
// let the executor pick up the node and the timers
executor.spin_all(std::chrono::milliseconds(10));
const auto cur_time = std::chrono::steady_clock::now();
ASSERT_LT(cur_time, max_end_time);
}
ASSERT_TRUE(timer1_works || timer2_works);
}
TYPED_TEST(TestExecutors, dropSomeNodeWithTimer)
{
using ExecutorType = TypeParam;
ExecutorType executor;
auto node1 = std::make_shared<rclcpp::Node>("test_node_1");
auto node2 = std::make_shared<rclcpp::Node>("test_node_2");
bool timer1_works = false;
bool timer2_works = false;
auto timer1 = node1->create_timer(std::chrono::milliseconds(10), [&timer1_works]() {
timer1_works = true;
});
auto timer2 = node2->create_timer(std::chrono::milliseconds(10), [&timer2_works]() {
timer2_works = true;
});
executor.add_node(node1);
executor.add_node(node2);
// first let's make sure that both timers work
auto max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
while(!timer1_works || !timer2_works) {
// let the executor pick up the node and the timers
executor.spin_all(std::chrono::milliseconds(10));
const auto cur_time = std::chrono::steady_clock::now();
ASSERT_LT(cur_time, max_end_time);
}
// delete node 1.
node1 = nullptr;
timer2_works = false;
max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
while(!timer2_works) {
// let the executor pick up the node and the timer
executor.spin_all(std::chrono::milliseconds(10));
const auto cur_time = std::chrono::steady_clock::now();
ASSERT_LT(cur_time, max_end_time);
}
ASSERT_TRUE(timer2_works);
}
TYPED_TEST(TestExecutors, dropSomeSubscription)
{
using ExecutorType = TypeParam;
ExecutorType executor;
auto node = std::make_shared<rclcpp::Node>("test_node");
bool sub1_works = false;
bool sub2_works = false;
auto sub1 = node->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
[&sub1_works](const test_msgs::msg::Empty &) {
sub1_works = true;
});
auto sub2 = node->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
[&sub2_works](const test_msgs::msg::Empty &) {
sub2_works = true;
});
auto pub = node->create_publisher<test_msgs::msg::Empty>("/test_drop", 10);
executor.add_node(node);
// first let's make sure that both timers work
auto max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
while(!sub1_works || !sub2_works) {
pub->publish(test_msgs::msg::Empty());
// let the executor pick up the node and the timers
executor.spin_all(std::chrono::milliseconds(10));
const auto cur_time = std::chrono::steady_clock::now();
ASSERT_LT(cur_time, max_end_time);
}
// delete subscription 2. Note, the executor uses an unordered map internally, to order
// the entities added to the rcl waitset therefore the order is kind of undefined,
// and this test may be flaky. In case it triggers, something is most likely
// really broken.
sub2.reset();
sub1_works = false;
sub2_works = false;
max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
while(!sub1_works && !sub2_works) {
pub->publish(test_msgs::msg::Empty());
// let the executor pick up the node and the timers
executor.spin_all(std::chrono::milliseconds(10));
const auto cur_time = std::chrono::steady_clock::now();
ASSERT_LT(cur_time, max_end_time);
}
ASSERT_TRUE(sub1_works || sub2_works);
}
TYPED_TEST(TestExecutors, dropSomeNodesWithSubscription)
{
using ExecutorType = TypeParam;
ExecutorType executor;
auto node = std::make_shared<rclcpp::Node>("test_node");
auto node1 = std::make_shared<rclcpp::Node>("test_node_1");
auto node2 = std::make_shared<rclcpp::Node>("test_node_2");
bool sub1_works = false;
bool sub2_works = false;
auto sub1 = node1->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
[&sub1_works](const test_msgs::msg::Empty &) {
sub1_works = true;
});
auto sub2 = node2->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
[&sub2_works](const test_msgs::msg::Empty &) {
sub2_works = true;
});
auto pub = node->create_publisher<test_msgs::msg::Empty>("/test_drop", 10);
executor.add_node(node);
executor.add_node(node1);
executor.add_node(node2);
// first let's make sure that both subscribers work
auto max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
while(!sub1_works || !sub2_works) {
pub->publish(test_msgs::msg::Empty());
// let the executor pick up the node and the timers
executor.spin_all(std::chrono::milliseconds(10));
const auto cur_time = std::chrono::steady_clock::now();
ASSERT_LT(cur_time, max_end_time);
}
// delete node 2.
node2 = nullptr;
sub1_works = false;
max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
while(!sub1_works) {
pub->publish(test_msgs::msg::Empty());
// let the executor pick up the node and the timers
executor.spin_all(std::chrono::milliseconds(10));
const auto cur_time = std::chrono::steady_clock::now();
ASSERT_LT(cur_time, max_end_time);
}
ASSERT_TRUE(sub1_works);
}
TYPED_TEST(TestExecutors, dropSubscriptionDuringCallback)
{
using ExecutorType = TypeParam;
ExecutorType executor;
auto node = std::make_shared<rclcpp::Node>("test_node");
bool sub1_works = false;
bool sub2_works = false;
auto cbg = node->create_callback_group(rclcpp::CallbackGroupType::MutuallyExclusive, true);
rclcpp::SubscriptionOptions sub_ops;
sub_ops.callback_group = cbg;
rclcpp::SubscriptionBase::SharedPtr sub1;
rclcpp::SubscriptionBase::SharedPtr sub2;
// Note, the executor uses an unordered map internally, to order
// the entities added to the rcl waitset therefore the order of the subscriptions
// is kind of undefined. Therefore each sub deletes the other one.
sub1 = node->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
[&sub1_works, &sub2](const test_msgs::msg::Empty &) {
sub1_works = true;
// delete the other subscriber
sub2.reset();
}, sub_ops);
sub2 = node->create_subscription<test_msgs::msg::Empty>("/test_drop", 10,
[&sub2_works, &sub1](const test_msgs::msg::Empty &) {
sub2_works = true;
// delete the other subscriber
sub1.reset();
}, sub_ops);
auto pub = node->create_publisher<test_msgs::msg::Empty>("/test_drop", 10);
// wait for both subs to be connected
auto max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(1500);
while ((sub1->get_publisher_count() == 0) || (sub2->get_publisher_count() == 0)) {
const auto cur_time = std::chrono::steady_clock::now();
ASSERT_LT(cur_time, max_end_time);
}
executor.add_node(node);
// publish some messages, until one subscriber fired. As both subscribers are
// connected to the same topic, they should fire in the same wait.
max_end_time = std::chrono::steady_clock::now() + std::chrono::milliseconds(100);
while (!sub1_works && !sub2_works) {
pub->publish(test_msgs::msg::Empty());
// let the executor pick up the node and the timers
executor.spin_all(std::chrono::milliseconds(10));
const auto cur_time = std::chrono::steady_clock::now();
ASSERT_LT(cur_time, max_end_time);
}
// only one subscriber must have worked, as the other
// one was deleted during the callback
ASSERT_TRUE(!sub1_works || !sub2_works);
}

View File

@@ -267,12 +267,15 @@ public:
void TearDown()
{
node.reset();
executor.cancel();
// Clean up thread object
if (standalone_thread.joinable()) {
standalone_thread.join();
}
node.reset();
sim_clock_node.reset();
}
std::shared_ptr<TimerNode> node;
@@ -310,7 +313,6 @@ TYPED_TEST(TestTimerCancelBehavior, testTimer1CancelledWithExecutorSpin) {
this->sim_clock_node->sleep_for(50ms);
this->node->CancelTimer1();
this->sim_clock_node->sleep_for(150ms);
this->executor.cancel();
int t1_runs = this->node->GetTimer1Cnt();
int t2_runs = this->node->GetTimer2Cnt();
@@ -328,7 +330,6 @@ TYPED_TEST(TestTimerCancelBehavior, testTimer2CancelledWithExecutorSpin) {
this->sim_clock_node->sleep_for(50ms);
this->node->CancelTimer2();
this->sim_clock_node->sleep_for(150ms);
this->executor.cancel();
int t1_runs = this->node->GetTimer1Cnt();
int t2_runs = this->node->GetTimer2Cnt();
@@ -355,8 +356,6 @@ TYPED_TEST(TestTimerCancelBehavior, testHeadTimerCancelThenResetBehavior) {
int t1_runs_final = this->node->GetTimer1Cnt();
int t2_runs_final = this->node->GetTimer2Cnt();
this->executor.cancel();
// T1 should have been restarted, and execute about 15 additional times.
// Check 10 greater than initial, to account for some timing jitter.
EXPECT_LT(t1_runs_initial + 50, t1_runs_final);
@@ -384,8 +383,6 @@ TYPED_TEST(TestTimerCancelBehavior, testBackTimerCancelThenResetBehavior) {
int t1_runs_final = this->node->GetTimer1Cnt();
int t2_runs_final = this->node->GetTimer2Cnt();
this->executor.cancel();
// T2 should have been restarted, and execute about 15 additional times.
// Check 10 greater than initial, to account for some timing jitter.
EXPECT_LT(t2_runs_initial + 50, t2_runs_final);
@@ -419,8 +416,6 @@ TYPED_TEST(TestTimerCancelBehavior, testBothTimerCancelThenResetT1Behavior) {
int t1_runs_final = this->node->GetTimer1Cnt();
int t2_runs_final = this->node->GetTimer2Cnt();
this->executor.cancel();
// T1 and T2 should have the same initial count.
EXPECT_LE(std::abs(t1_runs_initial - t2_runs_initial), 1);
@@ -458,8 +453,6 @@ TYPED_TEST(TestTimerCancelBehavior, testBothTimerCancelThenResetT2Behavior) {
int t1_runs_final = this->node->GetTimer1Cnt();
int t2_runs_final = this->node->GetTimer2Cnt();
this->executor.cancel();
// T1 and T2 should have the same initial count.
EXPECT_LE(std::abs(t1_runs_initial - t2_runs_initial), 1);

View File

@@ -99,14 +99,6 @@ TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup_with_cbgroup)
{
using ExecutorType = TypeParam;
// TODO(alsora): Enable when https://github.com/ros2/rclcpp/pull/2595 gets merged
if (
std::is_same<ExecutorType, rclcpp::executors::SingleThreadedExecutor>() ||
std::is_same<ExecutorType, rclcpp::executors::MultiThreadedExecutor>())
{
GTEST_SKIP();
}
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event

View File

@@ -64,9 +64,8 @@ TestWaitable::take_data()
}
std::shared_ptr<void>
TestWaitable::take_data_by_entity_id(size_t id)
TestWaitable::take_data_by_entity_id([[maybe_unused]] size_t id)
{
(void) id;
return nullptr;
}

View File

@@ -18,6 +18,7 @@
#include <atomic>
#include <functional>
#include <memory>
#include <vector>
#include "rclcpp/waitable.hpp"
#include "rclcpp/guard_condition.hpp"
@@ -64,6 +65,11 @@ public:
size_t
get_is_ready_call_count() const;
std::vector<std::shared_ptr<rclcpp::TimerBase>> get_timers() const override
{
return {};
}
private:
std::atomic<size_t> trigger_count_ = 0;
std::atomic<size_t> is_ready_count_ = 0;

View File

@@ -29,6 +29,8 @@
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_parameters.hpp"
#include "test_msgs/msg/empty.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
@@ -329,6 +331,89 @@ TEST_F(TestNodeParameters, add_remove_post_set_parameters_callback) {
std::runtime_error("Post set parameter callback doesn't exist"));
}
TEST_F(TestNodeParameters, set_param_recursive_in_post_set_parameters_callback) {
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription_;
rclcpp::Publisher<test_msgs::msg::Empty>::SharedPtr publisher_;
rcl_interfaces::msg::ParameterDescriptor param_descriptor;
param_descriptor.name = "create_entities";
param_descriptor.type = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL;
param_descriptor.read_only = false;
bool result = node_parameters->declare_parameter(
"create_entities", rclcpp::ParameterValue(false), param_descriptor, false).get<bool>();
EXPECT_EQ(result, false);
// Register a callback to create/delete publisher and subscription with
// QoS override parameter options. This will call declare_parameter recursively
// during this callback.
auto sub_callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
auto callback = [&](const std::vector<rclcpp::Parameter> & parameters) {
for (const auto & parameter : parameters) {
if (parameter.get_name() == "create_entities" &&
parameter.get_type() == rclcpp::ParameterType::PARAMETER_BOOL)
{
if (parameter.as_bool()) {
ASSERT_EQ(subscription_, nullptr);
rclcpp::SubscriptionOptions sub_options;
// This will declare the QoS override parameters in this callback.
sub_options.qos_overriding_options =
rclcpp::QosOverridingOptions::with_default_policies();
subscription_ = node->create_subscription<test_msgs::msg::Empty>(
"empty",
rclcpp::QoS(10),
sub_callback,
sub_options);
ASSERT_NE(subscription_, nullptr);
ASSERT_EQ(publisher_, nullptr);
rclcpp::PublisherOptions pub_options;
// This will declare the QoS override parameters in this callback.
pub_options.qos_overriding_options =
rclcpp::QosOverridingOptions::with_default_policies();
publisher_ = node->create_publisher<test_msgs::msg::Empty>(
"empty",
rclcpp::QoS(10),
pub_options);
ASSERT_NE(publisher_, nullptr);
} else {
ASSERT_NE(subscription_, nullptr);
subscription_.reset();
ASSERT_EQ(subscription_, nullptr);
ASSERT_NE(publisher_, nullptr);
publisher_.reset();
ASSERT_EQ(publisher_, nullptr);
}
}
}
};
auto handle = node_parameters->add_post_set_parameters_callback(callback);
ASSERT_NE(handle, nullptr);
EXPECT_TRUE(node_parameters->has_parameter("create_entities"));
EXPECT_EQ(node_parameters->get_parameter("create_entities").get_value<bool>(), false);
// This will call the registered callback, that will create endpoints with
// declaring the QoS override parameters recursively.
auto results = node_parameters->set_parameters({rclcpp::Parameter("create_entities", true)});
EXPECT_TRUE(!results.empty() && results[0].successful);
EXPECT_TRUE(node_parameters->has_parameter("create_entities"));
EXPECT_EQ(node_parameters->get_parameter("create_entities").get_value<bool>(), true);
// Destroy publisher and subscription endpoints.
results = node_parameters->set_parameters({rclcpp::Parameter("create_entities", false)});
EXPECT_TRUE(!results.empty() && results[0].successful);
EXPECT_TRUE(node_parameters->has_parameter("create_entities"));
EXPECT_EQ(node_parameters->get_parameter("create_entities").get_value<bool>(), false);
// Make sure recreation can also work without any exception.
results = node_parameters->set_parameters({rclcpp::Parameter("create_entities", true)});
EXPECT_TRUE(!results.empty() && results[0].successful);
EXPECT_NO_THROW(node_parameters->remove_post_set_parameters_callback(handle.get()));
}
TEST_F(TestNodeParameters, wildcard_with_namespace)
{
rclcpp::NodeOptions opts;

View File

@@ -38,6 +38,11 @@ public:
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
std::vector<std::shared_ptr<rclcpp::TimerBase>> get_timers() const override
{
return {};
}
};
class TestNodeWaitables : public ::testing::Test

View File

@@ -58,6 +58,11 @@ public:
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
std::vector<std::shared_ptr<rclcpp::TimerBase>> get_timers() const override
{
return {};
}
};
struct RclWaitSetSizes
@@ -535,8 +540,8 @@ TEST_F(TestAllocatorMemoryStrategy, add_handles_to_wait_set_bad_arguments) {
});
allocator_memory_strategy()->collect_entities(weak_groups_to_nodes);
EXPECT_FALSE(allocator_memory_strategy()->add_handles_to_wait_set(nullptr));
EXPECT_TRUE(rcl_error_is_set());
rcl_reset_error();
// The error message is collected and already reset.
EXPECT_FALSE(rcl_error_is_set());
}
TEST_F(TestAllocatorMemoryStrategy, add_handles_to_wait_set_subscription) {

View File

@@ -184,9 +184,8 @@ class ClientTypeNames
{
public:
template<typename T>
static std::string GetName(int idx)
static std::string GetName([[maybe_unused]] int idx)
{
(void)idx;
if (std::is_same_v<T, rclcpp::Client<test_msgs::srv::Empty>>) {
return "Client";
}
@@ -431,7 +430,7 @@ TYPED_TEST(TestAllClientTypesWithServer, client_qos)
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);

View File

@@ -1,4 +1,4 @@
// Copyright 2024 Cellumation GmbH
// Copyright 2025 Cellumation GmbH
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -32,18 +32,28 @@ public:
void test_wakeup_before_sleep(const rclcpp::Clock::SharedPtr & clock)
{
std::atomic_bool thread_finished = false;
rclcpp::ClockConditionalVariable cond(clock);
bool stopSleeping = false;
std::thread wait_thread = std::thread(
[&clock, &thread_finished]()
[&cond, &clock, &stopSleeping, &thread_finished]()
{
// make sure the thread starts sleeping late
std::this_thread::sleep_for(std::chrono::milliseconds(100));
clock->sleep_until(clock->now() + std::chrono::seconds(3));
std::unique_lock lk(cond.mutex());
cond.wait_until(lk, clock->now() + std::chrono::seconds(3),
[&stopSleeping] () {return stopSleeping;});
thread_finished = true;
});
// notify the clock, that the sleep shall be interrupted
clock->cancel_sleep_or_wait();
{
std::lock_guard lk(cond.mutex());
// stop sleeping after next notification
stopSleeping = true;
}
// notify the conditional, to recheck it pred
cond.notify_one();
auto start_time = std::chrono::steady_clock::now();
auto cur_time = start_time;
@@ -61,19 +71,29 @@ public:
void test_wakeup_after_sleep(const rclcpp::Clock::SharedPtr & clock)
{
std::atomic_bool thread_finished = false;
rclcpp::ClockConditionalVariable cond(clock);
bool stopSleeping = false;
std::thread wait_thread = std::thread(
[&clock, &thread_finished]()
[&cond, &clock, &stopSleeping, &thread_finished]()
{
clock->sleep_until(clock->now() + std::chrono::seconds(3));
std::unique_lock lk(cond.mutex());
cond.wait_until(lk, clock->now() + std::chrono::seconds(3),
[&stopSleeping] () {return stopSleeping;});
thread_finished = true;
});
// make sure the thread is already sleeping before we send the cancel
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// notify the clock, that the sleep shall be interrupted
clock->cancel_sleep_or_wait();
{
std::lock_guard lk(cond.mutex());
// stop sleeping after next notification
stopSleeping = true;
}
// notify the conditional, to recheck it pred
cond.notify_one();
auto start_time = std::chrono::steady_clock::now();
auto cur_time = start_time;
@@ -113,7 +133,7 @@ protected:
};
INSTANTIATE_TEST_SUITE_P(
Clocks,
ClockConditionalVariable,
TestClockWakeup,
::testing::Values(
RCL_SYSTEM_TIME, RCL_ROS_TIME, RCL_STEADY_TIME
@@ -140,18 +160,24 @@ TEST_F(TestClockWakeup, wakeup_sleep_ros_time_active) {
TEST_F(TestClockWakeup, no_wakeup_on_sim_time) {
node->set_parameter({"use_sim_time", true});
auto clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
EXPECT_FALSE(clock->ros_time_is_active());
rclcpp::TimeSource time_source(node);
time_source.attachClock(clock);
EXPECT_TRUE(clock->ros_time_is_active());
std::atomic_bool thread_finished = false;
rclcpp::ClockConditionalVariable cond(clock);
bool stopSleeping = false;
std::thread wait_thread = std::thread(
[&clock, &thread_finished]()
[&cond, &clock, &stopSleeping, &thread_finished]()
{
// make sure the thread starts sleeping late
clock->sleep_until(clock->now() + std::chrono::milliseconds(10));
std::unique_lock lk(cond.mutex());
// only sleep for an short period
cond.wait_until(lk, clock->now() + std::chrono::milliseconds(10),
[&stopSleeping] () {return stopSleeping;});
thread_finished = true;
});
@@ -159,8 +185,13 @@ TEST_F(TestClockWakeup, no_wakeup_on_sim_time) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));
EXPECT_FALSE(thread_finished);
// notify the clock, that the sleep shall be interrupted
clock->cancel_sleep_or_wait();
{
std::lock_guard lk(cond.mutex());
// stop sleeping after next notification
stopSleeping = true;
}
// notify the conditional, to recheck it pred
cond.notify_one();
auto start_time = std::chrono::steady_clock::now();
auto cur_time = start_time;
@@ -175,55 +206,41 @@ TEST_F(TestClockWakeup, no_wakeup_on_sim_time) {
EXPECT_LT(cur_time, start_time + std::chrono::seconds(1));
}
TEST_F(TestClockWakeup, multiple_threads_wait_on_one_clock) {
TEST_F(TestClockWakeup, wakeup_on_ros_shutdown) {
auto clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
std::vector<bool> thread_finished(10, false);
std::atomic_bool thread_finished = false;
rclcpp::ClockConditionalVariable cond(clock);
std::vector<std::thread> threads;
bool stopSleeping = false;
for (size_t nr = 0; nr < thread_finished.size(); nr++) {
threads.push_back(
std::thread(
[&clock, &thread_finished, nr]()
{
// make sure the thread starts sleeping late
clock->sleep_until(clock->now() + std::chrono::seconds(10));
thread_finished[nr] = true;
}));
}
std::thread wait_thread = std::thread(
[&cond, &clock, &stopSleeping, &thread_finished]()
{
std::unique_lock lk(cond.mutex());
// only sleep for an short period
cond.wait_until(lk, clock->now() + std::chrono::seconds(10),
[&stopSleeping] () {return stopSleeping;});
thread_finished = true;
});
// wait a bit so all threads can execute the sleep_until
// wait a bit to be sure the thread is sleeping
std::this_thread::sleep_for(std::chrono::milliseconds(500));
for (const bool & finished : thread_finished) {
EXPECT_FALSE(finished);
}
EXPECT_FALSE(thread_finished);
rclcpp::shutdown();
auto start_time = std::chrono::steady_clock::now();
auto cur_time = start_time;
bool threads_finished = false;
while (!threads_finished && start_time + std::chrono::seconds(1) > cur_time) {
threads_finished = true;
for (const bool finished : thread_finished) {
if (!finished) {
threads_finished = false;
}
}
while (!thread_finished && start_time + std::chrono::seconds(1) > cur_time) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
cur_time = std::chrono::steady_clock::now();
}
for (const bool finished : thread_finished) {
EXPECT_TRUE(finished);
}
EXPECT_TRUE(thread_finished);
for (auto & thread : threads) {
thread.join();
}
wait_thread.join();
EXPECT_LT(cur_time, start_time + std::chrono::seconds(1));
}

View File

@@ -92,3 +92,19 @@ TEST_F(TestCreateSubscription, create_with_statistics) {
ASSERT_NE(nullptr, subscription);
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
}
TEST_F(TestCreateSubscription, create_with_intra_process_com) {
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
auto options = rclcpp::SubscriptionOptions();
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
ASSERT_NO_THROW(
{
subscription = rclcpp::create_subscription<test_msgs::msg::Empty>(
node, "topic_name", rclcpp::SystemDefaultsQoS(), callback, options);
});
ASSERT_NE(nullptr, subscription);
EXPECT_STREQ("/ns/topic_name", subscription->get_topic_name());
}

View File

@@ -19,11 +19,16 @@
#include <stdexcept>
#include <string>
#include "rcl_interfaces/msg/parameter_event.hpp"
#include "rclcpp/executor.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
#include "test_msgs/msg/empty.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
@@ -331,8 +336,17 @@ TEST_F(TestExecutor, spin_all_fail_wait_set_clear) {
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {});
dummy.add_node(node);
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
auto node_topics = node->get_node_topics_interface();
subscription =
rclcpp::create_subscription<test_msgs::msg::Empty>(
node_topics, "test", rclcpp::QoS(10), std::move(callback));
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_wait_set_clear, RCL_RET_ERROR);
dummy.spin_all(std::chrono::milliseconds(1));
// second spin_all triggers rcl_wait_set_clear that should be called
// whenever a waitset gets rebuild and it was not changed in size.
RCLCPP_EXPECT_THROW_EQ(
dummy.spin_all(std::chrono::milliseconds(1)),
std::runtime_error("Couldn't clear the wait set: error not set"));
@@ -354,8 +368,14 @@ TEST_F(TestExecutor, spin_some_fail_wait_set_resize) {
TEST_F(TestExecutor, spin_some_fail_add_handles_to_wait_set) {
DummyExecutor dummy;
auto node = std::make_shared<rclcpp::Node>("node", "ns");
auto timer =
node->create_wall_timer(std::chrono::milliseconds(1), [&]() {});
// create subscription explicitly, because we do not create subscription
// on /parameter_events for 'use_sim_time' parameter anymore.
auto callback = [](test_msgs::msg::Empty::ConstSharedPtr) {};
rclcpp::Subscription<test_msgs::msg::Empty>::SharedPtr subscription;
subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test", rclcpp::QoS(10), std::move(callback));
dummy.add_node(node);
auto mock = mocking_utils::patch_and_return(

View File

@@ -74,29 +74,23 @@ struct FunctionObjectOneIntOneChar
struct ObjectMember
{
int callback_one_bool(bool a)
int callback_one_bool([[maybe_unused]] bool a)
{
(void)a;
return 7;
}
int callback_one_bool_const(bool a) const
int callback_one_bool_const([[maybe_unused]] bool a) const
{
(void)a;
return 7;
}
int callback_two_bools(bool a, bool b)
int callback_two_bools([[maybe_unused]] bool a, [[maybe_unused]] bool b)
{
(void)a;
(void)b;
return 8;
}
int callback_one_bool_one_float(bool a, float b)
int callback_one_bool_one_float([[maybe_unused]] bool a, [[maybe_unused]] float b)
{
(void)a;
(void)b;
return 9;
}
};
@@ -212,20 +206,15 @@ TEST(TestFunctionTraits, arity) {
return 0;
};
auto lambda_one_int = [](int one) {
(void)one;
auto lambda_one_int = []([[maybe_unused]] int one) {
return 1;
};
auto lambda_two_ints = [](int one, int two) {
(void)one;
(void)two;
auto lambda_two_ints = []([[maybe_unused]] int one, [[maybe_unused]] int two) {
return 2;
};
auto lambda_one_int_one_char = [](int one, char two) {
(void)one;
(void)two;
auto lambda_one_int_one_char = []([[maybe_unused]] int one, [[maybe_unused]] char two) {
return 3;
};
@@ -303,20 +292,15 @@ TEST(TestFunctionTraits, argument_types) {
>::value, "Functor accepts a char as second argument");
// Test lambdas
auto lambda_one_int = [](int one) {
(void)one;
auto lambda_one_int = []([[maybe_unused]] int one) {
return 1;
};
auto lambda_two_ints = [](int one, int two) {
(void)one;
(void)two;
auto lambda_two_ints = []([[maybe_unused]] int one, [[maybe_unused]] int two) {
return 2;
};
auto lambda_one_int_one_char = [](int one, char two) {
(void)one;
(void)two;
auto lambda_one_int_one_char = []([[maybe_unused]] int one, [[maybe_unused]] char two) {
return 3;
};
@@ -533,22 +517,17 @@ TEST(TestFunctionTraits, check_arguments) {
"Functor accepts an int and a char as arguments");
// Test lambdas
auto lambda_one_int = [](int one) {
(void)one;
auto lambda_one_int = []([[maybe_unused]] int one) {
return 1;
};
(void)lambda_one_int; // to quiet clang
auto lambda_two_ints = [](int one, int two) {
(void)one;
(void)two;
auto lambda_two_ints = []([[maybe_unused]] int one, [[maybe_unused]] int two) {
return 2;
};
(void)lambda_two_ints; // to quiet clang
auto lambda_one_int_one_char = [](int one, char two) {
(void)one;
(void)two;
auto lambda_one_int_one_char = []([[maybe_unused]] int one, [[maybe_unused]] char two) {
return 3;
};
(void)lambda_one_int_one_char; // to quiet clang
@@ -603,14 +582,11 @@ TEST(TestFunctionTraits, check_arguments) {
Tests that same_arguments work.
*/
TEST(TestFunctionTraits, same_arguments) {
auto lambda_one_int = [](int one) {
(void)one;
auto lambda_one_int = []([[maybe_unused]] int one) {
return 1;
};
auto lambda_two_ints = [](int one, int two) {
(void)one;
(void)two;
auto lambda_two_ints = []([[maybe_unused]] int one, [[maybe_unused]] int two) {
return 1;
};
@@ -658,8 +634,7 @@ TEST(TestFunctionTraits, return_type) {
"Functor return ints");
// Test lambda
auto lambda_one_int_return_double = [](int one) -> double {
(void)one;
auto lambda_one_int_return_double = []([[maybe_unused]] int one) -> double {
return 1.0;
};
@@ -697,20 +672,15 @@ TEST(TestFunctionTraits, sfinae_match) {
return 0;
};
auto lambda_one_int = [](int one) {
(void)one;
auto lambda_one_int = []([[maybe_unused]] int one) {
return 1;
};
auto lambda_two_ints = [](int one, int two) {
(void)one;
(void)two;
auto lambda_two_ints = []([[maybe_unused]] int one, [[maybe_unused]] int two) {
return 2;
};
auto lambda_one_int_one_char = [](int one, char two) {
(void)one;
(void)two;
auto lambda_one_int_one_char = []([[maybe_unused]] int one, [[maybe_unused]] char two) {
return 3;
};

View File

@@ -14,6 +14,8 @@
#include <gtest/gtest.h>
#include <chrono>
#include <cstdint>
#include <string>
#include <memory>
#include <utility>
@@ -28,6 +30,7 @@
#include "../mocking_utils/patch.hpp"
#include "test_msgs/srv/empty.hpp"
#include "test_msgs/srv/basic_types.hpp"
using namespace std::chrono_literals;
@@ -218,7 +221,7 @@ TEST_F(TestGenericClient, wait_for_service) {
TEST_F(TestGenericClientSub, construction_and_destruction) {
{
auto client = subnode->create_generic_client("test_service", "test_msgs/srv/Empty");
EXPECT_STREQ(client->get_service_name(), "/ns/test_service");
EXPECT_STREQ(client->get_service_name(), "/ns/sub_ns/test_service");
}
{
@@ -228,3 +231,69 @@ TEST_F(TestGenericClientSub, construction_and_destruction) {
}, rclcpp::exceptions::InvalidServiceNameError);
}
}
TEST_F(TestGenericClientSub, async_send_request_with_request) {
const std::string service_name = "test_service";
int64_t expected_change = 1111;
auto client = node->create_generic_client(service_name, "test_msgs/srv/BasicTypes");
auto callback = [&expected_change](
const test_msgs::srv::BasicTypes::Request::SharedPtr request,
test_msgs::srv::BasicTypes::Response::SharedPtr response) {
response->int64_value = request->int64_value + expected_change;
};
auto service =
node->create_service<test_msgs::srv::BasicTypes>(service_name, std::move(callback));
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
ASSERT_TRUE(client->service_is_ready());
test_msgs::srv::BasicTypes::Request request;
request.int64_value = 12345678;
auto future = client->async_send_request(static_cast<void *>(&request));
rclcpp::spin_until_future_complete(
node->get_node_base_interface(), future, std::chrono::seconds(5));
ASSERT_TRUE(future.valid());
auto get_untyped_response = future.get();
auto typed_response =
static_cast<test_msgs::srv::BasicTypes::Response *>(get_untyped_response.get());
EXPECT_EQ(typed_response->int64_value, (request.int64_value + expected_change));
}
TEST_F(TestGenericClientSub, async_send_request_with_request_and_callback) {
const std::string service_name = "test_service";
int64_t expected_change = 2222;
auto client = node->create_generic_client(service_name, "test_msgs/srv/BasicTypes");
auto server_callback = [&expected_change](
const test_msgs::srv::BasicTypes::Request::SharedPtr request,
test_msgs::srv::BasicTypes::Response::SharedPtr response) {
response->int64_value = request->int64_value + expected_change;
};
auto service =
node->create_service<test_msgs::srv::BasicTypes>(service_name, std::move(server_callback));
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
ASSERT_TRUE(client->service_is_ready());
test_msgs::srv::BasicTypes::Request request;
request.int64_value = 12345678;
auto client_callback = [&request, &expected_change](
rclcpp::GenericClient::SharedFuture future) {
auto untyped_response = future.get();
auto typed_response =
static_cast<test_msgs::srv::BasicTypes::Response *>(untyped_response.get());
EXPECT_EQ(typed_response->int64_value, (request.int64_value + expected_change));
};
auto future =
client->async_send_request(static_cast<void *>(&request), client_callback);
rclcpp::spin_until_future_complete(
node->get_node_base_interface(), future, std::chrono::seconds(5));
}

View File

@@ -320,7 +320,7 @@ TEST_F(TestGenericService, rcl_service_request_subscription_get_actual_qos_error
TEST_F(TestGenericService, generic_service_qos) {
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);

View File

@@ -21,7 +21,7 @@
#include "rclcpp/rclcpp.hpp"
/*
Construtctor
Constructor
*/
TEST(TestIntraProcessBuffer, constructor) {
using MessageT = char;

View File

@@ -123,6 +123,7 @@ public:
return result;
}
private:
// need to store the messages somewhere otherwise the memory address will be reused
ConstMessageSharedPtr shared_msg;
MessageUniquePtr unique_msg;
@@ -158,9 +159,9 @@ class PublisherBase
public:
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
explicit PublisherBase(rclcpp::QoS qos = rclcpp::QoS(10))
: qos_profile(qos),
topic_name("topic")
explicit PublisherBase(const std::string & topic, const rclcpp::QoS & qos)
: topic_name(topic),
qos_profile(qos)
{}
virtual ~PublisherBase()
@@ -192,23 +193,23 @@ public:
}
bool
operator==(const rmw_gid_t & gid) const
operator==([[maybe_unused]] const rmw_gid_t & gid) const
{
(void)gid;
return false;
}
bool
operator==(const rmw_gid_t * gid) const
operator==([[maybe_unused]] const rmw_gid_t * gid) const
{
(void)gid;
return false;
}
rclcpp::QoS qos_profile;
std::string topic_name;
uint64_t intra_process_publisher_id_;
IntraProcessManagerWeakPtr weak_ipm_;
private:
std::string topic_name;
rclcpp::QoS qos_profile;
};
template<typename T, typename Alloc = std::allocator<void>>
@@ -223,8 +224,8 @@ public:
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>)
explicit Publisher(rclcpp::QoS qos = rclcpp::QoS(10))
: PublisherBase(qos)
explicit Publisher(const std::string & topic, const rclcpp::QoS & qos)
: PublisherBase(topic, qos)
{
auto allocator = std::make_shared<Alloc>();
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
@@ -257,12 +258,12 @@ public:
RCLCPP_SMART_PTR_ALIASES_ONLY(SubscriptionIntraProcessBase)
explicit SubscriptionIntraProcessBase(
rclcpp::Context::SharedPtr context,
const std::string & topic = "topic",
rclcpp::QoS qos = rclcpp::QoS(10))
: qos_profile(qos), topic_name(topic)
[[maybe_unused]] rclcpp::Context::SharedPtr context,
const std::string & topic,
const rclcpp::QoS & qos)
: topic_name(topic), qos_profile(qos)
{
(void)context;
// This function is intentionally left empty.
}
virtual ~SubscriptionIntraProcessBase() {}
@@ -292,8 +293,8 @@ public:
size_t
available_capacity() const = 0;
rclcpp::QoS qos_profile;
std::string topic_name;
rclcpp::QoS qos_profile;
};
template<
@@ -307,8 +308,8 @@ class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
explicit SubscriptionIntraProcessBuffer(rclcpp::QoS qos)
: SubscriptionIntraProcessBase(nullptr, "topic", qos), take_shared_method(false)
explicit SubscriptionIntraProcessBuffer(const std::string & topic, const rclcpp::QoS & qos)
: SubscriptionIntraProcessBase(nullptr, topic, qos), take_shared_method(false)
{
buffer = std::make_unique<rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>>();
}
@@ -375,8 +376,8 @@ class SubscriptionIntraProcess : public SubscriptionIntraProcessBuffer<
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
explicit SubscriptionIntraProcess(rclcpp::QoS qos = rclcpp::QoS(10))
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(qos)
explicit SubscriptionIntraProcess(const std::string & topic, const rclcpp::QoS & qos)
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(topic, qos)
{
}
};
@@ -466,12 +467,11 @@ TEST(TestIntraProcessManager, add_pub_sub) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10).best_effort());
auto p2 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
p2->topic_name = "different_topic_name";
auto p2 = std::make_shared<PublisherT>("different_topic_name", rclcpp::QoS(10).best_effort());
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).best_effort());
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10).best_effort());
auto p1_id = ipm->add_publisher(p1);
auto p2_id = ipm->add_publisher(p2);
@@ -480,24 +480,42 @@ TEST(TestIntraProcessManager, add_pub_sub) {
bool unique_ids = p1_id != p2_id && p2_id != s1_id;
ASSERT_TRUE(unique_ids);
// p1 has 1 subcription, s1
size_t p1_subs = ipm->get_subscription_count(p1_id);
// p2 has 0 subscriptions
size_t p2_subs = ipm->get_subscription_count(p2_id);
// Non-existent publisher_id has 0 subscriptions
size_t non_existing_pub_subs = ipm->get_subscription_count(42);
ASSERT_EQ(1u, p1_subs);
ASSERT_EQ(0u, p2_subs);
ASSERT_EQ(0u, non_existing_pub_subs);
auto p3 = std::make_shared<PublisherT>(rclcpp::QoS(10).reliable());
auto p3 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10).reliable());
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).reliable());
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10).reliable());
// s2 may be able to communicate with p1 depending on the RMW
auto s2_id = ipm->template add_subscription<MessageT>(s2);
// p3 can definitely communicate with s2, may be able to communicate with s1 depending on the RMW
auto p3_id = ipm->add_publisher(p3);
// p1 definitely matches subscription s1, since the topic name and QoS match exactly.
// If the RMW can match best-effort publishers to reliable subscriptions (like Zenoh can),
// then p1 will also match s2.
p1_subs = ipm->get_subscription_count(p1_id);
// No subscriptions with a topic name of "different_topic_name" were added.
p2_subs = ipm->get_subscription_count(p2_id);
// On all current RMWs (DDS and Zenoh), a reliable publisher like p3 can communicate with both
// reliable and best-effort subscriptions (s1 and s2).
size_t p3_subs = ipm->get_subscription_count(p3_id);
ASSERT_EQ(1u, p1_subs);
rclcpp::QoSCheckCompatibleResult qos_compatible =
rclcpp::qos_check_compatible(p1->get_actual_qos(), s2->get_actual_qos());
if (qos_compatible.compatibility == rclcpp::QoSCompatibility::Error) {
ASSERT_EQ(1u, p1_subs);
} else {
ASSERT_EQ(2u, p1_subs);
}
ASSERT_EQ(0u, p2_subs);
ASSERT_EQ(2u, p3_subs);
@@ -528,11 +546,11 @@ TEST(TestIntraProcessManager, single_subscription) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>();
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s1->take_shared_method = false;
auto s1_id = ipm->template add_subscription<MessageT>(s1);
@@ -543,7 +561,7 @@ TEST(TestIntraProcessManager, single_subscription) {
ASSERT_EQ(original_message_pointer, received_message_pointer_1);
ipm->remove_subscription(s1_id);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s2->take_shared_method = true;
auto s2_id = ipm->template add_subscription<MessageT>(s2);
(void)s2_id;
@@ -582,15 +600,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>();
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s1->take_shared_method = false;
auto s1_id = ipm->template add_subscription<MessageT>(s1);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s2->take_shared_method = false;
auto s2_id = ipm->template add_subscription<MessageT>(s2);
@@ -606,11 +624,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
ipm->remove_subscription(s1_id);
ipm->remove_subscription(s2_id);
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
auto s3 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s3->take_shared_method = true;
auto s3_id = ipm->template add_subscription<MessageT>(s3);
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
auto s4 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s4->take_shared_method = true;
auto s4_id = ipm->template add_subscription<MessageT>(s4);
@@ -625,11 +643,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
ipm->remove_subscription(s3_id);
ipm->remove_subscription(s4_id);
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
auto s5 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s5->take_shared_method = false;
auto s5_id = ipm->template add_subscription<MessageT>(s5);
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
auto s6 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s6->take_shared_method = false;
auto s6_id = ipm->template add_subscription<MessageT>(s6);
@@ -645,12 +663,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
ipm->remove_subscription(s5_id);
ipm->remove_subscription(s6_id);
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
auto s7 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s7->take_shared_method = true;
auto s7_id = ipm->template add_subscription<MessageT>(s7);
(void)s7_id;
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
auto s8 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s8->take_shared_method = true;
auto s8_id = ipm->template add_subscription<MessageT>(s8);
(void)s8_id;
@@ -688,15 +706,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>();
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s1->take_shared_method = true;
auto s1_id = ipm->template add_subscription<MessageT>(s1);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s2->take_shared_method = false;
auto s2_id = ipm->template add_subscription<MessageT>(s2);
@@ -711,15 +729,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
ipm->remove_subscription(s1_id);
ipm->remove_subscription(s2_id);
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
auto s3 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s3->take_shared_method = false;
auto s3_id = ipm->template add_subscription<MessageT>(s3);
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
auto s4 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s4->take_shared_method = false;
auto s4_id = ipm->template add_subscription<MessageT>(s4);
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
auto s5 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s5->take_shared_method = true;
auto s5_id = ipm->template add_subscription<MessageT>(s5);
@@ -743,19 +761,19 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
ipm->remove_subscription(s4_id);
ipm->remove_subscription(s5_id);
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
auto s6 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s6->take_shared_method = true;
auto s6_id = ipm->template add_subscription<MessageT>(s6);
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
auto s7 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s7->take_shared_method = true;
auto s7_id = ipm->template add_subscription<MessageT>(s7);
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
auto s8 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s8->take_shared_method = false;
auto s8_id = ipm->template add_subscription<MessageT>(s8);
auto s9 = std::make_shared<SubscriptionIntraProcessT>();
auto s9 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s9->take_shared_method = false;
auto s9_id = ipm->template add_subscription<MessageT>(s9);
@@ -781,12 +799,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
ipm->remove_subscription(s8_id);
ipm->remove_subscription(s9_id);
auto s10 = std::make_shared<SubscriptionIntraProcessT>();
auto s10 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s10->take_shared_method = false;
auto s10_id = ipm->template add_subscription<MessageT>(s10);
(void)s10_id;
auto s11 = std::make_shared<SubscriptionIntraProcessT>();
auto s11 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s11->take_shared_method = true;
auto s11_id = ipm->template add_subscription<MessageT>(s11);
(void)s11_id;
@@ -831,10 +849,12 @@ TEST(TestIntraProcessManager, lowest_available_capacity) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).best_effort());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).best_effort());
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
auto s1 =
std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(history_depth).best_effort());
auto s2 =
std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(history_depth).best_effort());
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
@@ -902,7 +922,7 @@ TEST(TestIntraProcessManager, transient_local_invalid_buffer) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).transient_local());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).transient_local());
ASSERT_THROW(
{
@@ -926,14 +946,14 @@ TEST(TestIntraProcessManager, transient_local) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).transient_local());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).transient_local());
auto s1 =
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
auto s2 =
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
auto s3 =
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
auto s1 = std::make_shared<SubscriptionIntraProcessT>(
"topic", rclcpp::QoS(history_depth).transient_local());
auto s2 = std::make_shared<SubscriptionIntraProcessT>(
"topic", rclcpp::QoS(history_depth).transient_local());
auto s3 = std::make_shared<SubscriptionIntraProcessT>(
"topic", rclcpp::QoS(history_depth).transient_local());
s1->take_shared_method = false;
s2->take_shared_method = true;

View File

@@ -61,9 +61,8 @@ public:
return static_cast<T *>(std::malloc(size * sizeof(T)));
}
void deallocate(T * ptr, size_t size)
void deallocate(T * ptr, [[maybe_unused]] size_t size)
{
(void)size;
if (!ptr) {
return;
}

View File

@@ -45,6 +45,11 @@ public:
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
std::vector<std::shared_ptr<rclcpp::TimerBase>> get_timers() const override
{
return {};
}
};
class TestMemoryStrategy : public ::testing::Test

View File

@@ -306,6 +306,39 @@ TEST_F(TestNode, subnode_get_name_and_namespace) {
}, rclcpp::exceptions::NameValidationError);
}
}
TEST_F(TestNode, subnode_parameter_operation) {
auto node = std::make_shared<rclcpp::Node>("my_node", "ns");
auto subnode = node->create_sub_node("sub_ns");
auto value = subnode->declare_parameter("param", 5);
EXPECT_EQ(value, 5);
// node and sub-node shares NodeParametersInterface, so expecting the exception.
EXPECT_THROW(
node->declare_parameter("param", 0),
rclcpp::exceptions::ParameterAlreadyDeclaredException);
rclcpp::Parameter param;
node->get_parameter("param", param);
EXPECT_EQ(param.get_value<int>(), 5);
subnode->get_parameter("param", param);
EXPECT_EQ(param.get_value<int>(), 5);
int param_int;
node->get_parameter("param", param_int);
EXPECT_EQ(param_int, 5);
subnode->get_parameter("param", param_int);
EXPECT_EQ(param_int, 5);
EXPECT_EQ(node->get_parameter_or("param", 333), 5);
EXPECT_EQ(subnode->get_parameter_or("param", 666), 5);
node->get_parameter_or("param", param_int, 333);
EXPECT_EQ(param_int, 5);
subnode->get_parameter_or("param", param_int, 666);
EXPECT_EQ(param_int, 5);
}
/*
Testing node construction and destruction.
*/
@@ -1332,6 +1365,203 @@ TEST_F(TestNode, set_parameter_undeclared_parameters_not_allowed) {
node->declare_parameter(name, 42, descriptor),
rclcpp::exceptions::InvalidParameterValueException);
}
{
// setting an array parameter with integer range descriptor
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 10;
integer_range.to_value = 18;
integer_range.step = 2;
node->declare_parameter(name, std::vector<int64_t>{10, 12, 14, 16, 18}, descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER_ARRAY);
EXPECT_EQ(value.get_value<std::vector<int64_t>>(), std::vector<int64_t>({10, 12, 14, 16, 18}));
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({12, 14, 10}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({12, 14, 10}));
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({10, 10, 10}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({10, 10, 10}));
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({18}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({15}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({20}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({8}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({12, 8, 18}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18}));
}
{
// setting an array parameter with integer range descriptor, from_value > to_value
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 20;
integer_range.to_value = 18;
integer_range.step = 1;
node->declare_parameter(name, std::vector<int64_t>({18, 20}), descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER_ARRAY);
EXPECT_EQ(value.get_value<std::vector<int64_t>>(), std::vector<int64_t>({18, 20}));
EXPECT_TRUE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({20, 18}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({20, 18}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({18, 19, 20}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({20, 18}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({10}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({20, 18}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({25}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({20, 18}));
}
{
// setting an array parameter with integer range descriptor, from_value = to_value
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 18;
integer_range.to_value = 18;
integer_range.step = 1;
node->declare_parameter(name, std::vector<int64_t>({18}), descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER_ARRAY);
EXPECT_EQ(value.get_value<std::vector<int64_t>>(), std::vector<int64_t>({18}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({17}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({19}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18}));
}
{
// setting an array parameter with integer range descriptor,
// step > distance(from_value, to_value)
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 18;
integer_range.to_value = 25;
integer_range.step = 10;
node->declare_parameter(name, std::vector<int64_t>({18, 25}), descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER_ARRAY);
EXPECT_EQ(value.get_value<std::vector<int64_t>>(), std::vector<int64_t>({18, 25}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({17}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18, 25}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({19}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18, 25}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({28}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18, 25}));
}
{
// setting an array parameter with integer range descriptor, distance not multiple of the step.
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 18;
integer_range.to_value = 28;
integer_range.step = 7;
node->declare_parameter(name, std::vector<int64_t>({18, 25, 28}), descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER_ARRAY);
EXPECT_EQ(value.get_value<std::vector<int64_t>>(), std::vector<int64_t>({18, 25, 28}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({17}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18, 25, 28}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({19}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18, 25, 28}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({32}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({18, 25, 28}));
}
{
// setting an array parameter with integer range descriptor, step=0
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 10;
integer_range.to_value = 18;
integer_range.step = 0;
node->declare_parameter(name, std::vector<int64_t>({10, 11, 12, 13, 14, 15, 16, 17, 18}),
descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_INTEGER_ARRAY);
EXPECT_EQ(value.get_value<std::vector<int64_t>>(),
std::vector<int64_t>({10, 11, 12, 13, 14, 15, 16, 17, 18}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({9}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({10, 11, 12, 13, 14, 15, 16, 17, 18}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<int64_t>({19}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<int64_t>>(),
std::vector<int64_t>({10, 11, 12, 13, 14, 15, 16, 17, 18}));
}
{
// setting an array parameter with integer range descriptor and wrong default value will throw
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.integer_range.resize(1);
auto & integer_range = descriptor.integer_range.at(0);
integer_range.from_value = 10;
integer_range.to_value = 18;
integer_range.step = 2;
ASSERT_THROW(
node->declare_parameter(name, std::vector<int64_t>({10, 11, 12, 13, 14, 15, 16, 17, 18}),
descriptor),
rclcpp::exceptions::InvalidParameterValueException);
}
{
// setting a parameter with floating point range descriptor
auto name = "parameter"_unq;
@@ -1502,6 +1732,201 @@ TEST_F(TestNode, set_parameter_undeclared_parameters_not_allowed) {
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name, 9.999)).successful);
EXPECT_EQ(node->get_parameter(name).get_value<double>(), 11.0);
}
{
// setting an array parameter with floating point range descriptor
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 11.0;
floating_point_range.step = 0.2;
node->declare_parameter(name, std::vector<double>({10.0, 10.4, 11.0, 10.8, 10.2, 10.6}),
descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE_ARRAY);
EXPECT_EQ(value.get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.4, 11.0, 10.8, 10.2, 10.6}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({10.3}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.4, 11.0, 10.8, 10.2, 10.6}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({12.0}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.4, 11.0, 10.8, 10.2, 10.6}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({9.4}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.4, 11.0, 10.8, 10.2, 10.6}));
}
{
// setting an array parameter with floating point range descriptor, negative step
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 11.0;
floating_point_range.step = -0.2;
node->declare_parameter(name, std::vector<double>({10.0, 10.2, 10.4, 10.6, 10.8, 11.0}),
descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE_ARRAY);
EXPECT_EQ(value.get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.2, 10.4, 10.6, 10.8, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({10.3}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.2, 10.4, 10.6, 10.8, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({12.0}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.2, 10.4, 10.6, 10.8, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({9.4}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.2, 10.4, 10.6, 10.8, 11.0}));
}
{
// setting an array parameter with floating point range descriptor, from_value > to_value
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 11.0;
floating_point_range.to_value = 10.0;
floating_point_range.step = 0.2;
node->declare_parameter(name, std::vector<double>({10.0, 11.0}), descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE_ARRAY);
EXPECT_EQ(value.get_value<std::vector<double>>(), std::vector<double>({10.0, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({10.2}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({12.0}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({9.4}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 11.0}));
}
{
// setting an array parameter with floating point range descriptor, from_value = to_value
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 10.0;
floating_point_range.step = 0.2;
node->declare_parameter(name, std::vector<double>({10.0}), descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE_ARRAY);
EXPECT_EQ(value.get_value<std::vector<double>>(), std::vector<double>({10.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({11.2}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({12.0}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({9.4}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0}));
}
{
// setting an array parameter with floating point range descriptor, step > distance
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 11.0;
floating_point_range.step = 2.2;
node->declare_parameter(name, std::vector<double>({10.0, 11.0}), descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE_ARRAY);
EXPECT_EQ(value.get_value<std::vector<double>>(), std::vector<double>({10.0, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({12.2}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({7.8}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 11.0}));
}
{
// setting an array parameter with floating point range descriptor,
// distance not multiple of the step.
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 11.0;
floating_point_range.step = 0.7;
node->declare_parameter(name, std::vector<double>({10.0, 10.7, 11.0}), descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE_ARRAY);
EXPECT_EQ(value.get_value<std::vector<double>>(), std::vector<double>({10.0, 10.7, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({12.2}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.7, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({11.4}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.7, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({9.3}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.7, 11.0}));
}
{
// setting an array parameter with floating point range descriptor, step=0
auto name = "parameter"_unq;
rcl_interfaces::msg::ParameterDescriptor descriptor;
descriptor.floating_point_range.resize(1);
auto & floating_point_range = descriptor.floating_point_range.at(0);
floating_point_range.from_value = 10.0;
floating_point_range.to_value = 11.0;
floating_point_range.step = 0.0;
node->declare_parameter(name, std::vector<double>({10.0, 10.0001, 10.5479051, 11.0}),
descriptor);
EXPECT_TRUE(node->has_parameter(name));
auto value = node->get_parameter(name);
EXPECT_EQ(value.get_type(), rclcpp::PARAMETER_DOUBLE_ARRAY);
EXPECT_EQ(value.get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.0001, 10.5479051, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({11.001}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.0001, 10.5479051, 11.0}));
EXPECT_FALSE(node->set_parameter(rclcpp::Parameter(name,
std::vector<double>({9.999}))).successful);
EXPECT_EQ(node->get_parameter(name).get_value<std::vector<double>>(),
std::vector<double>({10.0, 10.0001, 10.5479051, 11.0}));
}
{
// setting a parameter with a different type is still possible
// when having a descriptor specifying a type (type is a status, not a constraint).

View File

@@ -200,6 +200,57 @@ TEST(TestNodeOptions, copy) {
rcl_arguments_get_count_unparsed(&other_rcl_options->arguments),
rcl_arguments_get_count_unparsed(&rcl_options->arguments));
}
{
// The following scope test is missing:
// "arguments" because it is already tested in the above scopes
// "parameter_event_publisher_options" because it can not be directly compared with EXPECT_EQ
// "allocator" because it can not be directly compared with EXPECT_EQ
// We separate attribute modification from variable initialisation (copy assignment operator)
// to be sure the "non_default_options"'s properties are correctly set before testing the
// assignment operator.
auto non_default_options = rclcpp::NodeOptions();
non_default_options
.parameter_overrides({rclcpp::Parameter("foo", 0), rclcpp::Parameter("bar", "1")})
.use_global_arguments(false)
.enable_rosout(false)
.use_intra_process_comms(true)
.enable_topic_statistics(true)
.start_parameter_services(false)
.enable_logger_service(true)
.start_parameter_event_publisher(false)
.clock_type(RCL_SYSTEM_TIME)
.clock_qos(rclcpp::SensorDataQoS())
.use_clock_thread(false)
.parameter_event_qos(rclcpp::ClockQoS())
.rosout_qos(rclcpp::ParameterEventsQoS())
.allow_undeclared_parameters(true)
.automatically_declare_parameters_from_overrides(true);
auto copied_options = non_default_options;
EXPECT_EQ(non_default_options.parameter_overrides(), copied_options.parameter_overrides());
EXPECT_EQ(non_default_options.use_global_arguments(), copied_options.use_global_arguments());
EXPECT_EQ(non_default_options.enable_rosout(), copied_options.enable_rosout());
EXPECT_EQ(non_default_options.use_intra_process_comms(),
copied_options.use_intra_process_comms());
EXPECT_EQ(non_default_options.enable_topic_statistics(),
copied_options.enable_topic_statistics());
EXPECT_EQ(non_default_options.start_parameter_services(),
copied_options.start_parameter_services());
EXPECT_EQ(non_default_options.enable_logger_service(), copied_options.enable_logger_service());
EXPECT_EQ(non_default_options.start_parameter_event_publisher(),
copied_options.start_parameter_event_publisher());
EXPECT_EQ(non_default_options.clock_type(), copied_options.clock_type());
EXPECT_EQ(non_default_options.clock_qos(), copied_options.clock_qos());
EXPECT_EQ(non_default_options.use_clock_thread(), copied_options.use_clock_thread());
EXPECT_EQ(non_default_options.parameter_event_qos(), copied_options.parameter_event_qos());
EXPECT_EQ(non_default_options.rosout_qos(), copied_options.rosout_qos());
EXPECT_EQ(non_default_options.allow_undeclared_parameters(),
copied_options.allow_undeclared_parameters());
EXPECT_EQ(non_default_options.automatically_declare_parameters_from_overrides(),
copied_options.automatically_declare_parameters_from_overrides());
}
}
TEST(TestNodeOptions, append_parameter_override) {

View File

@@ -34,9 +34,9 @@ using namespace std::chrono_literals;
class TestParameterClient : public ::testing::Test
{
public:
void OnMessage(rcl_interfaces::msg::ParameterEvent::ConstSharedPtr event)
void OnMessage([[maybe_unused]] rcl_interfaces::msg::ParameterEvent::ConstSharedPtr event)
{
(void)event;
// This function is intentionally left empty.
}
protected:

View File

@@ -72,6 +72,13 @@ protected:
/*
Testing filters.
*/
TEST_F(TestParameterEventFilter, invalide_arguments) {
EXPECT_THROW(
rclcpp::ParameterEventsFilter(nullptr, {"new"}, {nt}),
std::invalid_argument
);
}
TEST_F(TestParameterEventFilter, full_by_type) {
auto res = rclcpp::ParameterEventsFilter(
full,

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <future>
#include <chrono>
#include <memory>
#include <string>
@@ -187,6 +188,21 @@ TEST_F(TestPublisher, various_creation_signatures) {
}
}
/*
Testing publisher with intraprocess enabled and SystemDefaultQoS
*/
TEST_F(TestPublisher, test_publisher_with_system_default_qos) {
initialize(rclcpp::NodeOptions().use_intra_process_comms(false));
// explicitly enable intra-process comm with publisher option
auto options = rclcpp::PublisherOptions();
options.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
using test_msgs::msg::Empty;
ASSERT_NO_THROW(
{
auto publisher = node->create_publisher<Empty>("topic", rclcpp::SystemDefaultsQoS());
});
}
/*
Testing publisher with intraprocess enabled and invalid QoS
*/
@@ -432,11 +448,10 @@ TEST_F(TestPublisher, intra_process_publish_failures) {
publisher->get_publisher_handle().get(), msg.get()));
}
}
RCLCPP_EXPECT_THROW_EQ(
node->create_publisher<test_msgs::msg::Empty>(
"topic", rclcpp::QoS(0), options),
std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value"));
// a zero depth with KEEP_LAST doesn't make sense,
// this will be interpreted as SystemDefaultQoS by rclcpp.
EXPECT_NO_THROW(
node->create_publisher<test_msgs::msg::Empty>("topic", rclcpp::QoS(0), options));
}
TEST_F(TestPublisher, inter_process_publish_failures) {
@@ -711,11 +726,7 @@ TEST_F(TestPublisher, intra_process_transient_local) {
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
struct IntraProcessCallback
{
void callback_fun(size_t s)
{
(void) s;
called = true;
}
void callback_fun(size_t) {called = true;}
bool called = false;
};
rclcpp::SubscriptionOptions sub_options_ipm_disabled;
@@ -772,3 +783,44 @@ TEST_F(TestPublisher, intra_process_transient_local) {
EXPECT_FALSE(callback3.called);
EXPECT_FALSE(callback4.called);
}
TEST_F(TestPublisher, intra_process_inter_process_mix_transient_local) {
constexpr auto history_depth = 10u;
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> pub_options_ipm_enabled;
pub_options_ipm_enabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
auto pub_ipm_enabled_transient_local_enabled = node->create_publisher<test_msgs::msg::Empty>(
"topic1",
rclcpp::QoS(rclcpp::KeepLast(history_depth)).transient_local(), pub_options_ipm_enabled);
test_msgs::msg::Empty msg;
pub_ipm_enabled_transient_local_enabled->publish(msg);
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
struct SubscriptionCallback
{
void callback_fun(size_t) {called.set_value();}
std::promise<void> called;
};
rclcpp::SubscriptionOptions sub_options_ipm_disabled;
sub_options_ipm_disabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
SubscriptionCallback intra_callback, inter_callback;
std::future<void> intra_callback_future = intra_callback.called.get_future(),
inter_callback_future = inter_callback.called.get_future();
auto sub_ipm_disabled_transient_local_enabled = node->create_subscription<test_msgs::msg::Empty>(
"topic1",
rclcpp::QoS(rclcpp::KeepLast(history_depth)).transient_local(),
do_nothing, sub_options_ipm_disabled);
sub_ipm_disabled_transient_local_enabled->set_on_new_intra_process_message_callback(
std::bind(&SubscriptionCallback::callback_fun, &intra_callback, std::placeholders::_1));
sub_ipm_disabled_transient_local_enabled->set_on_new_message_callback(
std::bind(&SubscriptionCallback::callback_fun, &inter_callback, std::placeholders::_1));
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node);
EXPECT_EQ(executor.spin_until_future_complete(inter_callback_future,
std::chrono::milliseconds(100)), rclcpp::FutureReturnCode::SUCCESS);
EXPECT_EQ(executor.spin_until_future_complete(intra_callback_future,
std::chrono::milliseconds(100)), rclcpp::FutureReturnCode::TIMEOUT);
}

View File

@@ -122,9 +122,9 @@ public:
}
protected:
static void OnMessage(test_msgs::msg::Empty::ConstSharedPtr msg)
static void OnMessage([[maybe_unused]] test_msgs::msg::Empty::ConstSharedPtr msg)
{
(void)msg;
// This function is intentionally left empty.
}
std::chrono::milliseconds offset{2000};

View File

@@ -88,21 +88,18 @@ struct TypeAdapter<int, rclcpp::msg::String>
static void
convert_to_ros_message(
const custom_type & source,
ros_message_type & destination)
[[maybe_unused]] const custom_type & source,
[[maybe_unused]] ros_message_type & destination)
{
(void) source;
(void) destination;
throw std::runtime_error("This should not happen");
}
static void
convert_to_custom(
const ros_message_type & source,
custom_type & destination)
[[maybe_unused]] const ros_message_type & source,
[[maybe_unused]] custom_type & destination)
{
(void) source;
(void) destination;
// This function is intentionally left empty.
}
};
@@ -165,9 +162,9 @@ TEST_F(TestPublisher, conversion_exception_is_passed_up) {
options.use_intra_process_comms(is_intra_process);
auto callback =
[](const rclcpp::msg::String::ConstSharedPtr msg) -> void
[]([[maybe_unused]] const rclcpp::msg::String::ConstSharedPtr msg) -> void
{
(void)msg;
// This function is intentionally left empty.
};
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns", options);

View File

@@ -184,7 +184,7 @@ TEST(TestQoS, DerivedTypes) {
EXPECT_EQ(rmw_qos_profile_parameter_events, parameter_events_qos.get_rmw_qos_profile());
rclcpp::RosoutQoS rosout_qos;
EXPECT_EQ(rcl_qos_profile_rosout_default, rosout_qos.get_rmw_qos_profile());
EXPECT_EQ(rmw_qos_profile_rosout_default, rosout_qos.get_rmw_qos_profile());
rclcpp::SystemDefaultsQoS system_default_qos;
const rclcpp::KeepLast expected_initialization(RMW_QOS_POLICY_DEPTH_SYSTEM_DEFAULT);

View File

@@ -73,7 +73,9 @@ TEST_F(TestQosEvent, test_publisher_constructor)
auto publisher = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, options);
if (rmw_implementation_str != "rmw_zenoh_cpp") {
if (rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED) &&
rmw_event_type_is_supported(RMW_EVENT_LIVELINESS_LOST))
{
// options arg with one of the callbacks
options.event_callbacks.deadline_callback =
[node = node.get()](rclcpp::QOSDeadlineOfferedInfo & event) {
@@ -121,7 +123,9 @@ TEST_F(TestQosEvent, test_subscription_constructor)
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, options);
if (rmw_implementation_str != "rmw_zenoh_cpp") {
if (rmw_event_type_is_supported(RMW_EVENT_REQUESTED_DEADLINE_MISSED) &&
rmw_event_type_is_supported(RMW_EVENT_LIVELINESS_CHANGED))
{
// options arg with one of the callbacks
options.event_callbacks.deadline_callback =
[node = node.get()](rclcpp::QOSDeadlineRequestedInfo & event) {
@@ -211,10 +215,9 @@ TEST_F(TestQosEvent, test_default_incompatible_qos_callbacks)
const auto timeout = std::chrono::seconds(10);
ex.spin_until_future_complete(log_msgs_future, timeout);
if (rmw_implementation_str == "rmw_zenoh_cpp") {
EXPECT_EQ(rclcpp::QoSCompatibility::Ok,
qos_check_compatible(qos_profile_publisher, qos_profile_subscription).compatibility);
} else {
if (qos_check_compatible(qos_profile_publisher,
qos_profile_subscription).compatibility != rclcpp::QoSCompatibility::Ok)
{
EXPECT_EQ(
"New subscription discovered on topic '/ns/test_topic', requesting incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
@@ -235,7 +238,8 @@ TEST_F(TestQosEvent, construct_destruct_rcl_error) {
// This callback requires some type of parameter, but it could be anything
auto callback = [](int) {};
const rcl_publisher_event_type_t event_type = rmw_implementation_str == "rmw_zenoh_cpp" ?
const rcl_publisher_event_type_t event_type =
!rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED) ?
RCL_PUBLISHER_MATCHED : RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
{
@@ -273,9 +277,10 @@ TEST_F(TestQosEvent, construct_destruct_rcl_error) {
}
TEST_F(TestQosEvent, execute) {
if (rmw_implementation_str == "rmw_zenoh_cpp") {
if (!rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED)) {
GTEST_SKIP();
}
auto publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
auto rcl_handle = publisher->get_publisher_handle();
@@ -308,9 +313,11 @@ TEST_F(TestQosEvent, add_to_wait_set) {
// This callback requires some type of parameter, but it could be anything
auto callback = [](int) {};
const rcl_publisher_event_type_t event_type = rmw_implementation_str == "rmw_zenoh_cpp" ?
const rcl_publisher_event_type_t event_type =
!rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED) ?
RCL_PUBLISHER_MATCHED : RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
callback, rcl_publisher_event_init, rcl_handle, event_type);
EXPECT_EQ(1u, handler.get_number_of_ready_events());
@@ -331,7 +338,9 @@ TEST_F(TestQosEvent, add_to_wait_set) {
TEST_F(TestQosEvent, test_on_new_event_callback)
{
if (rmw_implementation_str == "rmw_zenoh_cpp") {
if (!rmw_event_type_is_supported(RMW_EVENT_REQUESTED_DEADLINE_MISSED) ||
!rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED))
{
GTEST_SKIP();
}
@@ -380,7 +389,9 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
auto sub = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
auto dummy_cb = [](size_t count_events) {(void)count_events;};
if (rmw_implementation_str != "rmw_zenoh_cpp") {
if (rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED) &&
rmw_event_type_is_supported(RMW_EVENT_LIVELINESS_LOST))
{
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
@@ -405,7 +416,9 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
EXPECT_NO_THROW(
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_MATCHED));
if (rmw_implementation_str == "rmw_zenoh_cpp") {
if (rmw_event_type_is_supported(RMW_EVENT_REQUESTED_DEADLINE_MISSED) &&
rmw_event_type_is_supported(RMW_EVENT_LIVELINESS_CHANGED))
{
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
@@ -430,7 +443,9 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_MATCHED));
if (rmw_implementation_str != "rmw_zenoh_cpp") {
if (rmw_event_type_is_supported(RMW_EVENT_REQUESTED_DEADLINE_MISSED) &&
rmw_event_type_is_supported(RMW_EVENT_OFFERED_DEADLINE_MISSED))
{
std::function<void(size_t)> invalid_cb;
rclcpp::SubscriptionOptions sub_options;

View File

@@ -22,7 +22,7 @@
#include "rclcpp/experimental/buffers/ring_buffer_implementation.hpp"
/*
* Construtctor
* Constructor
*/
TEST(TestRingBufferImplementation, constructor) {
// Cannot create a buffer of size zero.
@@ -139,3 +139,28 @@ TEST(TestRingBufferImplementation, basic_usage_unique_ptr) {
EXPECT_EQ(false, rb.has_data());
EXPECT_EQ(false, rb.is_full());
}
TEST(TestRingBufferImplementation, test_buffer_clear) {
rclcpp::experimental::buffers::RingBufferImplementation<char> rb(2);
rb.enqueue('a');
rb.enqueue('b');
EXPECT_EQ(true, rb.has_data());
EXPECT_EQ(true, rb.is_full());
const auto all_data_vec = rb.get_all_data();
EXPECT_EQ(2u, all_data_vec.capacity());
EXPECT_EQ(2u, all_data_vec.size());
rb.clear();
EXPECT_EQ(false, rb.has_data());
EXPECT_EQ(false, rb.is_full());
const auto all_data_vec_empty = rb.get_all_data();
EXPECT_EQ(0u, all_data_vec_empty.capacity());
EXPECT_EQ(0u, all_data_vec_empty.size());
rb.enqueue('c');
rb.enqueue('d');
const auto c = rb.dequeue();
const auto d = rb.dequeue();
EXPECT_EQ('c', c);
EXPECT_EQ('d', d);
}

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