Compare commits

...

62 Commits

Author SHA1 Message Date
methylDragon
dd5c60827d Implement dynamic subscription and C++ wrapper classes
Signed-off-by: methylDragon <methylDragon@gmail.com>
2024-12-30 20:25:38 -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
Chris Lalancette
16290fb352 29.0.0 2024-10-03 16:06:52 +00:00
Chris Lalancette
e133cc65f6 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-10-03 16:06:42 +00:00
Chris Lalancette
50a1e50133 Increase the timeout for the cppcheck on rclcpp_action. (#2640)
The default is 300 seconds, but on Windows this is taking
between 250 and 300 seconds (I'm seeing it timeout sometimes).
Up the timeout to 600 seconds, which should be more than enough.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-10-03 10:33:15 -04:00
Alejandro Hernández Cordero
1a0092a65b Fixed test qos rmw zenoh (#2639)
* Fixed test qos rmw zenoh

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

* Update rclcpp/test/rclcpp/test_qos.cpp

Co-authored-by: Yadu <yadunund@openrobotics.org>
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>

---------

Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
Co-authored-by: Yadu <yadunund@openrobotics.org>
2024-10-02 10:59:48 -07:00
Tomoya Fujita
2813045a96 verify client gid uniqueness for a single service event. (#2636)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-10-01 10:15:58 -04:00
Alejandro Hernández Cordero
51dfdc3708 Skip some tests in test_qos_event and run others with event types supported by rmw_zenoh (#2626)
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
Signed-off-by: Yadunund <yadunund@intrinsic.ai>
Co-authored-by: Yadunund <yadunund@intrinsic.ai>
2024-09-30 10:03:51 +02:00
Alejandro Hernández Cordero
1f408e3b19 Shutdown the context before context's destructor is invoked in tests (#2633)
* zenoh: Shutdown the node properly in component tests

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

* Call rclcpp::shutdown when tearing down tests in rclcpp

Signed-off-by: Yadunund <yadunund@intrinsic.ai>

* Call rclcpp::shutdown when tearing down tests in rclcpp_lifecycle

Signed-off-by: Yadunund <yadunund@intrinsic.ai>

* Ensure context is initialized for all tests in test_publisher

Signed-off-by: Yadunund <yadunund@intrinsic.ai>

* Added feedback

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

* make linters happy

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

---------

Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
Signed-off-by: Yadunund <yadunund@intrinsic.ai>
Co-authored-by: Yadunund <yadunund@intrinsic.ai>
2024-09-24 09:36:54 -07:00
Alejandro Hernández Cordero
63105cd8a6 Skip rmw zenoh content filtering tests (#2627)
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2024-09-23 11:19:04 -07:00
Alberto Soragna
a78d0cbd33 add smart pointer macros definitions to action server and client base classes (#2631)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2024-09-16 18:42:47 +02:00
Barry Xu
918363d081 Use InvalidServiceTypeError for unavailable service type in GenericClient (#2629)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2024-09-16 08:53:48 -07:00
Tomoya Fujita
97c386ce40 LifecycleNode bugfix and add test cases (#2562)
* LifecycleNode base class resource needs to be reset via dtor.

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

* add debug notice that prints LifecycleNode is not shutdown in dtor.

  Currently it is user application responsibility to manage the all state control.
  See more details for https://github.com/ros2/rclcpp/issues/2520.

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

* add test cases to call shutdown from each primary state.

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

* address review comments.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-09-12 09:49:53 -07:00
Barry Xu
1a3dfaf45c Implement generic service (#2617)
* Implement generic service

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

* Add the required header files

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

* Fix compiling errors on Windows

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

* Fix compiling errors on Windows

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

* Fix compiling errors on Windows

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

---------

Signed-off-by: Barry Xu <barry.xu@sony.com>
2024-09-12 09:04:59 -07:00
Alberto Soragna
f7056c0d86 fix events-executor warm-up bug and add unit-tests (#2591)
* add unit-test to verify that spin-all doesn't need warm-up

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

* improve comment and add callback group test

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

* move executor tests to a new file

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

* do not require warm up iteration with events executor spin_some

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

* add spin_some tests and cleanup

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

* add missing include directives

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

---------

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
2024-09-11 09:08:37 +02:00
Alberto Soragna
2f71d6e249 remove unnecessary gtest-skip in test_executors (#2600)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2024-09-06 14:18:03 +02:00
Barry Xu
e846f56224 Correct node name in service test code (#2615)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2024-09-02 09:42:48 +02:00
Kang, Hsin-Yi
ee94bc63e4 Minor naming fixes for ParameterValue to_string() function (#2609)
More appropriate function argument naming.
Refer to: 
https://github.com/ros2/rclcpp/blob/rolling/rclcpp/src/rclcpp/parameter_value.cpp#L83

Signed-off-by: Kang, Hsin-Yi <f039281310@yahoo.com.tw>
2024-08-22 15:46:50 +02:00
Alejandro Hernández Cordero
b7c789328a Removed clang warnings (#2605)
Signed-off-by: Alejandro Hernández Cordero <ahcorde@gmail.com>
2024-08-22 08:45:51 +02:00
Chris Lalancette
4b1c125cac Fix a couple of issues in the documentation. (#2608)
These should make it render more nicely.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-08-21 17:58:34 -04:00
Alberto Soragna
dfaaf4739a deprecate the static single threaded executor (#2598)
* deprecate the static single threded executor

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

* suppress deprecation warning for static executor and remove benchmark tests

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

* fix uncrustify linter errors

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

* fix windows deprecation warnings

i created an alias to give the deprecated executor a new name; this works when the class is directly used but it doesn't work in combination with templates (like std::make_shared<DeprecatedAlias>) because the compiler needs to resolved the type behind the alias triggering the warning

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

* update test_reinitialized_timers.cpp to use the executor test utilities

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

* do not use executor pointer

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

---------

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
Co-authored-by: Alberto Soragna <asoragna@irobot.com>
2024-08-19 20:13:21 -07:00
Christophe Bedard
e6b6faf152 Fix name of ParameterEventHandler class in doc (#2604)
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-08-18 09:29:52 -07:00
Christophe Bedard
f961ca7855 Fix typo in rclcpp_components benchmark_components (#2602)
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-08-12 09:39:49 -07:00
Tomoya Fujita
6da8363582 subscriber_statistics_collectors_ should be protected by mutex. (#2592)
* subscriber_statistics_collectors_ should be protected by mutex.

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

* reduce mutex lock scope.

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

---------

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2024-08-08 17:05:34 -07:00
Christophe Bedard
ab7cf878c2 Properly test get_service_names_and_types_by_node in rclcpp_lifecycle (#2599)
The current
`TestLifecycleServiceClient.get_service_names_and_types_by_node` test
was using `LifecycleServiceClient`, which is just a normal
`rclcpp::Node` with some `rclcpp::Client`s, to test
`NodeGraph::get_service_names_and_types_by_node()`. However,
`NodeGraph::get_service_names_and_types_by_node()` is for service
servers, not clients. The test just ended up checking the built-in
services of an `rclcpp::Node`, since it wasn't actually checking the
names of the services, and not checking the clients of the
`LifecycleServiceClient` or the built-in services of a
`rclcpp_lifecycle::LifecycleNode`. I believe this was probably not the
intention, since this is an `rclcpp_lifecycle` test.

Instead, use an actual `rclcpp_lifecycle::LifecycleNode` and check its
service servers by calling
`NodeGraph::get_service_names_and_types_by_node()` through
`LifecycleNode::get_service_names_and_types_by_node()`.

Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-08-08 13:01:15 -07:00
Alexis Pojomovsky
496c45549b Fix bug in timers lifecycle for events executor (#2586)
* Remove expired timers before updating the collection

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Add regression test for reinitialized timers bug

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Add missing includes

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Relocate test under the executors directory

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Extend test to run with all supported executors

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Adjust comment in fix to make it more generic

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Apply ament clang format to test

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

* Fix uncrustify findings

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>

---------

Signed-off-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>
Co-authored-by: Alexis Pojomovsky <apojomovsky@ekumenlabs.com>
2024-08-07 18:15:08 -07:00
Alberto Soragna
2739327ee9 fix rclcpp/test/rclcpp/CMakeLists.txt to check for the correct targets existance (#2596)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2024-08-05 12:07:07 -07:00
Christophe Bedard
4e4f0cf43b Shut down context during init if logging config fails (#2594)
We already do this clean-up if some other tasks below fail.

Before:

  [ RUN      ] TestUtilities.test_context_init_shutdown_fails
  [ERROR] [1722555370.075637014] [rclcpp]: rcl context unexpectedly not shutdown during cleanup
  [WARN] [1722555370.077175569] [rclcpp]: logging was initialized more than once
  [       OK ] TestUtilities.test_context_init_shutdown_fails (3 ms)

After:

  [ RUN      ] TestUtilities.test_context_init_shutdown_fails
  [WARN] [1722555108.693207861] [rclcpp]: logging was initialized more than once
  [       OK ] TestUtilities.test_context_init_shutdown_fails (3 ms)

Also, remove an unnecessary line in `test_utilities`, and expect context
to not be valid if init fails.

Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2024-08-05 08:12:33 -07:00
Chris Lalancette
c46896731c Make more of the Waitable API abstract (#2593)
* Make Waitable::{is_ready,add_to_wait_set,execute} abstract APIs.

I initially started looking at this because clang was complaining
that "all paths through this function will call itself".  And it
is correct; if an implementation does not override these methods,
and they are every called, they will go into an infinite loop
calling themselves.

However, while looking at it it seemed to me that these were really
part of the API that a concrete implementation of Waitable needed
to implement.  It is fine if an implementation doesn't want to do
anything (like the tests), but all of the "real" users in the codebase
override these.

Thus, remove the implementations here and make these pure virtual
functions that all subclasses must implement.

* Remove some more "empty" implementations.

In particular, these are implementations in the Waitable
class that only throw exceptions.  Rather than do this,
make these APIs pure virtual so all downstream classes
have to implement them.  All of the current users (except
for tests) do anyway, and it makes the API much cleaner.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-08-02 08:01:25 -04:00
Chris Lalancette
45adf6565f 28.3.3 2024-07-29 14:52:31 +00:00
Chris Lalancette
6f5c6f45d7 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-07-29 14:52:23 +00:00
Chris Lalancette
9b1e6c9d52 Only compile the tests once. (#2590)
Even when we want to run them on multiple RMWs, we can
do that by compiling once, then setting the environment
variable appropriately.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-07-26 12:37:34 -04:00
117 changed files with 6901 additions and 842 deletions

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,67 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
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>`_)
* verify client gid uniqueness for a single service event. (`#2636 <https://github.com/ros2/rclcpp/issues/2636>`_)
* Skip some tests in test_qos_event and run others with event types supported by rmw_zenoh (`#2626 <https://github.com/ros2/rclcpp/issues/2626>`_)
* Shutdown the context before context's destructor is invoked in tests (`#2633 <https://github.com/ros2/rclcpp/issues/2633>`_)
* Skip rmw zenoh content filtering tests (`#2627 <https://github.com/ros2/rclcpp/issues/2627>`_)
* Use InvalidServiceTypeError for unavailable service type in GenericClient (`#2629 <https://github.com/ros2/rclcpp/issues/2629>`_)
* Implement generic service (`#2617 <https://github.com/ros2/rclcpp/issues/2617>`_)
* fix events-executor warm-up bug and add unit-tests (`#2591 <https://github.com/ros2/rclcpp/issues/2591>`_)
* remove unnecessary gtest-skip in test_executors (`#2600 <https://github.com/ros2/rclcpp/issues/2600>`_)
* Correct node name in service test code (`#2615 <https://github.com/ros2/rclcpp/issues/2615>`_)
* Minor naming fixes for ParameterValue to_string() function (`#2609 <https://github.com/ros2/rclcpp/issues/2609>`_)
* Removed clang warnings (`#2605 <https://github.com/ros2/rclcpp/issues/2605>`_)
* Fix a couple of issues in the documentation. (`#2608 <https://github.com/ros2/rclcpp/issues/2608>`_)
* deprecate the static single threaded executor (`#2598 <https://github.com/ros2/rclcpp/issues/2598>`_)
* Fix name of ParameterEventHandler class in doc (`#2604 <https://github.com/ros2/rclcpp/issues/2604>`_)
* subscriber_statistics_collectors\_ should be protected by mutex. (`#2592 <https://github.com/ros2/rclcpp/issues/2592>`_)
* Fix bug in timers lifecycle for events executor (`#2586 <https://github.com/ros2/rclcpp/issues/2586>`_)
* fix rclcpp/test/rclcpp/CMakeLists.txt to check for the correct targets existance (`#2596 <https://github.com/ros2/rclcpp/issues/2596>`_)
* Shut down context during init if logging config fails (`#2594 <https://github.com/ros2/rclcpp/issues/2594>`_)
* Make more of the Waitable API abstract (`#2593 <https://github.com/ros2/rclcpp/issues/2593>`_)
* Contributors: Alberto Soragna, Alejandro Hernández Cordero, Alexis Pojomovsky, Barry Xu, Chris Lalancette, Christophe Bedard, Kang, Hsin-Yi, Tomoya Fujita
28.3.3 (2024-07-29)
-------------------
* Only compile the tests once. (`#2590 <https://github.com/ros2/rclcpp/issues/2590>`_)
* Contributors: Chris Lalancette
28.3.2 (2024-07-24)
-------------------
* Updated rcpputils path API (`#2579 <https://github.com/ros2/rclcpp/issues/2579>`_)

View File

@@ -77,6 +77,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/future_return_code.cpp
src/rclcpp/generic_client.cpp
src/rclcpp/generic_publisher.cpp
src/rclcpp/generic_service.cpp
src/rclcpp/generic_subscription.cpp
src/rclcpp/graph_listener.cpp
src/rclcpp/guard_condition.cpp
@@ -112,6 +113,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/qos.cpp
src/rclcpp/event_handler.cpp
src/rclcpp/qos_overriding_options.cpp
src/rclcpp/dynamic_subscription.cpp
src/rclcpp/rate.cpp
src/rclcpp/serialization.cpp
src/rclcpp/serialized_message.cpp

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

@@ -0,0 +1,102 @@
// Copyright 2024 Sony Group Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__CREATE_GENERIC_SERVICE_HPP_
#define RCLCPP__CREATE_GENERIC_SERVICE_HPP_
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/generic_service.hpp"
#include "rclcpp/node_interfaces/get_node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/get_node_services_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/rmw.h"
namespace rclcpp
{
/// Create a generic service with a given type.
/**
* \param[in] node_base NodeBaseInterface implementation of the node on which
* to create the generic service.
* \param[in] node_services NodeServicesInterface implementation of the node on
* which to create the service.
* \param[in] service_name The name on which the service is accessible.
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
* \param[in] callback The callback to call when the service gets a request.
* \param[in] qos Quality of service profile for the service.
* \param[in] group Callback group to handle the reply to service calls.
* \return Shared pointer to the created service.
*/
template<typename CallbackT>
typename rclcpp::GenericService::SharedPtr
create_generic_service(
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
const std::string & service_name,
const std::string & service_type,
CallbackT && callback,
const rclcpp::QoS & qos,
rclcpp::CallbackGroup::SharedPtr group)
{
rclcpp::GenericServiceCallback any_service_callback;
any_service_callback.set(std::forward<CallbackT>(callback));
rcl_service_options_t service_options = rcl_service_get_default_options();
service_options.qos = qos.get_rmw_qos_profile();
auto serv = GenericService::make_shared(
node_base->get_shared_rcl_node_handle(),
service_name, service_type, any_service_callback, service_options);
auto serv_base_ptr = std::dynamic_pointer_cast<ServiceBase>(serv);
node_services->add_service(serv_base_ptr, group);
return serv;
}
/// Create a generic service with a given type.
/**
* The NodeT type needs to have NodeBaseInterface implementation and NodeServicesInterface
* implementation of the node which to create the generic service.
*
* \param[in] node The node on which to create the generic service.
* \param[in] service_name The name on which the service is accessible.
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
* \param[in] callback The callback to call when the service gets a request.
* \param[in] qos Quality of service profile for the service.
* \param[in] group Callback group to handle the reply to service calls.
* \return Shared pointer to the created service.
*/
template<typename NodeT, typename CallbackT>
typename rclcpp::GenericService::SharedPtr
create_generic_service(
NodeT node,
const std::string & service_name,
const std::string & service_type,
CallbackT && callback,
const rclcpp::QoS & qos,
rclcpp::CallbackGroup::SharedPtr group)
{
return create_generic_service<CallbackT>(
rclcpp::node_interfaces::get_node_base_interface(node),
rclcpp::node_interfaces::get_node_services_interface(node),
service_name,
service_type,
std::forward<CallbackT>(callback), qos.get_rmw_qos_profile(), group);
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_GENERIC_SERVICE_HPP_

View File

@@ -0,0 +1,175 @@
// Copyright 2022 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_SUBSCRIPTION_HPP_
#define RCLCPP__DYNAMIC_SUBSCRIPTION_HPP_
#include <rosidl_dynamic_typesupport/identifier.h>
#include <functional>
#include <memory>
#include <string>
#include "rcpputils/shared_library.hpp"
#include "rclcpp/callback_group.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type_support.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/serialized_message.hpp"
#include "rclcpp/subscription_base.hpp"
#include "rclcpp/typesupport_helpers.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// %Subscription for messages whose type descriptions are obtained at runtime.
/**
* Since the type is not known at compile time, this is not a template, and the dynamic library
* containing type support information has to be identified and loaded based on the type name.
*
* NOTE(methylDragon): No considerations for intra-process handling are made.
*/
class DynamicSubscription : public rclcpp::SubscriptionBase
{
public:
// cppcheck-suppress unknownMacro
RCLCPP_SMART_PTR_DEFINITIONS(DynamicSubscription)
template<typename AllocatorT = std::allocator<void>>
DynamicSubscription(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::dynamic_typesupport::DynamicMessageTypeSupport::SharedPtr type_support,
const std::string & topic_name,
const rclcpp::QoS & qos,
std::function<void(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr,
const rosidl_runtime_c__type_description__TypeDescription &
)> callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
: SubscriptionBase(
node_base,
type_support->get_const_rosidl_message_type_support(),
topic_name,
options.to_rcl_subscription_options(
qos),
options.event_callbacks,
options.use_default_callbacks,
DeliveredMessageKind::DYNAMIC_MESSAGE),
ts_(type_support),
callback_(callback),
serialization_support_(nullptr),
dynamic_message_(nullptr),
dynamic_message_type_(nullptr)
{
if (!type_support) {
throw std::runtime_error("DynamicMessageTypeSupport cannot be nullptr!");
}
if (type_support->get_const_rosidl_message_type_support().typesupport_identifier !=
rosidl_get_dynamic_typesupport_identifier())
{
throw std::runtime_error(
"DynamicSubscription must use dynamic type introspection type support!");
}
serialization_support_ = type_support->get_shared_dynamic_serialization_support();
dynamic_message_type_ = type_support->get_shared_dynamic_message_type()->clone_shared();
dynamic_message_ = type_support->get_shared_dynamic_message()->clone_shared();
}
// TODO(methylDragon):
/// Deferred type description constructor, only usable if the middleware implementation supports
/// type discovery
RCLCPP_PUBLIC
virtual ~DynamicSubscription() = default;
// Same as create_serialized_message() as the subscription is to serialized_messages only
RCLCPP_PUBLIC
std::shared_ptr<void> create_message() override;
RCLCPP_PUBLIC
std::shared_ptr<rclcpp::SerializedMessage> create_serialized_message() override;
/// Cast the message to a rclcpp::SerializedMessage and call the callback.
RCLCPP_PUBLIC
void handle_message(
std::shared_ptr<void> & message, const rclcpp::MessageInfo & message_info) override;
/// Handle dispatching rclcpp::SerializedMessage to user callback.
RCLCPP_PUBLIC
void
handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
const rclcpp::MessageInfo & message_info) override;
/// This function is currently not implemented.
RCLCPP_PUBLIC
void handle_loaned_message(
void * loaned_message, const rclcpp::MessageInfo & message_info) override;
// Same as return_serialized_message() as the subscription is to serialized_messages only
RCLCPP_PUBLIC
void return_message(std::shared_ptr<void> & message) override;
RCLCPP_PUBLIC
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) override;
// DYNAMIC TYPE ==================================================================================
// TODO(methylDragon): Reorder later
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
get_shared_dynamic_message_type() override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr get_shared_dynamic_message() override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support() override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr create_dynamic_message() override;
RCLCPP_PUBLIC
void return_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) override;
RCLCPP_PUBLIC
void handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info) override;
private:
RCLCPP_DISABLE_COPY(DynamicSubscription)
rclcpp::dynamic_typesupport::DynamicMessageTypeSupport::SharedPtr ts_;
std::function<void(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr,
const rosidl_runtime_c__type_description__TypeDescription &
)> callback_;
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr serialization_support_;
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr dynamic_message_;
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr dynamic_message_type_;
};
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_SUBSCRIPTION_HPP_

View File

@@ -0,0 +1,327 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_
#include <rosidl_dynamic_typesupport/types.h>
#include <rosidl_dynamic_typesupport/api/dynamic_data.h>
#include <cstdint>
#include <cstddef>
#include <memory>
#include <string>
#include "rclcpp/exceptions.hpp"
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#endif
#define __DYNAMIC_MESSAGE_GET_VALUE_BY_ID_FN(ValueT, FunctionT) \
template<> \
ValueT \
DynamicMessage::get_value<ValueT>(rosidl_dynamic_typesupport_member_id_t id) \
{ \
ValueT out; \
rosidl_dynamic_typesupport_dynamic_data_get_ ## FunctionT ## _value( \
&rosidl_dynamic_data_, id, &out); \
return out; \
}
#define __DYNAMIC_MESSAGE_GET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
template<> \
ValueT \
DynamicMessage::get_value<ValueT>(const std::string & name) \
{ \
return get_value<ValueT>(get_member_id(name)); \
}
#define __DYNAMIC_MESSAGE_SET_VALUE_BY_ID_FN(ValueT, FunctionT) \
template<> \
void \
DynamicMessage::set_value<ValueT>(rosidl_dynamic_typesupport_member_id_t id, ValueT value) \
{ \
rosidl_dynamic_typesupport_dynamic_data_set_ ## FunctionT ## _value( \
&rosidl_dynamic_data_, id, value); \
}
#define __DYNAMIC_MESSAGE_SET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
template<> \
void \
DynamicMessage::set_value<ValueT>(const std::string & name, ValueT value) \
{ \
set_value<ValueT>(get_member_id(name), value); \
}
#define __DYNAMIC_MESSAGE_INSERT_VALUE(ValueT, FunctionT) \
template<> \
rosidl_dynamic_typesupport_member_id_t \
DynamicMessage::insert_value<ValueT>(ValueT value) \
{ \
rosidl_dynamic_typesupport_member_id_t out; \
rosidl_dynamic_typesupport_dynamic_data_insert_ ## FunctionT ## _value( \
&rosidl_dynamic_data_, value, &out); \
return out; \
}
#define DYNAMIC_MESSAGE_DEFINITIONS(ValueT, FunctionT) \
__DYNAMIC_MESSAGE_GET_VALUE_BY_ID_FN(ValueT, FunctionT) \
__DYNAMIC_MESSAGE_GET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
__DYNAMIC_MESSAGE_SET_VALUE_BY_ID_FN(ValueT, FunctionT) \
__DYNAMIC_MESSAGE_SET_VALUE_BY_NAME_FN(ValueT, FunctionT) \
__DYNAMIC_MESSAGE_INSERT_VALUE(ValueT, FunctionT)
namespace rclcpp
{
namespace dynamic_typesupport
{
/**
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
*
* Explicitly:
* - Basic types: bool, byte, char
* - Float types: float, double
* - Int types: int8_t, int16_t, int32_t, int64_t
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
* - String types: std::string, std::u16string
*/
DYNAMIC_MESSAGE_DEFINITIONS(bool, bool);
// DYNAMIC_MESSAGE_DEFINITIONS(std::byte, byte);
DYNAMIC_MESSAGE_DEFINITIONS(char, char);
DYNAMIC_MESSAGE_DEFINITIONS(float, float32);
DYNAMIC_MESSAGE_DEFINITIONS(double, float64);
DYNAMIC_MESSAGE_DEFINITIONS(int8_t, int8);
DYNAMIC_MESSAGE_DEFINITIONS(int16_t, int16);
DYNAMIC_MESSAGE_DEFINITIONS(int32_t, int32);
DYNAMIC_MESSAGE_DEFINITIONS(int64_t, int64);
DYNAMIC_MESSAGE_DEFINITIONS(uint8_t, uint8);
DYNAMIC_MESSAGE_DEFINITIONS(uint16_t, uint16);
DYNAMIC_MESSAGE_DEFINITIONS(uint32_t, uint32);
DYNAMIC_MESSAGE_DEFINITIONS(uint64_t, uint64);
// DYNAMIC_MESSAGE_DEFINITIONS(std::string, std::string);
// DYNAMIC_MESSAGE_DEFINITIONS(std::u16string, std::u16string);
// Byte and String getters have a different implementation and are defined below
// BYTE ============================================================================================
template<>
std::byte
DynamicMessage::get_value<std::byte>(rosidl_dynamic_typesupport_member_id_t id)
{
unsigned char out;
rosidl_dynamic_typesupport_dynamic_data_get_byte_value(&get_rosidl_dynamic_data(), id, &out);
return static_cast<std::byte>(out);
}
template<>
std::byte
DynamicMessage::get_value<std::byte>(const std::string & name)
{
return get_value<std::byte>(get_member_id(name));
}
template<>
void
DynamicMessage::set_value<std::byte>(
rosidl_dynamic_typesupport_member_id_t id, const std::byte value)
{
rosidl_dynamic_typesupport_dynamic_data_set_byte_value(
&rosidl_dynamic_data_, id, static_cast<unsigned char>(value));
}
template<>
void
DynamicMessage::set_value<std::byte>(const std::string & name, const std::byte value)
{
set_value<std::byte>(get_member_id(name), value);
}
template<>
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_value<std::byte>(const std::byte value)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_byte_value(
&rosidl_dynamic_data_, static_cast<unsigned char>(value), &out);
return out;
}
// STRINGS =========================================================================================
template<>
std::string
DynamicMessage::get_value<std::string>(rosidl_dynamic_typesupport_member_id_t id)
{
size_t buf_length;
char * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_string_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length);
auto out = std::string(buf, buf_length);
delete buf;
return out;
}
template<>
std::u16string
DynamicMessage::get_value<std::u16string>(rosidl_dynamic_typesupport_member_id_t id)
{
size_t buf_length;
char16_t * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_wstring_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length);
auto out = std::u16string(buf, buf_length);
delete buf;
return out;
}
template<>
std::string
DynamicMessage::get_value<std::string>(const std::string & name)
{
return get_value<std::string>(get_member_id(name));
}
template<>
std::u16string
DynamicMessage::get_value<std::u16string>(const std::string & name)
{
return get_value<std::u16string>(get_member_id(name));
}
template<>
void
DynamicMessage::set_value<std::string>(
rosidl_dynamic_typesupport_member_id_t id, const std::string value)
{
rosidl_dynamic_typesupport_dynamic_data_set_string_value(
&rosidl_dynamic_data_, id, value.c_str(), value.size());
}
template<>
void
DynamicMessage::set_value<std::u16string>(
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value)
{
rosidl_dynamic_typesupport_dynamic_data_set_wstring_value(
&rosidl_dynamic_data_, id, value.c_str(), value.size());
}
template<>
void
DynamicMessage::set_value<std::string>(const std::string & name, const std::string value)
{
set_value<std::string>(get_member_id(name), value);
}
template<>
void
DynamicMessage::set_value<std::u16string>(const std::string & name, const std::u16string value)
{
set_value<std::u16string>(get_member_id(name), value);
}
template<>
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_value<std::string>(const std::string value)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_string_value(
&rosidl_dynamic_data_, value.c_str(), value.size(), &out);
return out;
}
template<>
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_value<std::u16string>(const std::u16string value)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_wstring_value(
&rosidl_dynamic_data_, value.c_str(), value.size(), &out);
return out;
}
// THROW FOR UNSUPPORTED TYPES =====================================================================
template<typename ValueT>
ValueT
DynamicMessage::get_value(rosidl_dynamic_typesupport_member_id_t id)
{
throw rclcpp::exceptions::UnimplementedError("get_value is not implemented for input type");
}
template<typename ValueT>
ValueT
DynamicMessage::get_value(const std::string & name)
{
throw rclcpp::exceptions::UnimplementedError("get_value is not implemented for input type");
}
template<typename ValueT>
void
DynamicMessage::set_value(
rosidl_dynamic_typesupport_member_id_t id, ValueT value)
{
throw rclcpp::exceptions::UnimplementedError("set_value is not implemented for input type");
}
template<typename ValueT>
void
DynamicMessage::set_value(const std::string & name, ValueT value)
{
throw rclcpp::exceptions::UnimplementedError("set_value is not implemented for input type");
}
template<typename ValueT>
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_value(ValueT value)
{
throw rclcpp::exceptions::UnimplementedError("insert_value is not implemented for input type");
}
} // namespace dynamic_typesupport
} // namespace rclcpp
#undef __DYNAMIC_MESSAGE_GET_VALUE_BY_ID_FN
#undef __DYNAMIC_MESSAGE_GET_VALUE_BY_NAME_FN
#undef __DYNAMIC_MESSAGE_SET_VALUE_BY_ID_FN
#undef __DYNAMIC_MESSAGE_SET_VALUE_BY_NAME_FN
#undef __DYNAMIC_MESSAGE_INSERT_VALUE
#undef DYNAMIC_MESSAGE_DEFINITIONS
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_

View File

@@ -0,0 +1,189 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_
#include <rosidl_dynamic_typesupport/types.h>
#include <rosidl_dynamic_typesupport/api/dynamic_type.h>
#include <cstdint>
#include <cstddef>
#include <memory>
#include <string>
#include "rclcpp/exceptions.hpp"
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_BUILDER_HPP_
#include "rclcpp/dynamic_typesupport/dynamic_message_type_builder.hpp"
#endif
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_MEMBER_FN(MemberT, FunctionT) \
template<> \
void \
DynamicMessageTypeBuilder::add_member<MemberT>( \
rosidl_dynamic_typesupport_member_id_t id, \
const std::string & name, \
const std::string & default_value) \
{ \
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _member( \
&rosidl_dynamic_type_builder_, \
id, name.c_str(), name.size(), default_value.c_str(), default_value.size()); \
}
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN(MemberT, FunctionT) \
template<> \
void \
DynamicMessageTypeBuilder::add_array_member<MemberT>( \
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, \
size_t array_length, \
const std::string & default_value) \
{ \
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _array_member( \
&rosidl_dynamic_type_builder_, \
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(), \
array_length); \
}
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
template<> \
void \
DynamicMessageTypeBuilder::add_unbounded_sequence_member<MemberT>( \
rosidl_dynamic_typesupport_member_id_t id, \
const std::string & name, \
const std::string & default_value) \
{ \
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## \
_unbounded_sequence_member( \
&rosidl_dynamic_type_builder_, \
id, name.c_str(), name.size(), default_value.c_str(), default_value.size()); \
}
#define __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_BOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
template<> \
void \
DynamicMessageTypeBuilder::add_bounded_sequence_member<MemberT>( \
rosidl_dynamic_typesupport_member_id_t id, \
const std::string & name, \
size_t sequence_bound, \
const std::string & default_value) \
{ \
rosidl_dynamic_typesupport_dynamic_type_builder_add_ ## FunctionT ## _bounded_sequence_member( \
&rosidl_dynamic_type_builder_, \
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(), \
sequence_bound); \
}
#define DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(MemberT, FunctionT) \
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_MEMBER_FN(MemberT, FunctionT) \
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN(MemberT, FunctionT) \
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
__DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_BOUNDED_SEQUENCE_MEMBER_FN(MemberT, FunctionT) \
namespace rclcpp
{
namespace dynamic_typesupport
{
/**
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
*
* Explicitly:
* - Basic types: bool, byte, char
* - Float types: float, double
* - Int types: int8_t, int16_t, int32_t, int64_t
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
* - String types: std::string, std::u16string
*/
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(bool, bool);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(std::byte, byte);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(char, char);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(float, float32);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(double, float64);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int8_t, int8);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int16_t, int16);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int32_t, int32);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(int64_t, int64);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint8_t, uint8);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint16_t, uint16);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint32_t, uint32);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(uint64_t, uint64);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(std::string, string);
DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS(std::u16string, wstring);
// THROW FOR UNSUPPORTED TYPES =====================================================================
template<typename MemberT>
void
DynamicMessageTypeBuilder::add_member(
rosidl_dynamic_typesupport_member_id_t id,
const std::string & name,
const std::string & default_value)
{
throw rclcpp::exceptions::UnimplementedError(
"add_member is not implemented for input type");
}
template<typename MemberT>
void
DynamicMessageTypeBuilder::add_array_member(
rosidl_dynamic_typesupport_member_id_t id,
const std::string & name,
size_t array_length, const std::string & default_value)
{
throw rclcpp::exceptions::UnimplementedError(
"add_array_member is not implemented for input type");
}
template<typename MemberT>
void
DynamicMessageTypeBuilder::add_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id,
const std::string & name,
const std::string & default_value)
{
throw rclcpp::exceptions::UnimplementedError(
"add_unbounded_sequence_member is not implemented for input type");
}
template<typename MemberT>
void
DynamicMessageTypeBuilder::add_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id,
const std::string & name,
size_t sequence_bound,
const std::string & default_value)
{
throw rclcpp::exceptions::UnimplementedError(
"add_bounded_sequence_member is not implemented for input type");
}
} // namespace dynamic_typesupport
} // namespace rclcpp
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_MEMBER_FN
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_ARRAY_MEMBER_FN
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_UNBOUNDED_SEQUENCE_MEMBER_FN
#undef __DYNAMIC_MESSAGE_TYPE_BUILDER_ADD_BOUNDED_SEQUENCE_MEMBER_FN
#undef DYNAMIC_MESSAGE_TYPE_BUILDER_DEFINITIONS
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_

View File

@@ -32,16 +32,372 @@ namespace rclcpp
namespace dynamic_typesupport
{
class DynamicMessageType;
class DynamicMessageTypeBuilder;
/// Utility wrapper class for rosidl_dynamic_typesupport_dynamic_data_t
/// STUBBED OUT
/**
* This class:
* - Exposes getter methods for the struct
* - Exposes the underlying serialization support API
*
* Ownership:
* - This class borrows the rosidl_dynamic_typesupport_serialization_support_t stored in the passed
* DynamicSerializationSupport. So it cannot outlive the DynamicSerializationSupport.
* - The DynamicSerializationSupport's rosidl_dynamic_typesupport_serialization_support_t pointer
* must point to the same location in memory as the stored raw pointer!
*
* Note: This class is meant to map to the lower level rosidl_dynamic_typesupport_dynamic_data_t,
* even though rosidl_dynamic_typesupport_dynamic_data_t is equivalent to
* rosidl_dynamic_typesupport_dynamic_data_t, exposing the fundamental methods available to
* rosidl_dynamic_typesupport_dynamic_data_t, allowing the user to access the data's fields.
*/
class DynamicMessage : public std::enable_shared_from_this<DynamicMessage>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessage)
// CONSTRUCTION ==================================================================================
// Most constructors require a passed in DynamicSerializationSupport::SharedPtr, to extend the
// lifetime of the serialization support (if the constructor cannot otherwise get it from args).
//
// In cases where a dynamic data pointer is passed, the serialization support composed by
// the data should be the exact same object managed by the DynamicSerializationSupport,
// otherwise the lifetime management will not work properly.
/// Construct a new DynamicMessage with the provided dynamic type builder, using its allocator
RCLCPP_PUBLIC
explicit DynamicMessage(std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder);
/// Construct a new DynamicMessage with the provided dynamic type builder and allocator
RCLCPP_PUBLIC
DynamicMessage(
std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder,
rcl_allocator_t allocator);
/// Construct a new DynamicMessage with the provided dynamic type, using its allocator
RCLCPP_PUBLIC
explicit DynamicMessage(std::shared_ptr<DynamicMessageType> dynamic_type);
/// Construct a new DynamicMessage with the provided dynamic type and allocator
RCLCPP_PUBLIC
DynamicMessage(
std::shared_ptr<DynamicMessageType> dynamic_type,
rcl_allocator_t allocator);
/// Assume ownership of struct
RCLCPP_PUBLIC
DynamicMessage(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_dynamic_data);
/// Loaning constructor
/// Must only be called with a rosidl dynaimc data object obtained from loaning!
// NOTE(methylDragon): I'd put this in protected, but I need this exposed to
// enable_shared_from_this...
RCLCPP_PUBLIC
DynamicMessage(
DynamicMessage::SharedPtr parent_data,
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_loaned_data);
// NOTE(methylDragon): Deliberately no constructor from description to nudge users towards using
// construction from dynamic type/builder, which is more efficient
/// Move constructor
RCLCPP_PUBLIC
DynamicMessage(DynamicMessage && other) noexcept;
/// Move assignment
RCLCPP_PUBLIC
DynamicMessage & operator=(DynamicMessage && other) noexcept;
RCLCPP_PUBLIC
virtual ~DynamicMessage();
// GETTERS =======================================================================================
RCLCPP_PUBLIC
const std::string
get_serialization_library_identifier() const;
RCLCPP_PUBLIC
const std::string
get_name() const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_dynamic_data_t &
get_rosidl_dynamic_data();
RCLCPP_PUBLIC
const rosidl_dynamic_typesupport_dynamic_data_t &
get_rosidl_dynamic_data() const;
RCLCPP_PUBLIC
DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support();
RCLCPP_PUBLIC
DynamicSerializationSupport::ConstSharedPtr
get_shared_dynamic_serialization_support() const;
RCLCPP_PUBLIC
size_t
get_item_count() const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
get_member_id(size_t index) const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
get_member_id(const std::string & name) const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
get_array_index(size_t index) const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
get_array_index(const std::string & name) const;
// METHODS =======================================================================================
RCLCPP_PUBLIC
DynamicMessage
clone(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
clone_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage
init_from_type(
DynamicMessageType & type, rcl_allocator_t allocator = rcl_get_default_allocator()) const;
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
init_from_type_shared(
DynamicMessageType & type, rcl_allocator_t allocator = rcl_get_default_allocator()) const;
RCLCPP_PUBLIC
bool
equals(const DynamicMessage & other) const;
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
loan_value(
rosidl_dynamic_typesupport_member_id_t id,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
loan_value(
const std::string & name,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
void
clear_all_values();
RCLCPP_PUBLIC
void
clear_nonkey_values();
RCLCPP_PUBLIC
void
clear_value(rosidl_dynamic_typesupport_member_id_t id);
RCLCPP_PUBLIC
void
clear_value(const std::string & name);
RCLCPP_PUBLIC
void
clear_sequence();
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_sequence_data();
RCLCPP_PUBLIC
void
remove_sequence_data(rosidl_dynamic_typesupport_member_id_t index);
RCLCPP_PUBLIC
bool
serialize(rcl_serialized_message_t & buffer);
RCLCPP_PUBLIC
bool
deserialize(rcl_serialized_message_t & buffer);
// MEMBER ACCESS TEMPLATES =======================================================================
/**
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
*
* Explicitly:
* - Basic types: bool, byte, char
* - Float types: float, double
* - Int types: int8_t, int16_t, int32_t, int64_t
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
* - String types: std::string, std::u16string
*/
template<typename ValueT>
ValueT
get_value(rosidl_dynamic_typesupport_member_id_t id);
template<typename ValueT>
ValueT
get_value(const std::string & name);
template<typename ValueT>
void
set_value(rosidl_dynamic_typesupport_member_id_t id, ValueT value);
template<typename ValueT>
void
set_value(const std::string & name, ValueT value);
template<typename ValueT>
rosidl_dynamic_typesupport_member_id_t
insert_value(ValueT value);
// FIXED STRING MEMBER ACCESS ====================================================================
RCLCPP_PUBLIC
const std::string
get_fixed_string_value(rosidl_dynamic_typesupport_member_id_t id, size_t string_length);
RCLCPP_PUBLIC
const std::string
get_fixed_string_value(const std::string & name, size_t string_length);
RCLCPP_PUBLIC
const std::u16string
get_fixed_wstring_value(rosidl_dynamic_typesupport_member_id_t id, size_t wstring_length);
RCLCPP_PUBLIC
const std::u16string
get_fixed_wstring_value(const std::string & name, size_t wstring_length);
RCLCPP_PUBLIC
void
set_fixed_string_value(
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_length);
RCLCPP_PUBLIC
void
set_fixed_string_value(const std::string & name, const std::string value, size_t string_length);
RCLCPP_PUBLIC
void
set_fixed_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_length);
RCLCPP_PUBLIC
void
set_fixed_wstring_value(
const std::string & name, const std::u16string value, size_t wstring_length);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_fixed_string_value(const std::string value, size_t string_length);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_fixed_wstring_value(const std::u16string value, size_t wstring_length);
// BOUNDED STRING MEMBER ACCESS ==================================================================
RCLCPP_PUBLIC
const std::string
get_bounded_string_value(rosidl_dynamic_typesupport_member_id_t id, size_t string_bound);
RCLCPP_PUBLIC
const std::string
get_bounded_string_value(const std::string & name, size_t string_bound);
RCLCPP_PUBLIC
const std::u16string
get_bounded_wstring_value(rosidl_dynamic_typesupport_member_id_t id, size_t wstring_bound);
RCLCPP_PUBLIC
const std::u16string
get_bounded_wstring_value(const std::string & name, size_t wstring_bound);
RCLCPP_PUBLIC
void
set_bounded_string_value(
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_bound);
RCLCPP_PUBLIC
void
set_bounded_string_value(const std::string & name, const std::string value, size_t string_bound);
RCLCPP_PUBLIC
void
set_bounded_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_bound);
RCLCPP_PUBLIC
void
set_bounded_wstring_value(
const std::string & name, const std::u16string value, size_t wstring_bound);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_bounded_string_value(const std::string value, size_t string_bound);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_bounded_wstring_value(const std::u16string value, size_t wstring_bound);
// NESTED MEMBER ACCESS ==========================================================================
RCLCPP_PUBLIC
DynamicMessage
get_nested_data(
rosidl_dynamic_typesupport_member_id_t id,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage
get_nested_data(
const std::string & name,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
get_nested_data_shared(
rosidl_dynamic_typesupport_member_id_t id,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
get_nested_data_shared(
const std::string & name,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
void
set_nested_data(rosidl_dynamic_typesupport_member_id_t id, DynamicMessage & value);
RCLCPP_PUBLIC
void
set_nested_data(const std::string & name, DynamicMessage & value);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_nested_data_copy(const DynamicMessage & value);
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_member_id_t
insert_nested_data(DynamicMessage & value);
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
@@ -62,6 +418,12 @@ private:
RCLCPP_PUBLIC
DynamicMessage();
RCLCPP_PUBLIC
bool
match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_data_t & dynamic_data);
};
} // namespace dynamic_typesupport

View File

@@ -30,16 +30,143 @@ namespace rclcpp
namespace dynamic_typesupport
{
class DynamicMessage;
class DynamicMessageTypeBuilder;
/// Utility wrapper class for `rosidl_dynamic_typesupport_dynamic_type_t`
/// STUBBED OUT
/**
* This class:
* - Exposes getter methods for the struct
* - Exposes the underlying serialization support API
*
* Ownership:
* - This class borrows the `rosidl_dynamic_typesupport_serialization_support_t` stored in the
* passed `DynamicSerializationSupport`.
* So it cannot outlive the `DynamicSerializationSupport`.
* - The `DynamicSerializationSupport`'s `rosidl_dynamic_typesupport_serialization_support_t`
* pointer must point to the same location in memory as the stored raw pointer!
*
* This class is meant to map to the lower level `rosidl_dynamic_typesupport_dynamic_type_t`,
* which can be constructed via `DynamicMessageTypeBuilder`, which maps to
* `rosidl_dynamic_typesupport_dynamic_type_builder_t`.
*
* The usual method of obtaining a `DynamicMessageType` is through construction of
* `rosidl_message_type_support_t` via `rcl_dynamic_message_type_support_handle_create()`, then
* taking ownership of its contents. But `DynamicMessageTypeBuilder` can also be used to obtain
* `DynamicMessageType` by constructing it bottom-up instead, since it exposes the lower_level
* rosidl methods.
*/
class DynamicMessageType : public std::enable_shared_from_this<DynamicMessageType>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageType)
// CONSTRUCTION ==================================================================================
// Most constructors require a passed in `DynamicSerializationSupport::SharedPtr`, to extend the
// lifetime of the serialization support (if the constructor cannot otherwise get it from args).
//
// In cases where a dynamic type pointer is passed, the serialization support composed by
// the type should be the exact same object managed by the `DynamicSerializationSupport`,
// otherwise the lifetime management will not work properly.
/// Construct a new `DynamicMessageType` with the provided dynamic type builder,
/// using its allocator
RCLCPP_PUBLIC
explicit DynamicMessageType(std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder);
/// Construct a new `DynamicMessageType` with the provided dynamic type builder and allocator
RCLCPP_PUBLIC
DynamicMessageType(
std::shared_ptr<DynamicMessageTypeBuilder> dynamic_type_builder,
rcl_allocator_t allocator);
/// Assume ownership of struct
RCLCPP_PUBLIC
DynamicMessageType(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_type_t && rosidl_dynamic_type);
/// From description
RCLCPP_PUBLIC
DynamicMessageType(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator());
/// Move constructor
RCLCPP_PUBLIC
DynamicMessageType(DynamicMessageType && other) noexcept;
/// Move assignment
RCLCPP_PUBLIC
DynamicMessageType & operator=(DynamicMessageType && other) noexcept;
RCLCPP_PUBLIC
virtual ~DynamicMessageType();
/// Swaps the serialization support if `serialization_support` is populated
/**
* The user can call this with another description to reconfigure the type without changing the
* serialization support
*/
RCLCPP_PUBLIC
void
init_from_description(
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator(),
DynamicSerializationSupport::SharedPtr serialization_support = nullptr);
// GETTERS =======================================================================================
RCLCPP_PUBLIC
const std::string
get_serialization_library_identifier() const;
RCLCPP_PUBLIC
const std::string
get_name() const;
RCLCPP_PUBLIC
size_t
get_member_count() const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_dynamic_type_t &
get_rosidl_dynamic_type();
RCLCPP_PUBLIC
const rosidl_dynamic_typesupport_dynamic_type_t &
get_rosidl_dynamic_type() const;
RCLCPP_PUBLIC
DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support();
RCLCPP_PUBLIC
DynamicSerializationSupport::ConstSharedPtr
get_shared_dynamic_serialization_support() const;
// METHODS =======================================================================================
RCLCPP_PUBLIC
DynamicMessageType
clone(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessageType::SharedPtr
clone_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
bool
equals(const DynamicMessageType & other) const;
RCLCPP_PUBLIC
DynamicMessage
build_dynamic_message(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
std::shared_ptr<DynamicMessage>
build_dynamic_message_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
@@ -56,6 +183,12 @@ private:
RCLCPP_PUBLIC
DynamicMessageType();
RCLCPP_PUBLIC
bool
match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_type_t & rosidl_dynamic_type);
};
} // namespace dynamic_typesupport

View File

@@ -30,16 +30,347 @@ namespace rclcpp
namespace dynamic_typesupport
{
class DynamicMessage;
class DynamicMessageType;
/// Utility wrapper class for `rosidl_dynamic_typesupport_dynamic_type_builder_t *`
/// STUBBED OUT
/**
* This class:
* - Manages the lifetime of the raw pointer.
* - Exposes getter methods to get the raw pointer and shared pointers
* - Exposes the underlying serialization support API
*
* Ownership:
* - This class borrows the rosidl_dynamic_typesupport_serialization_support_t stored in the passed
* `DynamicSerializationSupport`.
* So it cannot outlive the `DynamicSerializationSupport`.
* - The `DynamicSerializationSupport`'s `rosidl_dynamic_typesupport_serialization_support_t`
* pointer must point to the same location in memory as the stored raw pointer!
*
* This class is meant to map to rosidl_dynamic_typesupport_dynamic_type_builder_t, facilitating the
* construction of dynamic types bottom-up in the C++ layer.
*
* The usual method of obtaining a `DynamicMessageType` is through construction of
* `rosidl_message_type_support_t` via `rcl_dynamic_message_type_support_handle_create()`, then
* taking ownership of its contents.
* But `DynamicMessageTypeBuilder` can also be used to obtain `DynamicMessageType` by constructing
* it bottom-up instead, since it exposes the lower_level rosidl methods.
*/
class DynamicMessageTypeBuilder : public std::enable_shared_from_this<DynamicMessageTypeBuilder>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageTypeBuilder)
// CONSTRUCTION ==================================================================================
// All constructors require a passed in `DynamicSerializationSupport::SharedPtr`, to extend the
// lifetime of the serialization support.
//
// In cases where a dynamic type builder pointer is passed, the serialization support composed by
// the builder should be the exact same object managed by the `DynamicSerializationSupport`,
// otherwise the lifetime management will not work properly.
/// Construct a new `DynamicMessageTypeBuilder` with the provided serialization support using its
/// allocator
RCLCPP_PUBLIC
DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
const std::string & name);
/// Construct a new `DynamicMessageTypeBuilder` with the provided serialization support and
/// allocator
RCLCPP_PUBLIC
DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
const std::string & name,
rcl_allocator_t allocator);
/// Assume ownership of struct
RCLCPP_PUBLIC
DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_type_builder_t && dynamic_type_builder);
/// From description
RCLCPP_PUBLIC
DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator());
/// Move constructor
RCLCPP_PUBLIC
DynamicMessageTypeBuilder(DynamicMessageTypeBuilder && other) noexcept;
/// Move assignment
RCLCPP_PUBLIC
DynamicMessageTypeBuilder & operator=(DynamicMessageTypeBuilder && other) noexcept;
RCLCPP_PUBLIC
virtual ~DynamicMessageTypeBuilder();
/// Swaps the serialization support if serialization_support is populated
/**
* The user can call this with another description to reconfigure the type without changing the
* serialization support
*/
RCLCPP_PUBLIC
void
init_from_description(
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator(),
DynamicSerializationSupport::SharedPtr serialization_support = nullptr);
// GETTERS =======================================================================================
RCLCPP_PUBLIC
const std::string
get_serialization_library_identifier() const;
RCLCPP_PUBLIC
const std::string
get_name() const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_dynamic_type_builder_t &
get_rosidl_dynamic_type_builder();
RCLCPP_PUBLIC
const rosidl_dynamic_typesupport_dynamic_type_builder_t &
get_rosidl_dynamic_type_builder() const;
RCLCPP_PUBLIC
DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support();
RCLCPP_PUBLIC
DynamicSerializationSupport::ConstSharedPtr
get_shared_dynamic_serialization_support() const;
// METHODS =======================================================================================
RCLCPP_PUBLIC
void
set_name(const std::string & name);
RCLCPP_PUBLIC
DynamicMessageTypeBuilder
clone(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessageTypeBuilder::SharedPtr
clone_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
void
clear(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage
build_dynamic_message(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
build_dynamic_message_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessageType
build_dynamic_message_type(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicMessageType::SharedPtr
build_dynamic_message_type_shared(rcl_allocator_t allocator = rcl_get_default_allocator());
// ADD MEMBERS TEMPLATES =========================================================================
/**
* Since we're in a ROS layer, these should support all ROS interface C++ types as found in:
* https://docs.ros.org/en/rolling/Concepts/About-ROS-Interfaces.html
*
* Explicitly:
* - Basic types: bool, byte, char
* - Float types: float, double
* - Int types: int8_t, int16_t, int32_t, int64_t
* - Unsigned int types: uint8_t, uint16_t, uint32_t, uint64_t
* - String types: std::string, std::u16string
*/
template<typename MemberT>
void
add_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
const std::string & default_value = "");
template<typename MemberT>
void
add_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t array_length,
const std::string & default_value = "");
template<typename MemberT>
void
add_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
const std::string & default_value = "");
template<typename MemberT>
void
add_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t sequence_bound,
const std::string & default_value = "");
// ADD FIXED STRING MEMBERS ======================================================================
RCLCPP_PUBLIC
void
add_fixed_string_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_wstring_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_string_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_length, size_t array_length, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_wstring_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_length, size_t array_length, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_string_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_wstring_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_string_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_length, size_t sequence_bound, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_fixed_wstring_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_length, size_t sequence_bound, const std::string & default_value = "");
// ADD BOUNDED STRING MEMBERS ====================================================================
RCLCPP_PUBLIC
void
add_bounded_string_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_wstring_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_string_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_bound, size_t array_length, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_wstring_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_bound, size_t array_length, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_string_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_wstring_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_string_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_bound, size_t sequence_bound, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_bounded_wstring_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_bound, size_t sequence_bound, const std::string & default_value = "");
// ADD NESTED MEMBERS ============================================================================
RCLCPP_PUBLIC
void
add_complex_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, size_t array_length, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, size_t sequence_bound,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_array_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, size_t array_length,
const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_unbounded_sequence_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value = "");
RCLCPP_PUBLIC
void
add_complex_bounded_sequence_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, size_t sequence_bound,
const std::string & default_value = "");
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
@@ -56,6 +387,19 @@ private:
RCLCPP_PUBLIC
DynamicMessageTypeBuilder();
RCLCPP_PUBLIC
void
init_from_serialization_support_(
DynamicSerializationSupport::SharedPtr serialization_support,
const std::string & name,
rcl_allocator_t allocator);
RCLCPP_PUBLIC
bool
match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_type_builder_t & dynamic_type_builder);
};
} // namespace dynamic_typesupport

View File

@@ -38,16 +38,115 @@ namespace dynamic_typesupport
{
/// Utility wrapper class for `rosidl_message_type_support_t` containing managed
/// STUBBED OUT
/// instances of the typesupport handle impl.
/**
*
* NOTE: This class is the recommended way to obtain the dynamic message type
* support struct, instead of `rcl_dynamic_message_type_support_handle_init()`,
* because this class will manage the lifetimes for you.
*
* Do NOT call rcl_dynamic_message_type_support_handle_fini
*
* This class:
* - Exposes getter methods for the struct
* - Stores shared pointers to wrapper classes that expose the underlying
* serialization support API
*
* Ownership:
* - This class, similarly to the `rosidl_dynamic_typesupport_serialization_support_t`, must outlive
* all downstream usages of the serialization support.
*/
class DynamicMessageTypeSupport : public std::enable_shared_from_this<DynamicMessageTypeSupport>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageTypeSupport)
// CONSTRUCTION ==================================================================================
/// From description
/// Does NOT take ownership of the description (copies instead.)
/// Constructs type support top-down (calling `rcl_dynamic_message_type_support_handle_create()`)
RCLCPP_PUBLIC
DynamicMessageTypeSupport(
const rosidl_runtime_c__type_description__TypeDescription & description,
const std::string & serialization_library_name = "",
rcl_allocator_t allocator = rcl_get_default_allocator());
/// From description, for provided serialization support
/// Does NOT take ownership of the description (copies instead.)
/// Constructs type support top-down (calling `rosidl_dynamic_message_type_support_handle_init()`)
RCLCPP_PUBLIC
DynamicMessageTypeSupport(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator());
/// Assume ownership of managed types
/// Does NOT take ownership of the description (copies instead.)
///
/// The serialization support used to construct all managed SharedPtrs must match.
/// The structure of the provided `description` must match the `dynamic_message_type`
/// The structure of the provided `dynamic_message_type` must match the `dynamic_message
///
/// In this case, the user would have constructed the type support bototm-up (by creating the
/// respective dynamic members.)
RCLCPP_PUBLIC
DynamicMessageTypeSupport(
DynamicSerializationSupport::SharedPtr serialization_support,
DynamicMessageType::SharedPtr dynamic_message_type,
DynamicMessage::SharedPtr dynamic_message,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
virtual ~DynamicMessageTypeSupport();
// GETTERS =======================================================================================
RCLCPP_PUBLIC
const std::string
get_serialization_library_identifier() const;
RCLCPP_PUBLIC
const rosidl_message_type_support_t &
get_const_rosidl_message_type_support();
RCLCPP_PUBLIC
const rosidl_message_type_support_t &
get_const_rosidl_message_type_support() const;
RCLCPP_PUBLIC
const rosidl_runtime_c__type_description__TypeDescription &
get_rosidl_runtime_c_type_description() const;
RCLCPP_PUBLIC
DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support();
RCLCPP_PUBLIC
DynamicSerializationSupport::ConstSharedPtr
get_shared_dynamic_serialization_support() const;
RCLCPP_PUBLIC
DynamicMessageType::SharedPtr
get_shared_dynamic_message_type();
RCLCPP_PUBLIC
DynamicMessageType::ConstSharedPtr
get_shared_dynamic_message_type() const;
RCLCPP_PUBLIC
DynamicMessage::SharedPtr
get_shared_dynamic_message();
RCLCPP_PUBLIC
DynamicMessage::ConstSharedPtr
get_shared_dynamic_message() const;
protected:
RCLCPP_PUBLIC
rosidl_message_type_support_t &
get_rosidl_message_type_support();
DynamicSerializationSupport::SharedPtr serialization_support_;
DynamicMessageType::SharedPtr dynamic_message_type_;
DynamicMessage::SharedPtr dynamic_message_;

View File

@@ -31,22 +31,60 @@ namespace dynamic_typesupport
{
/// Utility wrapper class for rosidl_dynamic_typesupport_serialization_support_t
/**
* This class:
* - Exposes getter methods for the struct
* - Exposes the underlying serialization support API
*
* Ownership:
* - This class, similarly to the rosidl_dynamic_typesupport_serialization_support_t, must outlive
* all downstream usages of the serialization support.
*/
class DynamicSerializationSupport : public std::enable_shared_from_this<DynamicSerializationSupport>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicSerializationSupport)
// CONSTRUCTION ==================================================================================
RCLCPP_PUBLIC
explicit DynamicSerializationSupport(rcl_allocator_t allocator = rcl_get_default_allocator());
/// Get the rmw middleware implementation specific serialization support (configured by name)
RCLCPP_PUBLIC
DynamicSerializationSupport(
const std::string & serialization_library_name,
rcl_allocator_t allocator = rcl_get_default_allocator());
/// Assume ownership of struct
RCLCPP_PUBLIC
explicit DynamicSerializationSupport(
rosidl_dynamic_typesupport_serialization_support_t && rosidl_serialization_support);
/// Move constructor
RCLCPP_PUBLIC
DynamicSerializationSupport(DynamicSerializationSupport && other) noexcept;
/// Move assignment
RCLCPP_PUBLIC
DynamicSerializationSupport & operator=(DynamicSerializationSupport && other) noexcept;
RCLCPP_PUBLIC
virtual ~DynamicSerializationSupport();
// GETTERS =======================================================================================
RCLCPP_PUBLIC
const std::string
get_serialization_library_identifier() const;
RCLCPP_PUBLIC
rosidl_dynamic_typesupport_serialization_support_t &
get_rosidl_serialization_support();
RCLCPP_PUBLIC
const rosidl_dynamic_typesupport_serialization_support_t &
get_rosidl_serialization_support() const;
protected:
rosidl_dynamic_typesupport_serialization_support_t rosidl_serialization_support_;

View File

@@ -100,6 +100,15 @@ public:
{}
};
class InvalidServiceTypeError : public std::runtime_error
{
public:
InvalidServiceTypeError()
: std::runtime_error("Service type is invalid.") {}
explicit InvalidServiceTypeError(const std::string & msg)
: std::runtime_error(msg) {}
};
class UnimplementedError : public std::runtime_error
{
public:

View File

@@ -31,9 +31,15 @@ namespace executors
/// Static executor implementation
/**
* This executor is a static version of the original single threaded executor.
* It's static because it doesn't reconstruct the executable list for every iteration.
* It contains some performance optimization to avoid unnecessary reconstructions of
* the executable list for every iteration.
* All nodes, callbackgroups, timers, subscriptions etc. are created before
* spin() is called, and modified only when an entity is added/removed to/from a node.
* This executor is deprecated because these performance improvements have now been
* applied to all other executors.
* This executor is also considered unstable due to known bugs.
* See the unit-tests that are only applied to `StandardExecutors` for information
* on the known limitations.
*
* To run this executor instead of SingleThreadedExecutor replace:
* rclcpp::executors::SingleThreadedExecutor exec;
@@ -44,7 +50,8 @@ namespace executors
* exec.spin();
* exec.remove_node(node);
*/
class StaticSingleThreadedExecutor : public rclcpp::Executor
class [[deprecated("Use rclcpp::executors::SingleThreadedExecutor")]] StaticSingleThreadedExecutor
: public rclcpp::Executor
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(StaticSingleThreadedExecutor)

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

@@ -0,0 +1,308 @@
// Copyright 2024 Sony Group Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__GENERIC_SERVICE_HPP_
#define RCLCPP__GENERIC_SERVICE_HPP_
#include <cstdlib>
#include <functional>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include <variant>
#include "rclcpp/typesupport_helpers.hpp"
#include "rosidl_runtime_c/service_type_support_struct.h"
#include "rosidl_typesupport_introspection_cpp/identifier.hpp"
#include "rosidl_typesupport_introspection_cpp/service_introspection.hpp"
#include "service.hpp"
namespace rclcpp
{
class GenericService;
class GenericServiceCallback
{
public:
using SharedRequest = std::shared_ptr<void>;
using SharedResponse = std::shared_ptr<void>;
GenericServiceCallback()
: callback_(std::monostate{})
{}
template<
typename CallbackT,
typename std::enable_if_t<!detail::can_be_nullptr<CallbackT>::value, int> = 0>
void
set(CallbackT && callback)
{
// Workaround Windows issue with std::bind
if constexpr (
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrCallback
>::value)
{
callback_.template emplace<SharedPtrCallback>(callback);
} else if constexpr ( // NOLINT, can't satisfy both cpplint and uncrustify
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrWithRequestHeaderCallback
>::value)
{
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallback
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallbackWithServiceHandle
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
} else {
// the else clause is not needed, but anyways we should only be doing this instead
// of all the above workaround ...
callback_ = std::forward<CallbackT>(callback);
}
}
template<
typename CallbackT,
typename std::enable_if_t<detail::can_be_nullptr<CallbackT>::value, int> = 0>
void
set(CallbackT && callback)
{
if (!callback) {
throw std::invalid_argument("AnyServiceCallback::set(): callback cannot be nullptr");
}
// Workaround Windows issue with std::bind
if constexpr (
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrCallback
>::value)
{
callback_.template emplace<SharedPtrCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrWithRequestHeaderCallback
>::value)
{
callback_.template emplace<SharedPtrWithRequestHeaderCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallback
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallback>(callback);
} else if constexpr ( // NOLINT
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrDeferResponseCallbackWithServiceHandle
>::value)
{
callback_.template emplace<SharedPtrDeferResponseCallbackWithServiceHandle>(callback);
} else {
// the else clause is not needed, but anyways we should only be doing this instead
// of all the above workaround ...
callback_ = std::forward<CallbackT>(callback);
}
}
SharedResponse
dispatch(
const std::shared_ptr<rclcpp::GenericService> & service_handle,
const std::shared_ptr<rmw_request_id_t> & request_header,
SharedRequest request,
SharedRequest response)
{
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);
if (std::holds_alternative<std::monostate>(callback_)) {
// TODO(ivanpauno): Remove the set method, and force the users of this class
// to pass a callback at construnciton.
throw std::runtime_error{"unexpected request without any callback set"};
}
if (std::holds_alternative<SharedPtrDeferResponseCallback>(callback_)) {
const auto & cb = std::get<SharedPtrDeferResponseCallback>(callback_);
cb(request_header, std::move(request));
return nullptr;
}
if (std::holds_alternative<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_)) {
const auto & cb = std::get<SharedPtrDeferResponseCallbackWithServiceHandle>(callback_);
cb(service_handle, request_header, std::move(request));
return nullptr;
}
if (std::holds_alternative<SharedPtrCallback>(callback_)) {
(void)request_header;
const auto & cb = std::get<SharedPtrCallback>(callback_);
cb(std::move(request), std::move(response));
} else if (std::holds_alternative<SharedPtrWithRequestHeaderCallback>(callback_)) {
const auto & cb = std::get<SharedPtrWithRequestHeaderCallback>(callback_);
cb(request_header, std::move(request), std::move(response));
}
TRACETOOLS_TRACEPOINT(callback_end, static_cast<const void *>(this));
return response;
}
void register_callback_for_tracing()
{
#ifndef TRACETOOLS_DISABLED
std::visit(
[this](auto && arg) {
if (TRACETOOLS_TRACEPOINT_ENABLED(rclcpp_callback_register)) {
char * symbol = tracetools::get_symbol(arg);
TRACETOOLS_DO_TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
symbol);
std::free(symbol);
}
}, callback_);
#endif // TRACETOOLS_DISABLED
}
private:
using SharedPtrCallback = std::function<void (SharedRequest, SharedResponse)>;
using SharedPtrWithRequestHeaderCallback = std::function<
void (
std::shared_ptr<rmw_request_id_t>,
SharedRequest,
SharedResponse
)>;
using SharedPtrDeferResponseCallback = std::function<
void (
std::shared_ptr<rmw_request_id_t>,
SharedRequest
)>;
using SharedPtrDeferResponseCallbackWithServiceHandle = std::function<
void (
std::shared_ptr<rclcpp::GenericService>,
std::shared_ptr<rmw_request_id_t>,
SharedRequest
)>;
std::variant<
std::monostate,
SharedPtrCallback,
SharedPtrWithRequestHeaderCallback,
SharedPtrDeferResponseCallback,
SharedPtrDeferResponseCallbackWithServiceHandle> callback_;
};
class GenericService
: public ServiceBase,
public std::enable_shared_from_this<GenericService>
{
public:
using Request = void *; // Serialized/Deserialized data pointer of request message
using Response = void *; // Serialized/Deserialized data pointer of response message
using SharedRequest = std::shared_ptr<void>;
using SharedResponse = std::shared_ptr<void>;
using CallbackType = std::function<void (const SharedRequest, SharedResponse)>;
using CallbackWithHeaderType =
std::function<void (const std::shared_ptr<rmw_request_id_t>,
const SharedRequest,
SharedResponse)>;
RCLCPP_SMART_PTR_DEFINITIONS(GenericService)
/// Default constructor.
/**
* The constructor for a Service is almost never called directly.
* Instead, services should be instantiated through the function
* rclcpp::create_service().
*
* \param[in] node_handle NodeBaseInterface pointer that is used in part of the setup.
* \param[in] service_name Name of the topic to publish to.
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool".
* \param[in] any_callback User defined callback to call when a client request is received.
* \param[in] service_options options for the service.
*/
RCLCPP_PUBLIC
GenericService(
std::shared_ptr<rcl_node_t> node_handle,
const std::string & service_name,
const std::string & service_type,
GenericServiceCallback any_callback,
rcl_service_options_t & service_options);
GenericService() = delete;
RCLCPP_PUBLIC
virtual ~GenericService() {}
/// Take the next request from the service.
/**
* \sa ServiceBase::take_type_erased_request().
*
* \param[out] request_out The reference to a service deserialized request object
* into which the middleware will copy the taken request.
* \param[out] request_id_out The output id for the request which can be used
* to associate response with this request in the future.
* \returns true if the request was taken, otherwise false.
* \throws rclcpp::exceptions::RCLError based exceptions if the underlying
* rcl calls fail.
*/
RCLCPP_PUBLIC
bool
take_request(SharedRequest request_out, rmw_request_id_t & request_id_out);
RCLCPP_PUBLIC
std::shared_ptr<void>
create_request() override;
RCLCPP_PUBLIC
std::shared_ptr<void>
create_response();
RCLCPP_PUBLIC
std::shared_ptr<rmw_request_id_t>
create_request_header() override;
RCLCPP_PUBLIC
void
handle_request(
std::shared_ptr<rmw_request_id_t> request_header,
std::shared_ptr<void> request) override;
RCLCPP_PUBLIC
void
send_response(rmw_request_id_t & req_id, SharedResponse & response);
private:
RCLCPP_DISABLE_COPY(GenericService)
GenericServiceCallback any_callback_;
std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;
const rosidl_typesupport_introspection_cpp::MessageMembers * request_members_;
const rosidl_typesupport_introspection_cpp::MessageMembers * response_members_;
};
} // namespace rclcpp
#endif // RCLCPP__GENERIC_SERVICE_HPP_

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

@@ -44,6 +44,7 @@
#include "rclcpp/event.hpp"
#include "rclcpp/generic_client.hpp"
#include "rclcpp/generic_publisher.hpp"
#include "rclcpp/generic_service.hpp"
#include "rclcpp/generic_subscription.hpp"
#include "rclcpp/logger.hpp"
#include "rclcpp/macros.hpp"
@@ -303,6 +304,24 @@ public:
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
rclcpp::CallbackGroup::SharedPtr group = nullptr);
/// Create and return a GenericService.
/**
* \param[in] service_name The topic to service on.
* \param[in] service_type The name of service type, e.g. "std_srvs/srv/SetBool"
* \param[in] callback User-defined callback function.
* \param[in] qos Quality of service profile for the service.
* \param[in] group Callback group to call the service.
* \return Shared pointer to the created service.
*/
template<typename CallbackT>
typename rclcpp::GenericService::SharedPtr
create_generic_service(
const std::string & service_name,
const std::string & service_type,
CallbackT && callback,
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
rclcpp::CallbackGroup::SharedPtr group = nullptr);
/// Create and return a GenericPublisher.
/**
* The returned pointer will never be empty, but this function can throw various exceptions, for
@@ -1411,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
*/

View File

@@ -40,6 +40,7 @@
#include "rclcpp/create_generic_subscription.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rclcpp/create_service.hpp"
#include "rclcpp/create_generic_service.hpp"
#include "rclcpp/create_subscription.hpp"
#include "rclcpp/create_timer.hpp"
#include "rclcpp/detail/resolve_enable_topic_statistics.hpp"
@@ -171,6 +172,25 @@ Node::create_service(
group);
}
template<typename CallbackT>
typename rclcpp::GenericService::SharedPtr
Node::create_generic_service(
const std::string & service_name,
const std::string & service_type,
CallbackT && callback,
const rclcpp::QoS & qos,
rclcpp::CallbackGroup::SharedPtr group)
{
return rclcpp::create_generic_service<CallbackT>(
node_base_,
node_services_,
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
service_type,
std::forward<CallbackT>(callback),
qos,
group);
}
template<typename AllocatorT>
std::shared_ptr<rclcpp::GenericPublisher>
Node::create_generic_publisher(

View File

@@ -67,19 +67,23 @@ struct ParameterEventCallbackHandle
* The first step is to instantiate a ParameterEventHandler, providing a ROS node to use
* to create any required subscriptions:
*
* auto param_handler = std::make_shared<rclcpp::ParameterEventHandler>(node);
* ```cpp
* auto param_handler = std::make_shared<rclcpp::ParameterEventHandler>(node);
* ```
*
* Next, you can supply a callback to the add_parameter_callback method, as follows:
*
* auto cb1 = [&node](const rclcpp::Parameter & p) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb1: Received an update to parameter \"%s\" of type %s: \"%ld\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_int());
* };
* auto handle1 = param_handler->add_parameter_callback("an_int_param", cb1);
* ```cpp
* auto cb1 = [&node](const rclcpp::Parameter & p) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb1: Received an update to parameter \"%s\" of type %s: \"%ld\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_int());
* };
* auto handle1 = param_handler->add_parameter_callback("an_int_param", cb1);
* ```
*
* In this case, we didn't supply a node name (the third, optional, parameter) so the
* default will be to monitor for changes to the "an_int_param" parameter associated with
@@ -92,16 +96,18 @@ struct ParameterEventCallbackHandle
* You may also monitor for changes to parameters in other nodes by supplying the node
* name to add_parameter_callback:
*
* auto cb2 = [&node](const rclcpp::Parameter & p) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb2: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_string().c_str());
* };
* auto handle2 = param_handler->add_parameter_callback(
* "some_remote_param_name", cb2, "some_remote_node_name");
* ```cpp
* auto cb2 = [&node](const rclcpp::Parameter & p) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb2: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_string().c_str());
* };
* auto handle2 = param_handler->add_parameter_callback(
* "some_remote_param_name", cb2, "some_remote_node_name");
* ```
*
* In this case, the callback will be invoked whenever "some_remote_param_name" changes
* on remote node "some_remote_node_name".
@@ -109,7 +115,9 @@ struct ParameterEventCallbackHandle
* To remove a parameter callback, reset the callback handle smart pointer or call
* remove_parameter_callback, passing the handle returned from add_parameter_callback:
*
* param_handler->remove_parameter_callback(handle2);
* ```cpp
* param_handler->remove_parameter_callback(handle2);
* ```
*
* You can also monitor for *all* parameter changes, using add_parameter_event_callback.
* In this case, the callback will be invoked whenever any parameter changes in the system.
@@ -117,40 +125,42 @@ struct ParameterEventCallbackHandle
* is convenient to use a regular expression on the node names or namespaces of interest.
* For example:
*
* auto cb3 =
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent & event) {
* // Look for any updates to parameters in "/a_namespace" as well as any parameter changes
* // to our own node ("this_node")
* std::regex re("(/a_namespace/.*)|(/this_node)");
* if (regex_match(event.node, re)) {
* // Now that we know the event matches the regular expression we scanned for, we can
* // use 'get_parameter_from_event' to get a specific parameter name that we're looking for
* rclcpp::Parameter p;
* if (rclcpp::ParameterEventsSubscriber::get_parameter_from_event(
* event, p, remote_param_name, fqn))
* {
* RCLCPP_INFO(
* node->get_logger(),
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_string().c_str());
* }
*
* // You can also use 'get_parameter*s*_from_event' to enumerate all changes that came
* // in on this event
* auto params = rclcpp::ParameterEventsSubscriber::get_parameters_from_event(event);
* for (auto & p : params) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.value_to_string().c_str());
* }
* ```cpp
* auto cb3 =
* [fqn, remote_param_name, &node](const rcl_interfaces::msg::ParameterEvent & event) {
* // Look for any updates to parameters in "/a_namespace" as well as any parameter changes
* // to our own node ("this_node")
* std::regex re("(/a_namespace/.*)|(/this_node)");
* if (regex_match(event.node, re)) {
* // Now that we know the event matches the regular expression we scanned for, we can
* // use 'get_parameter_from_event' to get a specific parameter name that we're looking for
* rclcpp::Parameter p;
* if (rclcpp::ParameterEventHandler::get_parameter_from_event(
* event, p, remote_param_name, fqn))
* {
* RCLCPP_INFO(
* node->get_logger(),
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.as_string().c_str());
* }
* };
* auto handle3 = param_handler->add_parameter_event_callback(cb3);
*
* // You can also use 'get_parameter*s*_from_event' to enumerate all changes that came
* // in on this event
* auto params = rclcpp::ParameterEventHandler::get_parameters_from_event(event);
* for (auto & p : params) {
* RCLCPP_INFO(
* node->get_logger(),
* "cb3: Received an update to parameter \"%s\" of type: %s: \"%s\"",
* p.get_name().c_str(),
* p.get_type_name().c_str(),
* p.value_to_string().c_str());
* }
* }
* };
* auto handle3 = param_handler->add_parameter_event_callback(cb3);
* ```
*
* For both parameter callbacks and parameter event callbacks, when multiple callbacks are added,
* the callbacks are invoked last-in, first-called order (LIFO).
@@ -160,7 +170,9 @@ struct ParameterEventCallbackHandle
*
* To remove a parameter event callback, reset the callback smart pointer or use:
*
* param_handler->remove_event_parameter_callback(handle3);
* ```cpp
* param_handler->remove_event_parameter_callback(handle3);
* ```
*/
class ParameterEventHandler
{

View File

@@ -359,7 +359,7 @@ private:
/// Return the value of a parameter as a string
RCLCPP_PUBLIC
std::string
to_string(const ParameterValue & type);
to_string(const ParameterValue & value);
} // namespace rclcpp

View File

@@ -146,8 +146,7 @@ public:
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
{
// Topic is unused for now.
(void)topic;
(void)qos;
(void)options;
// If needed, setup intra process communication.
@@ -155,22 +154,26 @@ public:
auto context = node_base->get_context();
// Get the intra process manager instance for this context.
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
// Register the publisher with the intra process manager.
if (qos.history() != rclcpp::HistoryPolicy::KeepLast) {
// Check if the QoS is compatible with intra-process.
auto qos_profile = get_actual_qos();
if (qos_profile.history() != rclcpp::HistoryPolicy::KeepLast) {
throw std::invalid_argument(
"intraprocess communication allowed only with keep last history qos policy");
"intraprocess communication on topic '" + topic +
"' allowed only with keep last history qos policy");
}
if (qos.depth() == 0) {
if (qos_profile.depth() == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value");
"intraprocess communication on topic '" + topic +
"' is not allowed with a zero qos history depth value");
}
if (qos.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
if (qos_profile.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
buffer_ = rclcpp::experimental::create_intra_process_buffer<
ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter>(
rclcpp::detail::resolve_intra_process_buffer_type(options_.intra_process_buffer_type),
qos,
qos_profile,
std::make_shared<ROSMessageTypeAllocator>(ros_message_type_allocator_));
}
// Register the publisher with the intra process manager.
uint64_t intra_process_publisher_id = ipm->add_publisher(this->shared_from_this(), buffer_);
this->setup_intra_process(
intra_process_publisher_id,
@@ -187,7 +190,7 @@ public:
* the loaned message will be directly allocated in the middleware.
* If not, the message allocator of this rclcpp::Publisher instance is being used.
*
* With a call to \sa `publish` the LoanedMessage instance is being returned to the middleware
* With a call to `publish` the LoanedMessage instance is being returned to the middleware
* or free'd accordingly to the allocator.
* If the message is not being published but processed differently, the destructor of this
* class will either return the message to the middleware or deallocate it via the internal
@@ -232,8 +235,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 +317,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

@@ -133,6 +133,9 @@
* - Dynamic typesupport
* - rclcpp::dynamic_typesupport::DynamicMessageTypeSupport
* - rclcpp/dynamic_typesupport/dynamic_message_type_support.hpp
* - Dynamic subscription
* - rclcpp::DynamicSubscription
* - rclcpp/dynamic_subscription.hpp
* - Generic publisher
* - rclcpp::Node::create_generic_publisher()
* - rclcpp::GenericPublisher
@@ -184,4 +187,6 @@
#include "rclcpp/waitable.hpp"
#include "rclcpp/wait_set.hpp"
#include "rclcpp/dynamic_subscription.hpp"
#endif // RCLCPP__RCLCPP_HPP_

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<

View File

@@ -644,6 +644,10 @@ protected:
rclcpp::node_interfaces::NodeBaseInterface * const node_base_;
// TODO(methylDragon): Remove if we don't need this
// rclcpp::node_interfaces::NodeGraphInterface * const node_graph_;
// rclcpp::node_interfaces::NodeServicesInterface * const node_services_;
std::shared_ptr<rcl_node_t> node_handle_;
std::recursive_mutex callback_mutex_;

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

@@ -172,12 +172,11 @@ private:
{
auto received_message_age = std::make_unique<ReceivedMessageAge>();
received_message_age->Start();
subscriber_statistics_collectors_.emplace_back(std::move(received_message_age));
auto received_message_period = std::make_unique<ReceivedMessagePeriod>();
received_message_period->Start();
{
std::lock_guard<std::mutex> lock(mutex_);
subscriber_statistics_collectors_.emplace_back(std::move(received_message_age));
subscriber_statistics_collectors_.emplace_back(std::move(received_message_period));
}

View File

@@ -109,7 +109,7 @@ public:
RCLCPP_PUBLIC
virtual
void
add_to_wait_set(rcl_wait_set_t & wait_set);
add_to_wait_set(rcl_wait_set_t & wait_set) = 0;
/// Check if the Waitable is ready.
/**
@@ -124,7 +124,7 @@ public:
RCLCPP_PUBLIC
virtual
bool
is_ready(const rcl_wait_set_t & wait_set);
is_ready(const rcl_wait_set_t & wait_set) = 0;
/// Take the data so that it can be consumed with `execute`.
/**
@@ -176,7 +176,7 @@ public:
RCLCPP_PUBLIC
virtual
std::shared_ptr<void>
take_data_by_entity_id(size_t id);
take_data_by_entity_id(size_t id) = 0;
/// Execute data that is passed in.
/**
@@ -203,7 +203,7 @@ public:
RCLCPP_PUBLIC
virtual
void
execute(const std::shared_ptr<void> & data);
execute(const std::shared_ptr<void> & data) = 0;
/// Exchange the "in use by wait set" state for this timer.
/**
@@ -246,7 +246,7 @@ public:
RCLCPP_PUBLIC
virtual
void
set_on_ready_callback(std::function<void(size_t, int)> callback);
set_on_ready_callback(std::function<void(size_t, int)> callback) = 0;
/// Unset any callback registered via set_on_ready_callback.
/**
@@ -256,7 +256,7 @@ public:
RCLCPP_PUBLIC
virtual
void
clear_on_ready_callback();
clear_on_ready_callback() = 0;
private:
std::atomic<bool> in_use_by_wait_set_{false};

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

View File

@@ -212,28 +212,27 @@ Context::init(
}
rcl_context_.reset(context, __delete_context);
if (init_options.auto_initialize_logging()) {
logging_mutex_ = get_global_logging_mutex();
std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
size_t & count = get_logging_reference_count();
if (0u == count) {
ret = rcl_logging_configure_with_output_handler(
&rcl_context_->global_arguments,
rcl_init_options_get_allocator(init_options.get_rcl_init_options()),
rclcpp_logging_output_handler);
if (RCL_RET_OK != ret) {
rcl_context_.reset();
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure logging");
}
} else {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"logging was initialized more than once");
}
++count;
}
try {
if (init_options.auto_initialize_logging()) {
logging_mutex_ = get_global_logging_mutex();
std::lock_guard<std::recursive_mutex> guard(*logging_mutex_);
size_t & count = get_logging_reference_count();
if (0u == count) {
ret = rcl_logging_configure_with_output_handler(
&rcl_context_->global_arguments,
rcl_init_options_get_allocator(init_options.get_rcl_init_options()),
rclcpp_logging_output_handler);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure logging");
}
} else {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"logging was initialized more than once");
}
++count;
}
std::vector<std::string> unparsed_ros_arguments = detail::get_unparsed_ros_arguments(
argc, argv, &(rcl_context_->global_arguments), rcl_get_default_allocator());
if (!unparsed_ros_arguments.empty()) {

View File

@@ -0,0 +1,49 @@
// Copyright 2024 Sony Group Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <memory>
#include "rclcpp/create_generic_service.hpp"
#include "rclcpp/generic_service.hpp"
namespace rclcpp
{
rclcpp::GenericService::SharedPtr
create_generic_service(
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
std::shared_ptr<node_interfaces::NodeGraphInterface> node_graph,
std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
const std::string & service_name,
const std::string & service_type,
GenericServiceCallback any_callback,
const rclcpp::QoS & qos,
rclcpp::CallbackGroup::SharedPtr group)
{
rcl_service_options_t options = rcl_service_get_default_options();
options.qos = qos.get_rmw_qos_profile();
auto srv = rclcpp::GenericService::make_shared(
node_base.get(),
node_graph,
service_name,
service_type,
any_callback,
options);
auto srv_base_ptr = std::dynamic_pointer_cast<rclcpp::ServiceBase>(srv);
node_services->add_service(srv_base_ptr, group);
return srv;
}
} // namespace rclcpp

View File

@@ -0,0 +1,113 @@
// Copyright 2022 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/dynamic_subscription.hpp"
#include <memory>
#include <string>
#include "rcl/subscription.h"
#include "rclcpp/exceptions.hpp"
namespace rclcpp
{
std::shared_ptr<void> DynamicSubscription::create_message()
{
return create_serialized_message();
}
std::shared_ptr<rclcpp::SerializedMessage> DynamicSubscription::create_serialized_message()
{
return std::make_shared<rclcpp::SerializedMessage>(0);
}
void DynamicSubscription::handle_message(std::shared_ptr<void> &, const rclcpp::MessageInfo &)
{
throw rclcpp::exceptions::UnimplementedError(
"handle_message is not implemented for DynamicSubscription");
}
void DynamicSubscription::handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> &, const rclcpp::MessageInfo &)
{
throw rclcpp::exceptions::UnimplementedError(
"handle_serialized_message is not implemented for DynamicSubscription");
}
void DynamicSubscription::handle_loaned_message(void *, const rclcpp::MessageInfo &)
{
throw rclcpp::exceptions::UnimplementedError(
"handle_loaned_message is not implemented for DynamicSubscription");
}
void DynamicSubscription::return_message(std::shared_ptr<void> & message)
{
auto typed_message = std::static_pointer_cast<rclcpp::SerializedMessage>(message);
return_serialized_message(typed_message);
}
void DynamicSubscription::return_serialized_message(
std::shared_ptr<rclcpp::SerializedMessage> & message)
{
message.reset();
}
// DYNAMIC TYPE ====================================================================================
// TODO(methylDragon): Re-order later
// Does not clone
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
DynamicSubscription::get_shared_dynamic_message_type()
{
return dynamic_message_type_;
}
// Does not clone
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
DynamicSubscription::get_shared_dynamic_message()
{
return dynamic_message_;
}
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
DynamicSubscription::get_shared_dynamic_serialization_support()
{
return serialization_support_;
}
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
DynamicSubscription::create_dynamic_message()
{
return dynamic_message_->init_from_type_shared(*dynamic_message_type_);
}
void
DynamicSubscription::return_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message)
{
message.reset();
}
void DynamicSubscription::handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info)
{
(void) message_info;
callback_(message, ts_->get_rosidl_runtime_c_type_description());
}
} // namespace rclcpp

View File

@@ -36,5 +36,734 @@ using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeBuilder;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_IMPL_HPP_
// Template specialization implementations
#include "rclcpp/dynamic_typesupport/detail/dynamic_message_impl.hpp"
#endif
// CONSTRUCTION ==================================================================================
DynamicMessage::DynamicMessage(const DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder)
: DynamicMessage::DynamicMessage(
dynamic_type_builder, dynamic_type_builder->get_rosidl_dynamic_type_builder().allocator) {}
DynamicMessage::DynamicMessage(
const DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder,
rcl_allocator_t allocator)
: serialization_support_(dynamic_type_builder->get_shared_dynamic_serialization_support()),
rosidl_dynamic_data_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data()),
is_loaned_(false),
parent_data_(nullptr)
{
if (!serialization_support_) {
throw std::runtime_error("dynamic message could not bind serialization support!");
}
if (!dynamic_type_builder) {
throw std::runtime_error("dynamic message type builder cannot be nullptr!");
}
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
dynamic_type_builder->get_rosidl_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type_builder(
&rosidl_dynamic_type_builder, &allocator, &get_rosidl_dynamic_data());
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic data object from dynamic type builder");
}
}
DynamicMessage::DynamicMessage(const DynamicMessageType::SharedPtr dynamic_type)
: DynamicMessage::DynamicMessage(
dynamic_type, dynamic_type->get_rosidl_dynamic_type().allocator) {}
DynamicMessage::DynamicMessage(
const DynamicMessageType::SharedPtr dynamic_type,
rcl_allocator_t allocator)
: serialization_support_(dynamic_type->get_shared_dynamic_serialization_support()),
rosidl_dynamic_data_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data()),
is_loaned_(false),
parent_data_(nullptr)
{
if (!serialization_support_) {
throw std::runtime_error("dynamic type could not bind serialization support!");
}
if (!dynamic_type) {
throw std::runtime_error("dynamic message type cannot be nullptr!");
}
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
dynamic_type->get_rosidl_dynamic_type();
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type(
&rosidl_dynamic_type, &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not init new dynamic data object from dynamic type") +
rcl_get_error_string().str);
}
}
DynamicMessage::DynamicMessage(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_dynamic_data)
: serialization_support_(serialization_support),
rosidl_dynamic_data_(std::move(rosidl_dynamic_data)),
is_loaned_(false),
parent_data_(nullptr)
{
if (serialization_support) {
if (!match_serialization_support_(*serialization_support, rosidl_dynamic_data)) {
throw std::runtime_error(
"serialization support library identifier does not match dynamic data's!");
}
}
}
DynamicMessage::DynamicMessage(
DynamicMessage::SharedPtr parent_data,
rosidl_dynamic_typesupport_dynamic_data_t && rosidl_loaned_data)
: serialization_support_(parent_data->get_shared_dynamic_serialization_support()),
rosidl_dynamic_data_(std::move(rosidl_loaned_data)),
is_loaned_(true),
parent_data_(nullptr)
{
if (!parent_data) {
throw std::runtime_error("parent dynamic data cannot be nullptr!");
}
if (serialization_support_) {
if (!match_serialization_support_(*serialization_support_, rosidl_loaned_data)) {
throw std::runtime_error(
"serialization support library identifier does not match loaned dynamic data's!");
}
}
parent_data_ = parent_data;
}
DynamicMessage::DynamicMessage(DynamicMessage && other) noexcept
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
rosidl_dynamic_data_(std::exchange(
other.rosidl_dynamic_data_,
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data())),
is_loaned_(other.is_loaned_),
parent_data_(std::exchange(other.parent_data_, nullptr))
{}
DynamicMessage &
DynamicMessage::operator=(DynamicMessage && other) noexcept
{
std::swap(serialization_support_, other.serialization_support_);
std::swap(rosidl_dynamic_data_, other.rosidl_dynamic_data_);
is_loaned_ = other.is_loaned_;
std::swap(parent_data_, other.parent_data_);
return *this;
}
DynamicMessage::~DynamicMessage()
{} // STUBBED
{
if (!is_loaned_) {
if (rosidl_dynamic_typesupport_dynamic_data_fini(&get_rosidl_dynamic_data()) !=
RCUTILS_RET_OK)
{
RCUTILS_LOG_ERROR("could not fini rosidl dynamic data");
}
return;
}
// Loaned case
if (!parent_data_) {
RCUTILS_LOG_ERROR("dynamic data is loaned, but parent is missing!!");
} else {
rosidl_dynamic_typesupport_dynamic_data_return_loaned_value(
&parent_data_->get_rosidl_dynamic_data(), &get_rosidl_dynamic_data());
}
}
bool
DynamicMessage::match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_data_t & rosidl_dynamic_type_data)
{
if (serialization_support.get_serialization_library_identifier() != std::string(
rosidl_dynamic_type_data.serialization_support->serialization_library_identifier))
{
RCUTILS_LOG_ERROR("serialization support library identifier does not match dynamic data's");
return false;
}
return true;
}
// GETTERS =======================================================================================
const std::string
DynamicMessage::get_serialization_library_identifier() const
{
return std::string(
get_rosidl_dynamic_data().serialization_support->serialization_library_identifier);
}
const std::string
DynamicMessage::get_name() const
{
size_t buf_length;
const char * buf;
if (
rosidl_dynamic_typesupport_dynamic_data_get_name(
&get_rosidl_dynamic_data(), &buf,
&buf_length) !=
RCUTILS_RET_OK)
{
throw std::runtime_error(
std::string("could not get name for dynamic data") + rcl_get_error_string().str);
}
return std::string(buf, buf_length);
}
rosidl_dynamic_typesupport_dynamic_data_t &
DynamicMessage::get_rosidl_dynamic_data()
{
return rosidl_dynamic_data_;
}
const rosidl_dynamic_typesupport_dynamic_data_t &
DynamicMessage::get_rosidl_dynamic_data() const
{
return rosidl_dynamic_data_;
}
DynamicSerializationSupport::SharedPtr
DynamicMessage::get_shared_dynamic_serialization_support()
{
return serialization_support_;
}
DynamicSerializationSupport::ConstSharedPtr
DynamicMessage::get_shared_dynamic_serialization_support() const
{
return serialization_support_;
}
size_t
DynamicMessage::get_item_count() const
{
size_t item_count;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_item_count(
&get_rosidl_dynamic_data(), &item_count);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not get item count of dynamic data");
}
return item_count;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::get_member_id(size_t index) const
{
rosidl_dynamic_typesupport_member_id_t member_id;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_member_id_at_index(
&get_rosidl_dynamic_data(), index, &member_id);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not member id of dynamic data element by index");
}
return member_id;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::get_member_id(const std::string & name) const
{
rosidl_dynamic_typesupport_member_id_t member_id;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_member_id_by_name(
&get_rosidl_dynamic_data(), name.c_str(), name.size(), &member_id);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not member id of dynamic data element by name");
}
return member_id;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::get_array_index(size_t index) const
{
rosidl_dynamic_typesupport_member_id_t array_index;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_get_array_index(
&get_rosidl_dynamic_data(), index, &array_index);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not array index of dynamic data element by index");
}
return array_index;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::get_array_index(const std::string & name) const
{
return get_array_index(get_member_id(name));
}
// METHODS =======================================================================================
DynamicMessage
DynamicMessage::clone(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_clone(
&get_rosidl_dynamic_data(), &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic data: ") + rcl_get_error_string().str);
}
return DynamicMessage(get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_data));
}
DynamicMessage::SharedPtr
DynamicMessage::clone_shared(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_clone(
&get_rosidl_dynamic_data(), &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic data: ") + rcl_get_error_string().str);
}
return DynamicMessage::make_shared(
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_data));
}
DynamicMessage
DynamicMessage::init_from_type(DynamicMessageType & type, rcl_allocator_t allocator) const
{
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type(
&type.get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic data object from dynamic type");
}
return DynamicMessage(serialization_support_, std::move(rosidl_dynamic_data));
}
DynamicMessage::SharedPtr
DynamicMessage::init_from_type_shared(DynamicMessageType & type, rcl_allocator_t allocator) const
{
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_init_from_dynamic_type(
&type.get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic data object from dynamic type");
}
return DynamicMessage::make_shared(serialization_support_, std::move(rosidl_dynamic_data));
}
bool
DynamicMessage::equals(const DynamicMessage & other) const
{
if (get_serialization_library_identifier() != other.get_serialization_library_identifier()) {
throw std::runtime_error("library identifiers don't match");
}
bool equals;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_equals(
&get_rosidl_dynamic_data(), &other.get_rosidl_dynamic_data(), &equals);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not equate dynamic messages");
}
return equals;
}
DynamicMessage::SharedPtr
DynamicMessage::loan_value(
rosidl_dynamic_typesupport_member_id_t id,
rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_loan_value(
&get_rosidl_dynamic_data(), id, &allocator, &rosidl_dynamic_data);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not loan dynamic data: ") + rcl_get_error_string().str);
}
return DynamicMessage::make_shared(shared_from_this(), std::move(rosidl_dynamic_data));
}
DynamicMessage::SharedPtr
DynamicMessage::loan_value(const std::string & name, rcl_allocator_t allocator)
{
return loan_value(get_member_id(name), allocator);
}
void
DynamicMessage::clear_all_values()
{
rosidl_dynamic_typesupport_dynamic_data_clear_all_values(&get_rosidl_dynamic_data());
}
void
DynamicMessage::clear_nonkey_values()
{
rosidl_dynamic_typesupport_dynamic_data_clear_nonkey_values(&get_rosidl_dynamic_data());
}
void
DynamicMessage::clear_value(rosidl_dynamic_typesupport_member_id_t id)
{
rosidl_dynamic_typesupport_dynamic_data_clear_value(&get_rosidl_dynamic_data(), id);
}
void
DynamicMessage::clear_value(const std::string & name)
{
clear_value(get_member_id(name));
}
void
DynamicMessage::clear_sequence()
{
rosidl_dynamic_typesupport_dynamic_data_clear_sequence_data(&get_rosidl_dynamic_data());
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_sequence_data()
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_sequence_data(&get_rosidl_dynamic_data(), &out);
return out;
}
void
DynamicMessage::remove_sequence_data(rosidl_dynamic_typesupport_member_id_t index)
{
rosidl_dynamic_typesupport_dynamic_data_remove_sequence_data(
&get_rosidl_dynamic_data(), index);
}
bool
DynamicMessage::serialize(rcl_serialized_message_t & buffer)
{
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_serialize(
&get_rosidl_dynamic_data(), &buffer);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could serialize loan dynamic data: ") + rcl_get_error_string().str);
}
return true;
}
bool
DynamicMessage::deserialize(rcl_serialized_message_t & buffer)
{
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_data_deserialize(
&get_rosidl_dynamic_data(), &buffer);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could deserialize loan dynamic data: ") + rcl_get_error_string().str);
}
return true;
}
// MEMBER ACCESS ===================================================================================
// Defined in "detail/dynamic_message_impl.hpp"
// FIXED STRING MEMBER ACCESS ======================================================================
const std::string
DynamicMessage::get_fixed_string_value(
rosidl_dynamic_typesupport_member_id_t id, size_t string_length)
{
size_t buf_length;
char * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_fixed_string_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length, string_length);
auto out = std::string(buf, buf_length);
delete buf;
return out;
}
const std::string
DynamicMessage::get_fixed_string_value(const std::string & name, size_t string_length)
{
return get_fixed_string_value(get_member_id(name), string_length);
}
const std::u16string
DynamicMessage::get_fixed_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, size_t wstring_length)
{
size_t buf_length;
char16_t * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_fixed_wstring_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length, wstring_length);
auto out = std::u16string(buf, buf_length);
delete buf;
return out;
}
const std::u16string
DynamicMessage::get_fixed_wstring_value(const std::string & name, size_t wstring_length)
{
return get_fixed_wstring_value(get_member_id(name), wstring_length);
}
void
DynamicMessage::set_fixed_string_value(
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_length)
{
rosidl_dynamic_typesupport_dynamic_data_set_fixed_string_value(
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), string_length);
}
void
DynamicMessage::set_fixed_string_value(
const std::string & name, const std::string value, size_t string_length)
{
set_fixed_string_value(get_member_id(name), value, string_length);
}
void
DynamicMessage::set_fixed_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_length)
{
rosidl_dynamic_typesupport_dynamic_data_set_fixed_wstring_value(
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), wstring_length);
}
void
DynamicMessage::set_fixed_wstring_value(
const std::string & name, const std::u16string value, size_t wstring_length)
{
set_fixed_wstring_value(get_member_id(name), value, wstring_length);
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_fixed_string_value(const std::string value, size_t string_length)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_fixed_string_value(
&get_rosidl_dynamic_data(), value.c_str(), value.size(), string_length, &out);
return out;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_fixed_wstring_value(const std::u16string value, size_t wstring_length)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_fixed_wstring_value(
&get_rosidl_dynamic_data(), value.c_str(), value.size(), wstring_length, &out);
return out;
}
// BOUNDED STRING MEMBER ACCESS ====================================================================
const std::string
DynamicMessage::get_bounded_string_value(
rosidl_dynamic_typesupport_member_id_t id, size_t string_bound)
{
size_t buf_length;
char * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_bounded_string_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length, string_bound);
auto out = std::string(buf, buf_length);
delete buf;
return out;
}
const std::string
DynamicMessage::get_bounded_string_value(const std::string & name, size_t string_bound)
{
return get_bounded_string_value(get_member_id(name), string_bound);
}
const std::u16string
DynamicMessage::get_bounded_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, size_t wstring_bound)
{
size_t buf_length;
char16_t * buf = nullptr;
rosidl_dynamic_typesupport_dynamic_data_get_bounded_wstring_value(
&get_rosidl_dynamic_data(), id, &buf, &buf_length, wstring_bound);
auto out = std::u16string(buf, buf_length);
delete buf;
return out;
}
const std::u16string
DynamicMessage::get_bounded_wstring_value(const std::string & name, size_t wstring_bound)
{
return get_bounded_wstring_value(get_member_id(name), wstring_bound);
}
void
DynamicMessage::set_bounded_string_value(
rosidl_dynamic_typesupport_member_id_t id, const std::string value, size_t string_bound)
{
rosidl_dynamic_typesupport_dynamic_data_set_bounded_string_value(
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), string_bound);
}
void
DynamicMessage::set_bounded_string_value(
const std::string & name, const std::string value, size_t string_bound)
{
set_bounded_string_value(get_member_id(name), value, string_bound);
}
void
DynamicMessage::set_bounded_wstring_value(
rosidl_dynamic_typesupport_member_id_t id, const std::u16string value, size_t wstring_bound)
{
rosidl_dynamic_typesupport_dynamic_data_set_bounded_wstring_value(
&get_rosidl_dynamic_data(), id, value.c_str(), value.size(), wstring_bound);
}
void
DynamicMessage::set_bounded_wstring_value(
const std::string & name, const std::u16string value, size_t wstring_bound)
{
set_bounded_wstring_value(get_member_id(name), value, wstring_bound);
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_bounded_string_value(const std::string value, size_t string_bound)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_bounded_string_value(
&get_rosidl_dynamic_data(), value.c_str(), value.size(), string_bound, &out);
return out;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_bounded_wstring_value(const std::u16string value, size_t wstring_bound)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_bounded_wstring_value(
&get_rosidl_dynamic_data(), value.c_str(), value.size(), wstring_bound, &out);
return out;
}
// NESTED MEMBER ACCESS ============================================================================
DynamicMessage
DynamicMessage::get_nested_data(
rosidl_dynamic_typesupport_member_id_t id, rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_data_t out =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rosidl_dynamic_typesupport_dynamic_data_get_nested_data(
&get_rosidl_dynamic_data(), id, &allocator, &out);
return DynamicMessage(get_shared_dynamic_serialization_support(), std::move(out));
}
DynamicMessage
DynamicMessage::get_nested_data(const std::string & name, rcl_allocator_t allocator)
{
return get_nested_data(get_member_id(name), allocator);
}
DynamicMessage::SharedPtr
DynamicMessage::get_nested_data_shared(
rosidl_dynamic_typesupport_member_id_t id, rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_data_t out =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_data();
rosidl_dynamic_typesupport_dynamic_data_get_nested_data(
&get_rosidl_dynamic_data(), id, &allocator, &out);
return DynamicMessage::make_shared(get_shared_dynamic_serialization_support(), std::move(out));
}
DynamicMessage::SharedPtr
DynamicMessage::get_nested_data_shared(const std::string & name, rcl_allocator_t allocator)
{
return get_nested_data_shared(get_member_id(name), allocator);
}
void
DynamicMessage::set_nested_data(
rosidl_dynamic_typesupport_member_id_t id, DynamicMessage & value)
{
rosidl_dynamic_typesupport_dynamic_data_set_nested_data(
&get_rosidl_dynamic_data(), id, &value.get_rosidl_dynamic_data());
}
void
DynamicMessage::set_nested_data(const std::string & name, DynamicMessage & value)
{
set_nested_data(get_member_id(name), value);
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_nested_data_copy(const DynamicMessage & value)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_nested_data_copy(
&get_rosidl_dynamic_data(), &value.get_rosidl_dynamic_data(), &out);
return out;
}
rosidl_dynamic_typesupport_member_id_t
DynamicMessage::insert_nested_data(DynamicMessage & value)
{
rosidl_dynamic_typesupport_member_id_t out;
rosidl_dynamic_typesupport_dynamic_data_insert_nested_data(
&get_rosidl_dynamic_data(), &value.get_rosidl_dynamic_data(), &out);
return out;
}

View File

@@ -34,5 +34,248 @@ using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeBuilder;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
// CONSTRUCTION ====================================================================================
DynamicMessageType::DynamicMessageType(DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder)
: DynamicMessageType::DynamicMessageType(
dynamic_type_builder, dynamic_type_builder->get_rosidl_dynamic_type_builder().allocator) {}
DynamicMessageType::DynamicMessageType(
DynamicMessageTypeBuilder::SharedPtr dynamic_type_builder,
rcl_allocator_t allocator)
: serialization_support_(dynamic_type_builder->get_shared_dynamic_serialization_support()),
rosidl_dynamic_type_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type())
{
if (!serialization_support_) {
throw std::runtime_error("dynamic type could not bind serialization support!");
}
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
dynamic_type_builder->get_rosidl_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_init_from_dynamic_type_builder(
&rosidl_dynamic_type_builder, &allocator, &get_rosidl_dynamic_type());
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic type object");
}
}
DynamicMessageType::DynamicMessageType(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_type_t && rosidl_dynamic_type)
: serialization_support_(serialization_support),
rosidl_dynamic_type_(std::move(rosidl_dynamic_type))
{
if (serialization_support) {
if (!match_serialization_support_(*serialization_support, rosidl_dynamic_type)) {
throw std::runtime_error(
"serialization support library identifier does not match dynamic type's!");
}
}
}
DynamicMessageType::DynamicMessageType(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator)
: serialization_support_(serialization_support),
rosidl_dynamic_type_(rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type())
{
init_from_description(description, allocator, serialization_support);
}
DynamicMessageType::DynamicMessageType(DynamicMessageType && other) noexcept
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
rosidl_dynamic_type_(std::exchange(
other.rosidl_dynamic_type_, rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type()))
{}
DynamicMessageType &
DynamicMessageType::operator=(DynamicMessageType && other) noexcept
{
std::swap(serialization_support_, other.serialization_support_);
std::swap(rosidl_dynamic_type_, other.rosidl_dynamic_type_);
return *this;
}
DynamicMessageType::~DynamicMessageType()
{} // STUBBED
{
if (rosidl_dynamic_typesupport_dynamic_type_fini(&get_rosidl_dynamic_type()) != RCUTILS_RET_OK) {
RCUTILS_LOG_ERROR("could not fini rosidl dynamic type");
}
}
void
DynamicMessageType::init_from_description(
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator,
DynamicSerializationSupport::SharedPtr serialization_support)
{
if (serialization_support) {
// Swap serialization support if serialization support is given
serialization_support_ = serialization_support;
}
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_init_from_description(
&serialization_support_->get_rosidl_serialization_support(),
&description,
&allocator,
&rosidl_dynamic_type);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic type object");
}
rosidl_dynamic_type_ = std::move(rosidl_dynamic_type);
}
bool
DynamicMessageType::match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_type_t & rosidl_dynamic_type)
{
if (serialization_support.get_serialization_library_identifier() != std::string(
rosidl_dynamic_type.serialization_support->serialization_library_identifier))
{
RCUTILS_LOG_ERROR(
"serialization support library identifier does not match dynamic type's (%s vs %s)",
serialization_support.get_serialization_library_identifier().c_str(),
rosidl_dynamic_type.serialization_support->serialization_library_identifier);
return false;
}
return true;
}
// GETTERS =========================================================================================
const std::string
DynamicMessageType::get_serialization_library_identifier() const
{
return std::string(
get_rosidl_dynamic_type().serialization_support->serialization_library_identifier);
}
const std::string
DynamicMessageType::get_name() const
{
size_t buf_length;
const char * buf;
rosidl_dynamic_typesupport_dynamic_type_get_name(&get_rosidl_dynamic_type(), &buf, &buf_length);
return std::string(buf, buf_length);
}
size_t
DynamicMessageType::get_member_count() const
{
size_t out;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_get_member_count(
&get_rosidl_dynamic_type(), &out);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not get member count: ") + rcl_get_error_string().str);
}
return out;
}
rosidl_dynamic_typesupport_dynamic_type_t &
DynamicMessageType::get_rosidl_dynamic_type()
{
return rosidl_dynamic_type_;
}
const rosidl_dynamic_typesupport_dynamic_type_t &
DynamicMessageType::get_rosidl_dynamic_type() const
{
return rosidl_dynamic_type_;
}
DynamicSerializationSupport::SharedPtr
DynamicMessageType::get_shared_dynamic_serialization_support()
{
return serialization_support_;
}
DynamicSerializationSupport::ConstSharedPtr
DynamicMessageType::get_shared_dynamic_serialization_support() const
{
return serialization_support_;
}
// METHODS =========================================================================================
DynamicMessageType
DynamicMessageType::clone(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_clone(
&get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_type);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic type: ") + rcl_get_error_string().str);
}
return DynamicMessageType(
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type));
}
DynamicMessageType::SharedPtr
DynamicMessageType::clone_shared(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_clone(
&get_rosidl_dynamic_type(), &allocator, &rosidl_dynamic_type);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic type: ") + rcl_get_error_string().str);
}
return DynamicMessageType::make_shared(
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type));
}
bool
DynamicMessageType::equals(const DynamicMessageType & other) const
{
if (get_serialization_library_identifier() != other.get_serialization_library_identifier()) {
throw std::runtime_error("library identifiers don't match");
}
bool out;
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_equals(
&get_rosidl_dynamic_type(), &other.get_rosidl_dynamic_type(), &out);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not equate dynamic message types: ") + rcl_get_error_string().str);
}
return out;
}
DynamicMessage
DynamicMessageType::build_dynamic_message(rcl_allocator_t allocator)
{
return DynamicMessage(shared_from_this(), allocator);
}
DynamicMessage::SharedPtr
DynamicMessageType::build_dynamic_message_shared(rcl_allocator_t allocator)
{
return DynamicMessage::make_shared(shared_from_this(), allocator);
}

View File

@@ -33,5 +33,582 @@ using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeBuilder;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DETAIL__DYNAMIC_MESSAGE_TYPE_BUILDER_IMPL_HPP_
// Template specialization implementations
#include "rclcpp/dynamic_typesupport/detail/dynamic_message_type_builder_impl.hpp"
#endif
// CONSTRUCTION ====================================================================================
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support, const std::string & name)
: DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
serialization_support,
name,
serialization_support->get_rosidl_serialization_support().allocator) {}
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
const std::string & name,
rcl_allocator_t allocator)
: serialization_support_(serialization_support),
rosidl_dynamic_type_builder_(
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())
{
init_from_serialization_support_(serialization_support, name, allocator);
}
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
rosidl_dynamic_typesupport_dynamic_type_builder_t && rosidl_dynamic_type_builder)
: serialization_support_(serialization_support),
rosidl_dynamic_type_builder_(
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())
{
if (!serialization_support) {
throw std::runtime_error("serialization support cannot be nullptr!");
}
if (!match_serialization_support_(*serialization_support, rosidl_dynamic_type_builder)) {
throw std::runtime_error(
"serialization support library does not match dynamic type builder's!");
}
}
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator)
: serialization_support_(serialization_support),
rosidl_dynamic_type_builder_(
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())
{
if (!serialization_support) {
throw std::runtime_error("serialization support cannot be nullptr!");
}
init_from_description(description, allocator, serialization_support);
}
DynamicMessageTypeBuilder::DynamicMessageTypeBuilder(DynamicMessageTypeBuilder && other) noexcept
: serialization_support_(std::exchange(other.serialization_support_, nullptr)),
rosidl_dynamic_type_builder_(std::exchange(
other.rosidl_dynamic_type_builder_,
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder())) {}
DynamicMessageTypeBuilder &
DynamicMessageTypeBuilder::operator=(DynamicMessageTypeBuilder && other) noexcept
{
std::swap(serialization_support_, other.serialization_support_);
std::swap(rosidl_dynamic_type_builder_, other.rosidl_dynamic_type_builder_);
return *this;
}
DynamicMessageTypeBuilder::~DynamicMessageTypeBuilder()
{} // STUBBED
{
if (rosidl_dynamic_typesupport_dynamic_type_builder_fini(&get_rosidl_dynamic_type_builder()) !=
RCUTILS_RET_OK)
{
RCUTILS_LOG_ERROR("could not fini rosidl dynamic type builder");
}
}
void
DynamicMessageTypeBuilder::init_from_description(
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator,
DynamicSerializationSupport::SharedPtr serialization_support)
{
if (serialization_support) {
// Swap serialization support if serialization support is given
serialization_support_ = serialization_support;
}
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_init_from_description(
&serialization_support_->get_rosidl_serialization_support(),
&description,
&allocator,
&rosidl_dynamic_type_builder);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init new dynamic type builder object");
}
rosidl_dynamic_type_builder_ = std::move(rosidl_dynamic_type_builder);
}
void
DynamicMessageTypeBuilder::init_from_serialization_support_(
DynamicSerializationSupport::SharedPtr serialization_support,
const std::string & name,
rcl_allocator_t allocator)
{
if (!serialization_support) {
throw std::runtime_error("serialization support cannot be nullptr!");
}
if (!&serialization_support->get_rosidl_serialization_support()) {
throw std::runtime_error("serialization support raw pointer cannot be nullptr!");
}
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_init(
&serialization_support->get_rosidl_serialization_support(),
name.c_str(), name.size(),
&allocator,
&rosidl_dynamic_type_builder);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not init dynamic type builder: ") + rcl_get_error_string().str);
}
}
bool
DynamicMessageTypeBuilder::match_serialization_support_(
const DynamicSerializationSupport & serialization_support,
const rosidl_dynamic_typesupport_dynamic_type_builder_t & rosidl_dynamic_type_builder)
{
if (serialization_support.get_serialization_library_identifier() != std::string(
rosidl_dynamic_type_builder.serialization_support->serialization_library_identifier))
{
RCUTILS_LOG_ERROR(
"serialization support library identifier does not match dynamic type builder's");
return false;
}
return true;
}
// GETTERS =======================================================================================
const std::string
DynamicMessageTypeBuilder::get_serialization_library_identifier() const
{
return std::string(
get_rosidl_dynamic_type_builder().serialization_support->serialization_library_identifier);
}
const std::string
DynamicMessageTypeBuilder::get_name() const
{
size_t buf_length;
const char * buf;
rosidl_dynamic_typesupport_dynamic_type_builder_get_name(
&get_rosidl_dynamic_type_builder(), &buf, &buf_length);
return std::string(buf, buf_length);
}
rosidl_dynamic_typesupport_dynamic_type_builder_t &
DynamicMessageTypeBuilder::get_rosidl_dynamic_type_builder()
{
return rosidl_dynamic_type_builder_;
}
const rosidl_dynamic_typesupport_dynamic_type_builder_t &
DynamicMessageTypeBuilder::get_rosidl_dynamic_type_builder() const
{
return rosidl_dynamic_type_builder_;
}
DynamicSerializationSupport::SharedPtr
DynamicMessageTypeBuilder::get_shared_dynamic_serialization_support()
{
return serialization_support_;
}
DynamicSerializationSupport::ConstSharedPtr
DynamicMessageTypeBuilder::get_shared_dynamic_serialization_support() const
{
return serialization_support_;
}
// METHODS =======================================================================================
void
DynamicMessageTypeBuilder::set_name(const std::string & name)
{
rosidl_dynamic_typesupport_dynamic_type_builder_set_name(
&get_rosidl_dynamic_type_builder(), name.c_str(), name.size());
}
DynamicMessageTypeBuilder
DynamicMessageTypeBuilder::clone(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_clone(
&get_rosidl_dynamic_type_builder(), &allocator, &rosidl_dynamic_type_builder);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic type builder: ") + rcl_get_error_string().str);
}
return DynamicMessageTypeBuilder(
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type_builder));
}
DynamicMessageTypeBuilder::SharedPtr
DynamicMessageTypeBuilder::clone_shared(rcl_allocator_t allocator)
{
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder =
rosidl_dynamic_typesupport_get_zero_initialized_dynamic_type_builder();
rcutils_ret_t ret = rosidl_dynamic_typesupport_dynamic_type_builder_clone(
&get_rosidl_dynamic_type_builder(), &allocator, &rosidl_dynamic_type_builder);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error(
std::string("could not clone dynamic type builder: ") + rcl_get_error_string().str);
}
return DynamicMessageTypeBuilder::make_shared(
get_shared_dynamic_serialization_support(), std::move(rosidl_dynamic_type_builder));
}
void
DynamicMessageTypeBuilder::clear(rcl_allocator_t allocator)
{
if (!serialization_support_) {
throw std::runtime_error(
"cannot call clear() on a dynamic type builder with uninitialized serialization support"
);
}
const std::string & name = get_name();
init_from_serialization_support_(serialization_support_, name, allocator);
}
DynamicMessage
DynamicMessageTypeBuilder::build_dynamic_message(rcl_allocator_t allocator)
{
return DynamicMessage(shared_from_this(), allocator);
}
DynamicMessage::SharedPtr
DynamicMessageTypeBuilder::build_dynamic_message_shared(rcl_allocator_t allocator)
{
return DynamicMessage::make_shared(shared_from_this(), allocator);
}
DynamicMessageType
DynamicMessageTypeBuilder::build_dynamic_message_type(rcl_allocator_t allocator)
{
return DynamicMessageType(shared_from_this(), allocator);
}
DynamicMessageType::SharedPtr
DynamicMessageTypeBuilder::build_dynamic_message_type_shared(rcl_allocator_t allocator)
{
return DynamicMessageType::make_shared(shared_from_this(), allocator);
}
// ADD MEMBERS =====================================================================================
// Defined in "detail/dynamic_message_type_builder_impl.hpp"
// ADD FIXED STRING MEMBERS ========================================================================
void
DynamicMessageTypeBuilder::add_fixed_string_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_length);
}
void
DynamicMessageTypeBuilder::add_fixed_wstring_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_length);
}
void
DynamicMessageTypeBuilder::add_fixed_string_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_length, size_t array_length, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_array_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_length, array_length);
}
void
DynamicMessageTypeBuilder::add_fixed_wstring_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_length, size_t array_length, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_array_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_length, array_length);
}
void
DynamicMessageTypeBuilder::add_fixed_string_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_length,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_unbounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_length);
}
void
DynamicMessageTypeBuilder::add_fixed_wstring_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_length,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_unbounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_length);
}
void
DynamicMessageTypeBuilder::add_fixed_string_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_length, size_t sequence_bound, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_string_bounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_length, sequence_bound);
}
void
DynamicMessageTypeBuilder::add_fixed_wstring_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_length, size_t sequence_bound, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_fixed_wstring_bounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_length, sequence_bound);
}
// ADD BOUNDED STRING MEMBERS ======================================================================
void
DynamicMessageTypeBuilder::add_bounded_string_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_bound);
}
void
DynamicMessageTypeBuilder::add_bounded_wstring_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_bound);
}
void
DynamicMessageTypeBuilder::add_bounded_string_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_bound, size_t array_length, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_array_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_bound, array_length);
}
void
DynamicMessageTypeBuilder::add_bounded_wstring_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_bound, size_t array_length, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_array_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_bound, array_length);
}
void
DynamicMessageTypeBuilder::add_bounded_string_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t string_bound,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_unbounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_bound);
}
void
DynamicMessageTypeBuilder::add_bounded_wstring_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name, size_t wstring_bound,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_unbounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_bound);
}
void
DynamicMessageTypeBuilder::add_bounded_string_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t string_bound, size_t sequence_bound, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_string_bounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
string_bound, sequence_bound);
}
void
DynamicMessageTypeBuilder::add_bounded_wstring_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
size_t wstring_bound, size_t sequence_bound, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_bounded_wstring_bounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
wstring_bound, sequence_bound);
}
// ADD NESTED MEMBERS ==============================================================================
void
DynamicMessageTypeBuilder::add_complex_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type.get_rosidl_dynamic_type());
}
void
DynamicMessageTypeBuilder::add_complex_array_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, size_t array_length, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_array_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type.get_rosidl_dynamic_type(), array_length);
}
void
DynamicMessageTypeBuilder::add_complex_unbounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_unbounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type.get_rosidl_dynamic_type());
}
void
DynamicMessageTypeBuilder::add_complex_bounded_sequence_member(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageType & nested_type, size_t sequence_bound, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_bounded_sequence_member(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type.get_rosidl_dynamic_type(), sequence_bound);
}
void
DynamicMessageTypeBuilder::add_complex_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_member_builder(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type_builder.get_rosidl_dynamic_type_builder());
}
void
DynamicMessageTypeBuilder::add_complex_array_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, size_t array_length,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_array_member_builder(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type_builder.get_rosidl_dynamic_type_builder(), array_length);
}
void
DynamicMessageTypeBuilder::add_complex_unbounded_sequence_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_unbounded_sequence_member_builder(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type_builder.get_rosidl_dynamic_type_builder());
}
void
DynamicMessageTypeBuilder::add_complex_bounded_sequence_member_builder(
rosidl_dynamic_typesupport_member_id_t id, const std::string & name,
DynamicMessageTypeBuilder & nested_type_builder, size_t sequence_bound,
const std::string & default_value)
{
rosidl_dynamic_typesupport_dynamic_type_builder_add_complex_bounded_sequence_member_builder(
&get_rosidl_dynamic_type_builder(),
id, name.c_str(), name.size(), default_value.c_str(), default_value.size(),
&nested_type_builder.get_rosidl_dynamic_type_builder(), sequence_bound);
}

View File

@@ -45,5 +45,273 @@ using rclcpp::dynamic_typesupport::DynamicMessageType;
using rclcpp::dynamic_typesupport::DynamicMessageTypeSupport;
using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
// CONSTRUCTION ====================================================================================
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
const rosidl_runtime_c__type_description__TypeDescription & description,
const std::string & serialization_library_name,
rcl_allocator_t allocator)
: serialization_support_(nullptr),
dynamic_message_type_(nullptr),
dynamic_message_(nullptr),
rosidl_message_type_support_(rosidl_get_zero_initialized_message_type_support_handle())
{
rcl_ret_t ret;
if (serialization_library_name.empty()) {
ret = rcl_dynamic_message_type_support_handle_init(
nullptr, &description, &allocator, &rosidl_message_type_support_);
} else {
ret = rcl_dynamic_message_type_support_handle_init(
serialization_library_name.c_str(), &description, &allocator, &rosidl_message_type_support_);
}
if (ret != RCL_RET_OK) {
std::string error_msg =
std::string("error initializing rosidl message type support: ") + rcl_get_error_string().str;
rcl_reset_error();
throw std::runtime_error(error_msg);
}
if (rosidl_message_type_support_.typesupport_identifier !=
rosidl_get_dynamic_typesupport_identifier())
{
throw std::runtime_error("rosidl message type support is of the wrong type");
}
auto ts_impl = static_cast<rosidl_dynamic_message_type_support_impl_t *>(const_cast<void *>(
rosidl_message_type_support_.data));
serialization_support_ = DynamicSerializationSupport::make_shared(
std::move(ts_impl->serialization_support));
dynamic_message_type_ = DynamicMessageType::make_shared(
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message_type));
dynamic_message_ = DynamicMessage::make_shared(
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message));
}
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
DynamicSerializationSupport::SharedPtr serialization_support,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator)
: serialization_support_(serialization_support),
dynamic_message_type_(nullptr),
dynamic_message_(nullptr),
rosidl_message_type_support_(rosidl_get_zero_initialized_message_type_support_handle())
{
// Check null
if (!serialization_support) {
throw std::runtime_error("serialization_support cannot be nullptr.");
}
rosidl_type_hash_t type_hash;
rcutils_ret_t hash_ret = rcl_calculate_type_hash(
// TODO(methylDragon): Swap this out with the conversion function when it is ready
reinterpret_cast<const type_description_interfaces__msg__TypeDescription *>(&description),
&type_hash);
if (hash_ret != RCL_RET_OK) {
throw std::runtime_error("failed to get type hash");
}
rcutils_ret_t ret = rosidl_dynamic_message_type_support_handle_init(
&serialization_support->get_rosidl_serialization_support(),
&type_hash, // type_hash
&description, // type_description
nullptr, // type_description_sources (not implemented for dynamic types)
&allocator,
&rosidl_message_type_support_);
if (ret != RCUTILS_RET_OK) {
throw std::runtime_error("could not init rosidl message type support");
}
if (rosidl_message_type_support_.typesupport_identifier !=
rosidl_get_dynamic_typesupport_identifier())
{
throw std::runtime_error("rosidl message type support is of the wrong type");
}
auto ts_impl = static_cast<const rosidl_dynamic_message_type_support_impl_t *>(
rosidl_message_type_support_.data);
dynamic_message_type_ = DynamicMessageType::make_shared(
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message_type));
dynamic_message_ = DynamicMessage::make_shared(
get_shared_dynamic_serialization_support(), std::move(*ts_impl->dynamic_message));
}
DynamicMessageTypeSupport::DynamicMessageTypeSupport(
DynamicSerializationSupport::SharedPtr serialization_support,
DynamicMessageType::SharedPtr dynamic_message_type,
DynamicMessage::SharedPtr dynamic_message,
const rosidl_runtime_c__type_description__TypeDescription & description,
rcl_allocator_t allocator)
: serialization_support_(serialization_support),
dynamic_message_type_(dynamic_message_type),
dynamic_message_(dynamic_message),
rosidl_message_type_support_(rosidl_get_zero_initialized_message_type_support_handle())
{
// Check null
if (!serialization_support) {
throw std::runtime_error("serialization_support cannot be nullptr.");
}
if (!dynamic_message_type) {
throw std::runtime_error("dynamic_message_type cannot be nullptr.");
}
if (!dynamic_message) {
throw std::runtime_error("dynamic_message cannot be nullptr.");
}
// Check identifiers
if (serialization_support->get_serialization_library_identifier() !=
dynamic_message_type->get_serialization_library_identifier())
{
throw std::runtime_error(
"serialization support library identifier does not match "
"dynamic message type library identifier.");
}
if (dynamic_message_type->get_serialization_library_identifier() !=
dynamic_message->get_serialization_library_identifier())
{
throw std::runtime_error(
"dynamic message type library identifier does not match "
"dynamic message library identifier.");
}
rosidl_type_hash_t type_hash;
rcutils_ret_t hash_ret = rcl_calculate_type_hash(
// TODO(methylDragon): Swap this out with the conversion function when it is ready
// from https://github.com/ros2/rcl/pull/1052
reinterpret_cast<const type_description_interfaces__msg__TypeDescription *>(&description),
&type_hash);
if (hash_ret != RCL_RET_OK) {
std::string error_msg = std::string("failed to get type hash: ") + rcl_get_error_string().str;
rcl_reset_error();
throw std::runtime_error(error_msg);
}
auto ts_impl = static_cast<rosidl_dynamic_message_type_support_impl_t *>(
allocator.zero_allocate(1, sizeof(rosidl_dynamic_message_type_support_impl_t), allocator.state)
);
if (!ts_impl) {
throw std::runtime_error("could not allocate rosidl_message_type_support_t");
}
ts_impl->allocator = allocator;
ts_impl->type_hash = type_hash;
if (!rosidl_runtime_c__type_description__TypeDescription__copy(
&description, &ts_impl->type_description))
{
throw std::runtime_error("could not copy type description");
}
// ts_impl->type_description_sources = // Not used
ts_impl->serialization_support = serialization_support->get_rosidl_serialization_support();
ts_impl->dynamic_message_type = &dynamic_message_type->get_rosidl_dynamic_type();
ts_impl->dynamic_message = &dynamic_message->get_rosidl_dynamic_data();
rosidl_message_type_support_ = {
rosidl_get_dynamic_typesupport_identifier(), // typesupport_identifier
ts_impl, // data
get_message_typesupport_handle_function, // func
// get_type_hash_func
rosidl_get_dynamic_message_type_support_type_hash_function,
// get_type_description_func
rosidl_get_dynamic_message_type_support_type_description_function,
// get_type_description_sources_func
rosidl_get_dynamic_message_type_support_type_description_sources_function
};
}
DynamicMessageTypeSupport::~DynamicMessageTypeSupport()
{} // STUBBED
{
// These must go first
serialization_support_.reset();
dynamic_message_type_.reset();
dynamic_message_.reset();
// Early return if type support isn't populated to avoid segfaults
if (!rosidl_message_type_support_.data) {
return;
}
// We only partially finalize the rosidl_message_type_support->data since its pointer members are
// managed by their respective SharedPtr wrapper classes.
auto ts_impl = static_cast<rosidl_dynamic_message_type_support_impl_t *>(
const_cast<void *>(rosidl_message_type_support_.data)
);
rcutils_allocator_t allocator = ts_impl->allocator;
rosidl_runtime_c__type_description__TypeDescription__fini(&ts_impl->type_description);
rosidl_runtime_c__type_description__TypeSource__Sequence__fini(
&ts_impl->type_description_sources);
allocator.deallocate(static_cast<void *>(ts_impl), allocator.state); // Always C allocated
}
// GETTERS =========================================================================================
const std::string
DynamicMessageTypeSupport::get_serialization_library_identifier() const
{
return serialization_support_->get_serialization_library_identifier();
}
rosidl_message_type_support_t &
DynamicMessageTypeSupport::get_rosidl_message_type_support()
{
return rosidl_message_type_support_;
}
const rosidl_message_type_support_t &
DynamicMessageTypeSupport::get_const_rosidl_message_type_support()
{
return rosidl_message_type_support_;
}
const rosidl_message_type_support_t &
DynamicMessageTypeSupport::get_const_rosidl_message_type_support() const
{
return rosidl_message_type_support_;
}
const rosidl_runtime_c__type_description__TypeDescription &
DynamicMessageTypeSupport::get_rosidl_runtime_c_type_description() const
{
auto ts_impl = static_cast<const rosidl_dynamic_message_type_support_impl_t *>(
get_const_rosidl_message_type_support().data
);
return ts_impl->type_description;
}
DynamicSerializationSupport::SharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_serialization_support()
{
return serialization_support_;
}
DynamicSerializationSupport::ConstSharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_serialization_support() const
{
return serialization_support_;
}
DynamicMessageType::SharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_message_type()
{
return dynamic_message_type_;
}
DynamicMessageType::ConstSharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_message_type() const
{
return dynamic_message_type_;
}
DynamicMessage::SharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_message()
{
return dynamic_message_;
}
DynamicMessage::ConstSharedPtr
DynamicMessageTypeSupport::get_shared_dynamic_message() const
{
return dynamic_message_;
}

View File

@@ -28,19 +28,71 @@ using rclcpp::dynamic_typesupport::DynamicSerializationSupport;
// CONSTRUCTION ====================================================================================
DynamicSerializationSupport::DynamicSerializationSupport(rcl_allocator_t allocator)
: DynamicSerializationSupport::DynamicSerializationSupport("", allocator)
{
throw std::runtime_error("Unimplemented");
}
: DynamicSerializationSupport::DynamicSerializationSupport("", allocator) {}
DynamicSerializationSupport::DynamicSerializationSupport(
const std::string & /*serialization_library_name*/,
rcl_allocator_t /*allocator*/)
const std::string & serialization_library_name,
rcl_allocator_t allocator)
: rosidl_serialization_support_(
rosidl_dynamic_typesupport_get_zero_initialized_serialization_support())
{
throw std::runtime_error("Unimplemented");
rmw_ret_t ret = RMW_RET_ERROR;
if (serialization_library_name.empty()) {
ret = rmw_serialization_support_init(NULL, &allocator, &rosidl_serialization_support_);
} else {
ret = rmw_serialization_support_init(
serialization_library_name.c_str(), &allocator, &rosidl_serialization_support_);
}
if (ret != RCL_RET_OK) {
std::string error_msg =
std::string("could not initialize new serialization support object: ") +
rcl_get_error_string().str;
rcl_reset_error();
throw std::runtime_error(error_msg);
}
}
DynamicSerializationSupport::DynamicSerializationSupport(
rosidl_dynamic_typesupport_serialization_support_t && rosidl_serialization_support)
: rosidl_serialization_support_(std::move(rosidl_serialization_support)) {}
DynamicSerializationSupport::DynamicSerializationSupport(
DynamicSerializationSupport && other) noexcept
: rosidl_serialization_support_(std::exchange(
other.rosidl_serialization_support_,
rosidl_dynamic_typesupport_get_zero_initialized_serialization_support())) {}
DynamicSerializationSupport &
DynamicSerializationSupport::operator=(DynamicSerializationSupport && other) noexcept
{
std::swap(rosidl_serialization_support_, other.rosidl_serialization_support_);
return *this;
}
DynamicSerializationSupport::~DynamicSerializationSupport()
{} // STUBBED
{
rosidl_dynamic_typesupport_serialization_support_fini(&rosidl_serialization_support_);
}
// GETTERS =========================================================================================
const std::string
DynamicSerializationSupport::get_serialization_library_identifier() const
{
return std::string(
rosidl_dynamic_typesupport_serialization_support_get_library_identifier(
&rosidl_serialization_support_));
}
rosidl_dynamic_typesupport_serialization_support_t &
DynamicSerializationSupport::get_rosidl_serialization_support()
{
return rosidl_serialization_support_;
}
const rosidl_dynamic_typesupport_serialization_support_t &
DynamicSerializationSupport::get_rosidl_serialization_support() const
{
return rosidl_serialization_support_;
}

View File

@@ -610,10 +610,25 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
}
// DYNAMIC SUBSCRIPTION ========================================================================
// Deliver dynamic message
// If a subscription is dynamic, then it will use its serialization-specific dynamic data.
//
// Two cases:
// - Dynamic type subscription using dynamic type stored in its own internal type support struct
// - Non-dynamic type subscription with no stored dynamic type
// - Subscriptions of this type must be able to lookup the local message description to
// generate a dynamic type at runtime!
// - TODO(methylDragon): I won't be handling this case yet
case rclcpp::DeliveredMessageKind::DYNAMIC_MESSAGE:
{
throw std::runtime_error("Unimplemented");
DynamicMessage::SharedPtr dynamic_message = subscription->create_dynamic_message();
take_and_do_error_handling(
"taking a dynamic message from topic",
subscription->get_topic_name(),
// This modifies the stored dynamic data in the DynamicMessage in-place
[&]() {return subscription->take_dynamic_message(*dynamic_message, message_info);},
[&]() {subscription->handle_dynamic_message(dynamic_message, message_info);});
subscription->return_dynamic_message(dynamic_message);
break;
}
default:
@@ -729,13 +744,33 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
// Clear any previous wait result
this->wait_result_.reset();
// we need to make sure that callback groups don't get out of scope
// during the wait. As in jazzy, they are not covered by the DynamicStorage,
// we explicitly hold them here as a bugfix
std::vector<rclcpp::CallbackGroup::SharedPtr> cbgs;
{
std::lock_guard<std::mutex> guard(mutex_);
if (this->entities_need_rebuild_.exchange(false) || current_collection_.empty()) {
this->collect_entities();
}
auto callback_groups = this->collector_.get_all_callback_groups();
cbgs.resize(callback_groups.size());
for(const auto & w_ptr : callback_groups) {
auto shr_ptr = w_ptr.lock();
if(shr_ptr) {
cbgs.push_back(std::move(shr_ptr));
}
}
}
this->wait_result_.emplace(wait_set_.wait(timeout));
// drop references to the callback groups, before trying to execute anything
cbgs.clear();
if (!this->wait_result_ || this->wait_result_->kind() == WaitResultKind::Empty) {
RCUTILS_LOG_WARN_NAMED(
"rclcpp",

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

@@ -79,7 +79,6 @@ EventsExecutor::setup_notify_waitable()
// ---> we need to wake up the executor so that it can terminate
// - a node or callback group guard condition is triggered:
// ---> the entities collection is changed, we need to update callbacks
entities_need_rebuild_ = false;
this->handle_updated_entities(false);
});
@@ -168,6 +167,14 @@ EventsExecutor::spin_some_impl(std::chrono::nanoseconds max_duration, bool exhau
return false;
};
// If this spin is not exhaustive (e.g. spin_some), we need to explicitly check
// if entities need to be rebuilt here rather than letting the notify waitable event do it.
// A non-exhaustive spin would not check for work a second time, thus delaying the execution
// of some entities to the next invocation of spin.
if (!exhaustive) {
this->handle_updated_entities(false);
}
// Get the number of events and timers ready at start
const size_t ready_events_at_start = events_queue_->size();
size_t executed_events = 0;
@@ -314,6 +321,17 @@ void
EventsExecutor::handle_updated_entities(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
// before that event gets processed, for example if
// a node or callback group is manually added to the executor.
const bool notify_waitable_triggered = entities_need_rebuild_.exchange(false);
if (!notify_waitable_triggered && !this->collector_.has_pending()) {
return;
}
// Build the new collection
this->collector_.update_collections();
auto callback_groups = this->collector_.get_all_callback_groups();
@@ -341,6 +359,11 @@ EventsExecutor::refresh_current_collection(
// Acquire lock before modifying the current collection
std::lock_guard<std::mutex> guard(mutex_);
// Remove expired entities to ensure re-initialized objects
// are updated. This fixes issues with stale state entities.
// See: https://github.com/ros2/rclcpp/pull/2586
current_collection_.remove_expired_entities();
current_collection_.timers.update(
new_collection.timers,
[this](rclcpp::TimerBase::SharedPtr timer) {timers_manager_->add_timer(timer);},

View File

@@ -31,22 +31,31 @@ GenericClient::GenericClient(
rcl_client_options_t & client_options)
: ClientBase(node_base, node_graph)
{
ts_lib_ = get_typesupport_library(
service_type, "rosidl_typesupport_cpp");
const rosidl_service_type_support_t * service_ts;
try {
ts_lib_ = get_typesupport_library(
service_type, "rosidl_typesupport_cpp");
auto service_ts_ = get_service_typesupport_handle(
service_type, "rosidl_typesupport_cpp", *ts_lib_);
service_ts = get_service_typesupport_handle(
service_type, "rosidl_typesupport_cpp", *ts_lib_);
auto response_type_support_intro = get_message_typesupport_handle(
service_ts_->response_typesupport,
rosidl_typesupport_introspection_cpp::typesupport_identifier);
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
response_type_support_intro->data);
auto response_type_support_intro = get_message_typesupport_handle(
service_ts->response_typesupport,
rosidl_typesupport_introspection_cpp::typesupport_identifier);
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
response_type_support_intro->data);
} catch (std::runtime_error & err) {
RCLCPP_ERROR(
rclcpp::get_node_logger(node_handle_.get()).get_child("rclcpp"),
"Invalid service type: %s",
err.what());
throw rclcpp::exceptions::InvalidServiceTypeError(err.what());
}
rcl_ret_t ret = rcl_client_init(
this->get_client_handle().get(),
this->get_rcl_node_handle(),
service_ts_,
service_ts,
service_name.c_str(),
&client_options);
if (ret != RCL_RET_OK) {
@@ -100,6 +109,13 @@ GenericClient::handle_response(
if (std::holds_alternative<Promise>(value)) {
auto & promise = std::get<Promise>(value);
promise.set_value(std::move(response));
} else if (std::holds_alternative<CallbackTypeValueVariant>(value)) {
auto & inner = std::get<CallbackTypeValueVariant>(value);
const auto & callback = std::get<CallbackType>(inner);
auto & promise = std::get<Promise>(inner);
auto & future = std::get<SharedFuture>(inner);
promise.set_value(std::move(response));
callback(std::move(future));
}
}
@@ -119,6 +135,18 @@ GenericClient::remove_pending_request(int64_t request_id)
return pending_requests_.erase(request_id) != 0u;
}
bool
GenericClient::remove_pending_request(const FutureAndRequestId & future)
{
return this->remove_pending_request(future.request_id);
}
bool
GenericClient::remove_pending_request(const SharedFutureAndRequestId & future)
{
return this->remove_pending_request(future.request_id);
}
std::optional<GenericClient::CallbackInfoVariant>
GenericClient::get_and_erase_pending_request(int64_t request_number)
{

View File

@@ -0,0 +1,172 @@
// Copyright 2024 Sony Group Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/generic_service.hpp"
namespace rclcpp
{
GenericService::GenericService(
std::shared_ptr<rcl_node_t> node_handle,
const std::string & service_name,
const std::string & service_type,
GenericServiceCallback any_callback,
rcl_service_options_t & service_options)
: ServiceBase(node_handle),
any_callback_(any_callback)
{
const rosidl_service_type_support_t * service_ts;
try {
ts_lib_ = get_typesupport_library(
service_type, "rosidl_typesupport_cpp");
service_ts = get_service_typesupport_handle(
service_type, "rosidl_typesupport_cpp", *ts_lib_);
auto request_type_support_intro = get_message_typesupport_handle(
service_ts->request_typesupport,
rosidl_typesupport_introspection_cpp::typesupport_identifier);
request_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
request_type_support_intro->data);
auto response_type_support_intro = get_message_typesupport_handle(
service_ts->response_typesupport,
rosidl_typesupport_introspection_cpp::typesupport_identifier);
response_members_ = static_cast<const rosidl_typesupport_introspection_cpp::MessageMembers *>(
response_type_support_intro->data);
} catch (std::runtime_error & err) {
RCLCPP_ERROR(
rclcpp::get_node_logger(node_handle_.get()).get_child("rclcpp"),
"Invalid service type: %s",
err.what());
throw rclcpp::exceptions::InvalidServiceTypeError(err.what());
}
// rcl does the static memory allocation here
service_handle_ = std::shared_ptr<rcl_service_t>(
new rcl_service_t, [handle = node_handle_, service_name](rcl_service_t * service)
{
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
RCLCPP_ERROR(
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
"Error in destruction of rcl service handle: %s",
rcl_get_error_string().str);
rcl_reset_error();
}
delete service;
});
*service_handle_.get() = rcl_get_zero_initialized_service();
rcl_ret_t ret = rcl_service_init(
service_handle_.get(),
node_handle.get(),
service_ts,
service_name.c_str(),
&service_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_SERVICE_NAME_INVALID) {
auto rcl_node_handle = get_rcl_node_handle();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
service_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
true);
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
}
TRACETOOLS_TRACEPOINT(
rclcpp_service_callback_added,
static_cast<const void *>(get_service_handle().get()),
static_cast<const void *>(&any_callback_));
#ifndef TRACETOOLS_DISABLED
any_callback_.register_callback_for_tracing();
#endif
}
bool
GenericService::take_request(
SharedRequest request_out,
rmw_request_id_t & request_id_out)
{
request_out = create_request();
return this->take_type_erased_request(request_out.get(), request_id_out);
}
std::shared_ptr<void>
GenericService::create_request()
{
Request request = new uint8_t[request_members_->size_of_];
request_members_->init_function(request, rosidl_runtime_cpp::MessageInitialization::ZERO);
return std::shared_ptr<void>(
request,
[this](void * p)
{
request_members_->fini_function(p);
delete[] reinterpret_cast<uint8_t *>(p);
});
}
std::shared_ptr<void>
GenericService::create_response()
{
Response response = new uint8_t[response_members_->size_of_];
response_members_->init_function(response, rosidl_runtime_cpp::MessageInitialization::ZERO);
return std::shared_ptr<void>(
response,
[this](void * p)
{
response_members_->fini_function(p);
delete[] reinterpret_cast<uint8_t *>(p);
});
}
std::shared_ptr<rmw_request_id_t>
GenericService::create_request_header()
{
return std::make_shared<rmw_request_id_t>();
}
void
GenericService::handle_request(
std::shared_ptr<rmw_request_id_t> request_header,
std::shared_ptr<void> request)
{
auto response = any_callback_.dispatch(
this->shared_from_this(), request_header, request, create_response());
if (response) {
send_response(*request_header, response);
}
}
void
GenericService::send_response(rmw_request_id_t & req_id, SharedResponse & response)
{
rcl_ret_t ret = rcl_send_response(get_service_handle().get(), &req_id, response.get());
if (ret == RCL_RET_TIMEOUT) {
RCLCPP_WARN(
node_logger_.get_child("rclcpp"),
"failed to send response to %s (timeout): %s",
this->get_service_name(), rcl_get_error_string().str);
rcl_reset_error();
return;
}
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send response");
}
}
} // namespace rclcpp

View File

@@ -46,8 +46,7 @@ GenericSubscription::handle_message(
"handle_message is not implemented for GenericSubscription");
}
void
GenericSubscription::handle_serialized_message(
void GenericSubscription::handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> & message,
const rclcpp::MessageInfo & message_info)
{

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

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

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");
}
}
@@ -544,9 +573,30 @@ SubscriptionBase::get_content_filter() const
// DYNAMIC TYPE ==================================================================================
bool
SubscriptionBase::take_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage & /*message_out*/,
rclcpp::MessageInfo & /*message_info_out*/)
rclcpp::dynamic_typesupport::DynamicMessage & message_out,
rclcpp::MessageInfo & message_info_out)
{
throw std::runtime_error("Unimplemented");
return false;
if (rmw_feature_supported(RMW_MIDDLEWARE_CAN_TAKE_DYNAMIC_MESSAGE)) {
rcl_ret_t ret = rcl_take_dynamic_message(
this->get_subscription_handle().get(),
&message_out.get_rosidl_dynamic_data(),
&message_info_out.get_rmw_message_info(),
nullptr);
if (RCL_RET_SUBSCRIPTION_TAKE_FAILED == ret) {
return false;
} else if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "Error when taking dynamic message");
}
} else { // Fall back to serialized conversion if direct dynamic message taking isn't supported
std::shared_ptr<rclcpp::SerializedMessage> serialized_msg = this->create_serialized_message();
if (!this->take_serialized(*serialized_msg.get(), message_info_out)) {
std::runtime_error("Couldn't take serialized message when attempting to take dynamic data!");
}
bool ret = message_out.deserialize(serialized_msg->get_rcl_serialized_message());
if (!ret) {
throw std::runtime_error("Couldn't convert serialized message to dynamic data!");
}
this->return_serialized_message(serialized_msg);
}
return true;
}

View File

@@ -54,54 +54,8 @@ Waitable::get_number_of_ready_guard_conditions()
return 0u;
}
std::shared_ptr<void>
Waitable::take_data_by_entity_id(size_t id)
{
(void)id;
throw std::runtime_error(
"Custom waitables should override take_data_by_entity_id "
"if they want to use it.");
}
bool
Waitable::exchange_in_use_by_wait_set_state(bool in_use_state)
{
return in_use_by_wait_set_.exchange(in_use_state);
}
void
Waitable::set_on_ready_callback(std::function<void(size_t, int)> callback)
{
(void)callback;
throw std::runtime_error(
"Custom waitables should override set_on_ready_callback "
"if they want to use it.");
}
void
Waitable::clear_on_ready_callback()
{
throw std::runtime_error(
"Custom waitables should override clear_on_ready_callback if they "
"want to use it and make sure to call it on the waitable destructor.");
}
bool
Waitable::is_ready(const rcl_wait_set_t & wait_set)
{
return this->is_ready(wait_set);
}
void
Waitable::add_to_wait_set(rcl_wait_set_t & wait_set)
{
this->add_to_wait_set(wait_set);
}
void
Waitable::execute(const std::shared_ptr<void> & data)
{
// note this const cast is only required to support a deprecated function
this->execute(const_cast<std::shared_ptr<void> &>(data));
}

View File

@@ -189,71 +189,6 @@ BENCHMARK_F(PerformanceTestExecutorSimple, multi_thread_executor_remove_node)(be
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
static_single_thread_executor_add_node)(benchmark::State & st)
{
rclcpp::executors::StaticSingleThreadedExecutor executor;
for (auto _ : st) {
(void)_;
executor.add_node(node);
st.PauseTiming();
executor.remove_node(node);
st.ResumeTiming();
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
static_single_thread_executor_remove_node)(benchmark::State & st)
{
rclcpp::executors::StaticSingleThreadedExecutor executor;
for (auto _ : st) {
(void)_;
st.PauseTiming();
executor.add_node(node);
st.ResumeTiming();
executor.remove_node(node);
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
static_single_thread_executor_spin_until_future_complete)(benchmark::State & st)
{
rclcpp::executors::StaticSingleThreadedExecutor executor;
// test success of an immediately finishing future
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
auto ret = executor.spin_until_future_complete(shared_future, 100ms);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
}
reset_heap_counters();
for (auto _ : st) {
(void)_;
// static_single_thread_executor has a special design. We need to add/remove the node each
// time you call spin
st.PauseTiming();
executor.add_node(node);
st.ResumeTiming();
ret = executor.spin_until_future_complete(shared_future, 100ms);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
break;
}
st.PauseTiming();
executor.remove_node(node);
st.ResumeTiming();
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
single_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
@@ -314,30 +249,6 @@ BENCHMARK_F(
}
}
BENCHMARK_F(
PerformanceTestExecutorSimple,
static_single_thread_executor_spin_node_until_future_complete)(benchmark::State & st)
{
rclcpp::executors::StaticSingleThreadedExecutor executor;
// test success of an immediately finishing future
std::promise<bool> promise;
std::future<bool> future = promise.get_future();
promise.set_value(true);
auto shared_future = future.share();
reset_heap_counters();
for (auto _ : st) {
(void)_;
auto ret = rclcpp::executors::spin_node_until_future_complete(
executor, node, shared_future, 1s);
if (ret != rclcpp::FutureReturnCode::SUCCESS) {
st.SkipWithError(rcutils_get_error_string().str);
break;
}
}
}
BENCHMARK_F(PerformanceTestExecutorSimple, spin_until_future_complete)(benchmark::State & st)
{
// test success of an immediately finishing future

View File

@@ -83,6 +83,18 @@ if(TARGET test_generic_client)
${test_msgs_TARGETS}
)
endif()
ament_add_gtest(test_generic_service test_generic_service.cpp)
ament_add_test_label(test_generic_service mimick)
if(TARGET test_generic_service)
target_link_libraries(test_generic_service ${PROJECT_NAME}
mimick
${rcl_interfaces_TARGETS}
rmw::rmw
rosidl_runtime_cpp::rosidl_runtime_cpp
rosidl_typesupport_cpp::rosidl_typesupport_cpp
${test_msgs_TARGETS}
)
endif()
ament_add_gtest(test_client_common test_client_common.cpp)
ament_add_test_label(test_client_common mimick)
if(TARGET test_client_common)
@@ -99,17 +111,6 @@ ament_add_gtest(test_create_subscription test_create_subscription.cpp)
if(TARGET test_create_subscription)
target_link_libraries(test_create_subscription ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
function(test_add_callback_groups_to_executor_for_rmw_implementation)
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
ament_add_gmock(test_add_callback_groups_to_executor${target_suffix} test_add_callback_groups_to_executor.cpp
ENV ${rmw_implementation_env_var}
TIMEOUT 120
)
if(TARGET test_add_callback_groups_to_executor${target_suffix})
target_link_libraries(test_add_callback_groups_to_executor${target_suffix} ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
endfunction()
call_for_each_rmw_implementation(test_add_callback_groups_to_executor_for_rmw_implementation)
ament_add_gtest(test_expand_topic_or_service_name test_expand_topic_or_service_name.cpp)
ament_add_test_label(test_expand_topic_or_service_name mimick)
if(TARGET test_expand_topic_or_service_name)
@@ -337,28 +338,6 @@ if(TARGET test_qos)
rmw::rmw
)
endif()
function(test_generic_pubsub_for_rmw_implementation)
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
ament_add_gmock(test_generic_pubsub${target_suffix} test_generic_pubsub.cpp
ENV ${rmw_implementation_env_var}
)
if(TARGET test_generic_pubsub${target_suffix})
target_link_libraries(test_generic_pubsub${target_suffix} ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
endif()
endfunction()
call_for_each_rmw_implementation(test_generic_pubsub_for_rmw_implementation)
function(test_qos_event_for_rmw_implementation)
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
ament_add_gmock(test_qos_event${target_suffix} test_qos_event.cpp
ENV ${rmw_implementation_env_var}
)
ament_add_test_label(test_qos_event${target_suffix} mimick)
if(TARGET test_qos_event${target_suffix})
target_link_libraries(test_qos_event${target_suffix} ${PROJECT_NAME} mimick rcutils::rcutils rmw::rmw ${test_msgs_TARGETS})
endif()
endfunction()
call_for_each_rmw_implementation(test_qos_event_for_rmw_implementation)
ament_add_gmock(test_qos_overriding_options test_qos_overriding_options.cpp)
if(TARGET test_qos_overriding_options)
@@ -483,6 +462,13 @@ if(TARGET test_interface_traits)
target_link_libraries(test_interface_traits ${PROJECT_NAME})
endif()
ament_add_gtest(test_reinitialized_timers
executors/test_reinitialized_timers.cpp
TIMEOUT 30)
if(TARGET test_reinitialized_timers)
target_link_libraries(test_reinitialized_timers ${PROJECT_NAME})
endif()
ament_add_gtest(
test_executors
executors/test_executors.cpp
@@ -501,7 +487,7 @@ ament_add_gtest(
executors/test_executors_timer_cancel_behavior.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors)
if(TARGET test_executors_timer_cancel_behavior)
target_link_libraries(test_executors_timer_cancel_behavior ${PROJECT_NAME} ${rosgraph_msgs_TARGETS})
endif()
@@ -510,7 +496,7 @@ ament_add_gtest(
executors/test_executors_callback_group_behavior.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors)
if(TARGET test_executors_callback_group_behavior)
target_link_libraries(test_executors_callback_group_behavior ${PROJECT_NAME})
endif()
@@ -519,7 +505,7 @@ ament_add_gtest(
executors/test_executors_intraprocess.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors)
if(TARGET test_executors_intraprocess)
target_link_libraries(test_executors_intraprocess ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
@@ -529,10 +515,20 @@ ament_add_gtest(
executors/test_waitable.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors)
if(TARGET test_executors_busy_waiting)
target_link_libraries(test_executors_busy_waiting ${PROJECT_NAME})
endif()
ament_add_gtest(
test_executors_warmup
executors/test_executors_warmup.cpp
executors/test_waitable.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors_warmup)
target_link_libraries(test_executors_warmup ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_static_single_threaded_executor executors/test_static_single_threaded_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
ament_add_test_label(test_static_single_threaded_executor mimick)
@@ -559,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()
@@ -650,16 +646,51 @@ if(TARGET test_graph_listener)
target_link_libraries(test_graph_listener ${PROJECT_NAME} mimick)
endif()
function(test_subscription_content_filter_for_rmw_implementation)
ament_add_gmock_executable(test_qos_event test_qos_event.cpp)
if(TARGET test_qos_event)
target_link_libraries(test_qos_event ${PROJECT_NAME} mimick rcutils::rcutils rmw::rmw ${test_msgs_TARGETS})
endif()
ament_add_gmock_executable(test_generic_pubsub test_generic_pubsub.cpp)
if(TARGET test_generic_pubsub)
target_link_libraries(test_generic_pubsub ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
endif()
ament_add_gmock_executable(test_add_callback_groups_to_executor test_add_callback_groups_to_executor.cpp)
if(TARGET test_add_callback_groups_to_executor)
target_link_libraries(test_add_callback_groups_to_executor ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gmock_executable(test_subscription_content_filter test_subscription_content_filter.cpp)
if(TARGET test_subscription_content_filter)
target_link_libraries(test_subscription_content_filter ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
endif()
function(test_on_all_rmws)
set(rmw_implementation_env_var RMW_IMPLEMENTATION=${rmw_implementation})
ament_add_gmock(test_subscription_content_filter${target_suffix}
test_subscription_content_filter.cpp
ament_add_gmock_test(test_qos_event
TEST_NAME test_qos_event${target_suffix}
ENV ${rmw_implementation_env_var}
)
ament_add_test_label(test_qos_event${target_suffix} mimick)
ament_add_gmock_test(test_generic_pubsub
TEST_NAME test_generic_pubsub${target_suffix}
ENV ${rmw_implementation_env_var}
)
ament_add_gmock_test(test_add_callback_groups_to_executor
TEST_NAME test_add_callback_groups_to_executor${target_suffix}
ENV ${rmw_implementation_env_var}
TIMEOUT 120
)
ament_add_gmock_test(test_subscription_content_filter
TEST_NAME test_subscription_content_filter${target_suffix}
ENV ${rmw_implementation_env_var}
TIMEOUT 120
)
ament_add_test_label(test_subscription_content_filter${target_suffix} mimick)
if(TARGET test_subscription_content_filter${target_suffix})
target_link_libraries(test_subscription_content_filter${target_suffix} ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
endif()
endfunction()
call_for_each_rmw_implementation(test_subscription_content_filter_for_rmw_implementation)
call_for_each_rmw_implementation(test_on_all_rmws)

View File

@@ -25,12 +25,37 @@
#include "rclcpp/executors/static_single_threaded_executor.hpp"
#include "rclcpp/executors/multi_threaded_executor.hpp"
// suppress deprecated StaticSingleThreadedExecutor warning
// we define an alias that explicitly indicates that this class is deprecated, while avoiding
// polluting a lot of files the gcc pragmas
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
using DeprecatedStaticSingleThreadedExecutor = rclcpp::executors::StaticSingleThreadedExecutor;
// remove warning suppression
#if !defined(_WIN32)
# pragma GCC diagnostic pop
#else // !defined(_WIN32)
# pragma warning(pop)
#endif
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
using ExecutorTypes =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor,
rclcpp::executors::StaticSingleThreadedExecutor,
DeprecatedStaticSingleThreadedExecutor,
rclcpp::experimental::executors::EventsExecutor>;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
class ExecutorTypeNames
{
@@ -46,10 +71,16 @@ public:
if (std::is_same<T, rclcpp::executors::MultiThreadedExecutor>()) {
return "MultiThreadedExecutor";
}
if (std::is_same<T, rclcpp::executors::StaticSingleThreadedExecutor>()) {
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
if (std::is_same<T, DeprecatedStaticSingleThreadedExecutor>()) {
return "StaticSingleThreadedExecutor";
}
#ifdef __clang__
# pragma clang diagnostic pop
#endif
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
return "EventsExecutor";

View File

@@ -479,14 +479,21 @@ TEST_F(TestEventsExecutor, test_default_incompatible_qos_callbacks)
const auto timeout = std::chrono::seconds(10);
ex.spin_until_future_complete(log_msgs_future, timeout);
EXPECT_EQ(
"New subscription discovered on topic '/test_topic', requesting incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
pub_log_msg);
EXPECT_EQ(
"New publisher discovered on topic '/test_topic', offering incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
sub_log_msg);
rclcpp::QoSCheckCompatibleResult qos_compatible = rclcpp::qos_check_compatible(
publisher->get_actual_qos(), subscription->get_actual_qos());
if (qos_compatible.compatibility == rclcpp::QoSCompatibility::Error) {
EXPECT_EQ(
"New subscription discovered on topic '/test_topic', requesting incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
pub_log_msg);
EXPECT_EQ(
"New publisher discovered on topic '/test_topic', offering incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
sub_log_msg);
} else {
EXPECT_EQ("", pub_log_msg);
EXPECT_EQ("", sub_log_msg);
}
rcutils_logging_set_output_handler(original_output_handler);
}

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;
@@ -480,20 +480,14 @@ TYPED_TEST(TestExecutors, spin_some)
// The purpose of this test is to check that the ExecutorT.spin_some() method:
// - does not continue executing after max_duration has elapsed
TYPED_TEST(TestExecutors, spin_some_max_duration)
// TODO(wjwwood): The `StaticSingleThreadedExecutor`
// do not properly implement max_duration (it seems), so disable this test
// for them in the meantime.
// see: https://github.com/ros2/rclcpp/issues/2462
TYPED_TEST(TestExecutorsStable, spinSomeMaxDuration)
{
using ExecutorType = TypeParam;
// TODO(wjwwood): The `StaticSingleThreadedExecutor`
// do not properly implement max_duration (it seems), so disable this test
// for them in the meantime.
// see: https://github.com/ros2/rclcpp/issues/2462
if (
std::is_same<ExecutorType, rclcpp::executors::StaticSingleThreadedExecutor>())
{
GTEST_SKIP();
}
// Use an isolated callback group to avoid interference from any housekeeping
// items that may be in the default callback group of the node.
constexpr bool automatically_add_to_executor_with_node = false;
@@ -647,13 +641,6 @@ TYPED_TEST(TestExecutors, testSpinUntilFutureCompleteInterrupted)
TYPED_TEST(TestExecutors, testRaceConditionAddNode)
{
using ExecutorType = TypeParam;
// rmw_connextdds doesn't support events-executor
if (
std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>() &&
std::string(rmw_get_implementation_identifier()).find("rmw_connextdds") == 0)
{
GTEST_SKIP();
}
// Spawn some threads to do some heavy work
std::atomic<bool> should_cancel = false;
@@ -674,20 +661,20 @@ TYPED_TEST(TestExecutors, testRaceConditionAddNode)
}
// Create an executor
auto executor = std::make_shared<ExecutorType>();
ExecutorType executor;
// Start spinning
auto executor_thread = std::thread(
[executor]() {
executor->spin();
[&executor]() {
executor.spin();
});
// Add a node to the executor
executor->add_node(this->node);
executor.add_node(this->node);
// Cancel the executor (make sure that it's already spinning first)
while (!executor->is_spinning() && rclcpp::ok()) {
while (!executor.is_spinning() && rclcpp::ok()) {
continue;
}
executor->cancel();
executor.cancel();
// Try to join the thread after cancelling the executor
// This is the "test". We want to make sure that we can still cancel the executor
@@ -727,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();
@@ -744,6 +738,13 @@ 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();
@@ -833,7 +834,7 @@ TEST(TestExecutors, testSpinWithNonDefaultContext)
rclcpp::shutdown(non_default_context);
}
TYPED_TEST(TestExecutors, release_ownership_entity_after_spinning_cancel)
TYPED_TEST(TestExecutors, releaseOwnershipEntityAfterSpinningCancel)
{
using ExecutorType = TypeParam;
ExecutorType executor;
@@ -856,3 +857,36 @@ 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();
}

View File

@@ -47,9 +47,6 @@ public:
auto waitable_interfaces = node->get_node_waitables_interface();
waitable = std::make_shared<TestWaitable>();
waitable_interfaces->add_waitable(waitable, callback_group);
executor = std::make_shared<T>();
executor->add_callback_group(callback_group, node->get_node_base_interface());
}
void TearDown() override
@@ -108,7 +105,6 @@ public:
rclcpp::CallbackGroup::SharedPtr callback_group;
std::shared_ptr<TestWaitable> waitable;
std::chrono::steady_clock::time_point start_time;
std::shared_ptr<T> executor;
bool has_executed;
};
@@ -116,10 +112,16 @@ TYPED_TEST_SUITE(TestBusyWaiting, ExecutorTypes, ExecutorTypeNames);
TYPED_TEST(TestBusyWaiting, test_spin_all)
{
using ExecutorType = TypeParam;
ExecutorType executor;
executor.add_callback_group(
this->callback_group,
this->node->get_node_base_interface());
this->set_up_and_trigger_waitable();
auto start_time = std::chrono::steady_clock::now();
this->executor->spin_all(this->max_duration);
executor.spin_all(this->max_duration);
this->check_for_busy_waits(start_time);
// this should get the initial trigger, and the follow up from in the callback
ASSERT_EQ(this->waitable->get_count(), 2u);
@@ -127,10 +129,16 @@ TYPED_TEST(TestBusyWaiting, test_spin_all)
TYPED_TEST(TestBusyWaiting, test_spin_some)
{
using ExecutorType = TypeParam;
ExecutorType executor;
executor.add_callback_group(
this->callback_group,
this->node->get_node_base_interface());
this->set_up_and_trigger_waitable();
auto start_time = std::chrono::steady_clock::now();
this->executor->spin_some(this->max_duration);
executor.spin_some(this->max_duration);
this->check_for_busy_waits(start_time);
// this should get the inital trigger, but not the follow up in the callback
ASSERT_EQ(this->waitable->get_count(), 1u);
@@ -138,6 +146,12 @@ TYPED_TEST(TestBusyWaiting, test_spin_some)
TYPED_TEST(TestBusyWaiting, test_spin)
{
using ExecutorType = TypeParam;
ExecutorType executor;
executor.add_callback_group(
this->callback_group,
this->node->get_node_base_interface());
std::condition_variable cv;
std::mutex cv_m;
bool first_check_passed = false;
@@ -151,8 +165,8 @@ TYPED_TEST(TestBusyWaiting, test_spin)
});
auto start_time = std::chrono::steady_clock::now();
std::thread t([this]() {
this->executor->spin();
std::thread t([&executor]() {
executor.spin();
});
// wait until thread has started (first execute of waitable)
@@ -172,7 +186,7 @@ TYPED_TEST(TestBusyWaiting, test_spin)
}
EXPECT_EQ(this->waitable->get_count(), 2u);
this->executor->cancel();
executor.cancel();
t.join();
this->check_for_busy_waits(start_time);

View File

@@ -282,12 +282,20 @@ public:
T executor;
};
#if !defined(_WIN32)
# ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
# endif
#endif
using MainExecutorTypes =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor,
rclcpp::executors::StaticSingleThreadedExecutor>;
DeprecatedStaticSingleThreadedExecutor>;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
// TODO(@fujitatomoya): this test excludes EventExecutor because it does not
// support simulation time used for this test to relax the racy condition.
// See more details for https://github.com/ros2/rclcpp/issues/2457.

View File

@@ -0,0 +1,244 @@
// Copyright 2024 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* This test checks all implementations of rclcpp::executor to check they pass they basic API
* tests. Anything specific to any executor in particular should go in a separate test file.
*/
#include <gtest/gtest.h>
#include <chrono>
#include <cstddef>
#include <memory>
#include <string>
#include <type_traits>
#include <utility>
#include "rclcpp/rclcpp.hpp"
#include "test_msgs/msg/empty.hpp"
#include "./executor_types.hpp"
using namespace std::chrono_literals;
template<typename T>
class TestExecutorsWarmup : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
}
void TearDown()
{
rclcpp::shutdown();
}
};
TYPED_TEST_SUITE(TestExecutorsWarmup, ExecutorTypes, ExecutorTypeNames);
// This test verifies that spin_all is correctly collecting work multiple times
// even when one of the items of work is a notifier waitable event and thus results in
// rebuilding the entities collection.
// When spin_all goes back to collect more work, it should see the ready items from
// the new added entities
TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup)
{
using ExecutorType = TypeParam;
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
// Add node to the executor before creating the entities
executor.add_node(node);
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback));
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// We need to select a duration that is greater than
// the time taken to refresh the entities collection and rebuild the waitset.
// spin-all is expected to process the notifier waitable event, rebuild the collection,
// and then collect more work, finding the subscription message event.
// This duration has been selected empirically.
executor.spin_all(std::chrono::milliseconds(500));
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}
// Same test as `spin_all_doesnt_require_warmup`, but uses a callback group
// This test reproduces the bug reported by https://github.com/ros2/rclcpp/issues/2589
TYPED_TEST(TestExecutorsWarmup, spin_all_doesnt_require_warmup_with_cbgroup)
{
using ExecutorType = TypeParam;
// 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
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
auto callback_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive,
false);
// Add callback group to the executor before creating the entities
executor.add_callback_group(callback_group, node->get_node_base_interface());
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
rclcpp::SubscriptionOptions sub_options;
sub_options.callback_group = callback_group;
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback), sub_options);
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// We need to select a duration that is greater than
// the time taken to refresh the entities collection and rebuild the waitset.
// spin-all is expected to process the notifier waitable event, rebuild the collection,
// and then collect more work, finding the subscription message event.
// This duration has been selected empirically.
executor.spin_all(std::chrono::milliseconds(500));
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}
TYPED_TEST(TestExecutorsWarmup, spin_some_doesnt_require_warmup)
{
using ExecutorType = TypeParam;
// TODO(alsora): currently only the events-executor passes this test.
// Enable single-threaded and multi-threaded executors
// when https://github.com/ros2/rclcpp/pull/2595 gets merged
if (
!std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>())
{
GTEST_SKIP();
}
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
// Add node to the executor before creating the entities
executor.add_node(node);
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback));
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// NOTE: intra-process communication is enabled, so the subscription will immediately see
// the new message, no risk of race conditions where spin_some gets called before the
// message has been delivered.
executor.spin_some();
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}
TYPED_TEST(TestExecutorsWarmup, spin_some_doesnt_require_warmup_with_cbgroup)
{
using ExecutorType = TypeParam;
// TODO(alsora): currently only the events-executor passes this test.
// Enable single-threaded and multi-threaded executors
// when https://github.com/ros2/rclcpp/pull/2595 gets merged
if (
!std::is_same<ExecutorType, rclcpp::experimental::executors::EventsExecutor>())
{
GTEST_SKIP();
}
ExecutorType executor;
// Enable intra-process to guarantee deterministic and synchronous delivery of the message / event
auto node_options = rclcpp::NodeOptions().use_intra_process_comms(true);
auto node = std::make_shared<rclcpp::Node>("test_node", node_options);
auto callback_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive,
false);
// Add callback group to the executor before creating the entities
executor.add_callback_group(callback_group, node->get_node_base_interface());
// Create entities, this will produce a notifier waitable event, telling the executor to refresh
// the entities collection
auto publisher = node->create_publisher<test_msgs::msg::Empty>("test_topic", rclcpp::QoS(10));
size_t callback_count = 0;
auto callback = [&callback_count](test_msgs::msg::Empty::ConstSharedPtr) {callback_count++;};
rclcpp::SubscriptionOptions sub_options;
sub_options.callback_group = callback_group;
auto subscription =
node->create_subscription<test_msgs::msg::Empty>(
"test_topic", rclcpp::QoS(10), std::move(callback), sub_options);
ASSERT_EQ(callback_count, 0u);
// Publish a message so that the new entities (i.e. the subscriber) already have work to do
publisher->publish(test_msgs::msg::Empty());
// NOTE: intra-process communication is enabled, so the subscription will immediately see
// the new message, no risk of race conditions where spin_some gets called before the
// message has been delivered.
executor.spin_some();
// Verify that the callback is called as part of the spin above
EXPECT_EQ(callback_count, 1u);
}

View File

@@ -32,6 +32,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
constexpr std::chrono::milliseconds PERIOD_MS = 1000ms;

View File

@@ -0,0 +1,88 @@
// Copyright 2024 iRobot Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <chrono>
#include <cstddef>
#include <memory>
#include <thread>
#include "rclcpp/rclcpp.hpp"
#include "./executor_types.hpp"
template<typename ExecutorType>
class TestTimersLifecycle : public testing::Test
{
public:
void SetUp() override {rclcpp::init(0, nullptr);}
void TearDown() override {rclcpp::shutdown();}
};
TYPED_TEST_SUITE(TestTimersLifecycle, ExecutorTypes, ExecutorTypeNames);
TYPED_TEST(TestTimersLifecycle, timers_lifecycle_reinitialized_object)
{
using ExecutorType = TypeParam;
ExecutorType executor;
auto timers_period = std::chrono::milliseconds(50);
auto node = std::make_shared<rclcpp::Node>("test_node");
executor.add_node(node);
size_t count_1 = 0;
auto timer_1 = rclcpp::create_timer(
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_1]() {count_1++;});
size_t count_2 = 0;
auto timer_2 = rclcpp::create_timer(
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_2]() {count_2++;});
{
std::thread executor_thread([&executor]() {executor.spin();});
while (count_2 < 10u) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
executor.cancel();
executor_thread.join();
EXPECT_GE(count_2, 10u);
EXPECT_LE(count_2 - count_1, 1u);
}
count_1 = 0;
timer_1 = rclcpp::create_timer(
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_1]() {count_1++;});
count_2 = 0;
timer_2 = rclcpp::create_timer(
node, node->get_clock(), rclcpp::Duration(timers_period), [&count_2]() {count_2++;});
{
std::thread executor_thread([&executor]() {executor.spin();});
while (count_2 < 10u) {
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
executor.cancel();
executor_thread.join();
EXPECT_GE(count_2, 10u);
EXPECT_LE(count_2 - count_1, 1u);
}
}

View File

@@ -20,12 +20,13 @@
#include <stdexcept>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/executors.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rclcpp/executors.hpp"
#include "test_msgs/srv/empty.hpp"
#include "./executor_types.hpp"
#include "../../mocking_utils/patch.hpp"
#include "../../utils/rclcpp_gtest_macros.hpp"
@@ -46,7 +47,15 @@ public:
};
TEST_F(TestStaticSingleThreadedExecutor, add_callback_group_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
@@ -61,7 +70,15 @@ TEST_F(TestStaticSingleThreadedExecutor, add_callback_group_trigger_guard_failed
}
TEST_F(TestStaticSingleThreadedExecutor, add_node_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
{
@@ -74,7 +91,15 @@ TEST_F(TestStaticSingleThreadedExecutor, add_node_trigger_guard_failed) {
}
TEST_F(TestStaticSingleThreadedExecutor, remove_callback_group_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
rclcpp::CallbackGroup::SharedPtr cb_group = node->create_callback_group(
rclcpp::CallbackGroupType::MutuallyExclusive);
@@ -92,7 +117,15 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_callback_group_trigger_guard_fai
}
TEST_F(TestStaticSingleThreadedExecutor, remove_node_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
{
@@ -105,7 +138,15 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_node_failed) {
}
TEST_F(TestStaticSingleThreadedExecutor, remove_node_trigger_guard_failed) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
executor.add_node(node);
@@ -120,7 +161,15 @@ TEST_F(TestStaticSingleThreadedExecutor, remove_node_trigger_guard_failed) {
}
TEST_F(TestStaticSingleThreadedExecutor, execute_service) {
rclcpp::executors::StaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-declarations"
#endif
DeprecatedStaticSingleThreadedExecutor executor;
#ifdef __clang__
# pragma clang diagnostic pop
#endif
auto node = std::make_shared<rclcpp::Node>("node", "ns");
executor.add_node(node);

View File

@@ -38,6 +38,7 @@ protected:
{
node.reset();
wrapped_node.reset();
rclcpp::shutdown();
}
static rclcpp::Node::SharedPtr node;

View File

@@ -47,9 +47,13 @@ constexpr char absolute_namespace[] = "/ns";
class TestNodeGraph : public ::testing::Test
{
public:
void SetUp()
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
node_ = std::make_shared<rclcpp::Node>(node_name, node_namespace);
// This dynamic cast is not necessary for the unittests, but instead is used to ensure
@@ -59,7 +63,7 @@ public:
ASSERT_NE(nullptr, node_graph_);
}
void TearDown()
static void TearDownTestCase()
{
rclcpp::shutdown();
}

View File

@@ -31,13 +31,13 @@ public:
void add_to_wait_set(rcl_wait_set_t &) override {}
bool is_ready(const rcl_wait_set_t &) override {return false;}
std::shared_ptr<void>
take_data() override
{
return nullptr;
}
std::shared_ptr<void> take_data() override {return nullptr;}
void execute(const std::shared_ptr<void> &) override {}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
};
class TestNodeWaitables : public ::testing::Test

View File

@@ -51,13 +51,13 @@ public:
return test_waitable_result;
}
std::shared_ptr<void>
take_data() override
{
return nullptr;
}
std::shared_ptr<void> take_data() override {return nullptr;}
void execute(const std::shared_ptr<void> &) override {}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
};
struct RclWaitSetSizes

View File

@@ -29,6 +29,8 @@
#include "rclcpp/executor.hpp"
#include "rclcpp/rclcpp.hpp"
#include "./executors/executor_types.hpp"
using namespace std::chrono_literals;
template<typename T>
@@ -48,48 +50,8 @@ public:
template<typename T>
class TestAddCallbackGroupsToExecutorStable : public TestAddCallbackGroupsToExecutor<T> {};
using ExecutorTypes =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor,
rclcpp::executors::StaticSingleThreadedExecutor,
rclcpp::experimental::executors::EventsExecutor>;
class ExecutorTypeNames
{
public:
template<typename T>
static std::string GetName(int idx)
{
(void)idx;
if (std::is_same<T, rclcpp::executors::SingleThreadedExecutor>()) {
return "SingleThreadedExecutor";
}
if (std::is_same<T, rclcpp::executors::MultiThreadedExecutor>()) {
return "MultiThreadedExecutor";
}
if (std::is_same<T, rclcpp::executors::StaticSingleThreadedExecutor>()) {
return "StaticSingleThreadedExecutor";
}
if (std::is_same<T, rclcpp::experimental::executors::EventsExecutor>()) {
return "EventsExecutor";
}
return "";
}
};
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutor, ExecutorTypes, ExecutorTypeNames);
// StaticSingleThreadedExecutor is not included in these tests for now
using StandardExecutors =
::testing::Types<
rclcpp::executors::SingleThreadedExecutor,
rclcpp::executors::MultiThreadedExecutor,
rclcpp::experimental::executors::EventsExecutor>;
TYPED_TEST_SUITE(TestAddCallbackGroupsToExecutorStable, StandardExecutors, ExecutorTypeNames);
/*

View File

@@ -431,7 +431,7 @@ TYPED_TEST(TestAllClientTypesWithServer, client_qos)
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);

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

@@ -34,6 +34,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
void

View File

@@ -27,6 +27,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
TEST_F(TestFindWeakNodes, allocator_strategy_with_weak_nodes) {

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;
@@ -105,7 +108,7 @@ TEST_F(TestGenericClient, construction_and_destruction) {
ASSERT_THROW(
{
auto client = node->create_generic_client("test_service", "test_msgs/srv/InvalidType");
}, std::runtime_error);
}, rclcpp::exceptions::InvalidServiceTypeError);
}
}
@@ -228,3 +231,69 @@ TEST_F(TestGenericClientSub, construction_and_destruction) {
}, rclcpp::exceptions::InvalidServiceNameError);
}
}
TEST_F(TestGenericClientSub, async_send_request_with_request) {
const std::string service_name = "test_service";
int64_t expected_change = 1111;
auto client = node->create_generic_client(service_name, "test_msgs/srv/BasicTypes");
auto callback = [&expected_change](
const test_msgs::srv::BasicTypes::Request::SharedPtr request,
test_msgs::srv::BasicTypes::Response::SharedPtr response) {
response->int64_value = request->int64_value + expected_change;
};
auto service =
node->create_service<test_msgs::srv::BasicTypes>(service_name, std::move(callback));
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
ASSERT_TRUE(client->service_is_ready());
test_msgs::srv::BasicTypes::Request request;
request.int64_value = 12345678;
auto future = client->async_send_request(static_cast<void *>(&request));
rclcpp::spin_until_future_complete(
node->get_node_base_interface(), future, std::chrono::seconds(5));
ASSERT_TRUE(future.valid());
auto get_untyped_response = future.get();
auto typed_response =
static_cast<test_msgs::srv::BasicTypes::Response *>(get_untyped_response.get());
EXPECT_EQ(typed_response->int64_value, (request.int64_value + expected_change));
}
TEST_F(TestGenericClientSub, async_send_request_with_request_and_callback) {
const std::string service_name = "test_service";
int64_t expected_change = 2222;
auto client = node->create_generic_client(service_name, "test_msgs/srv/BasicTypes");
auto server_callback = [&expected_change](
const test_msgs::srv::BasicTypes::Request::SharedPtr request,
test_msgs::srv::BasicTypes::Response::SharedPtr response) {
response->int64_value = request->int64_value + expected_change;
};
auto service =
node->create_service<test_msgs::srv::BasicTypes>(service_name, std::move(server_callback));
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
ASSERT_TRUE(client->service_is_ready());
test_msgs::srv::BasicTypes::Request request;
request.int64_value = 12345678;
auto client_callback = [&request, &expected_change](
rclcpp::GenericClient::SharedFuture future) {
auto untyped_response = future.get();
auto typed_response =
static_cast<test_msgs::srv::BasicTypes::Response *>(untyped_response.get());
EXPECT_EQ(typed_response->int64_value, (request.int64_value + expected_change));
};
auto future =
client->async_send_request(static_cast<void *>(&request), client_callback);
rclcpp::spin_until_future_complete(
node->get_node_base_interface(), future, std::chrono::seconds(5));
}

View File

@@ -89,9 +89,9 @@ public:
T2 message;
write_message(data, message);
rclcpp::Serialization<T2> ser;
rclcpp::Serialization<T2> serialization_support;
SerializedMessage result;
ser.serialize_message(&message, &result);
serialization_support.serialize_message(&message, &result);
return result;
}

View File

@@ -0,0 +1,422 @@
// Copyright 2024 Sony Group Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <gtest/gtest.h>
#include <chrono>
#include <cstddef>
#include <functional>
#include <memory>
#include <stdexcept>
#include <string>
#include <utility>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "../mocking_utils/patch.hpp"
#include "../utils/rclcpp_gtest_macros.hpp"
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "test_msgs/srv/empty.hpp"
#include "test_msgs/srv/basic_types.hpp"
using namespace std::chrono_literals;
class TestGenericService : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
node = std::make_shared<rclcpp::Node>("test_node", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
};
class TestGenericServiceSub : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
void SetUp()
{
node = std::make_shared<rclcpp::Node>("test_node", "/ns");
subnode = node->create_sub_node("sub_ns");
}
void TearDown()
{
node.reset();
}
rclcpp::Node::SharedPtr node;
rclcpp::Node::SharedPtr subnode;
};
/*
Testing service construction and destruction.
*/
TEST_F(TestGenericService, construction_and_destruction) {
auto callback = [](
rclcpp::GenericService::SharedRequest,
rclcpp::GenericService::SharedResponse) {};
{
auto generic_service = node->create_generic_service(
"test_generic_service", "rcl_interfaces/srv/ListParameters", callback);
EXPECT_NE(nullptr, generic_service->get_service_handle());
const rclcpp::ServiceBase * const_service_base = generic_service.get();
EXPECT_NE(nullptr, const_service_base->get_service_handle());
}
{
ASSERT_THROW(
{
auto generic_service = node->create_generic_service(
"invalid_service?", "test_msgs/srv/Empty", callback);
}, rclcpp::exceptions::InvalidServiceNameError);
}
{
ASSERT_THROW(
{
auto generic_service = node->create_generic_service(
"test_generic_service", "test_msgs/srv/NotExist", callback);
}, rclcpp::exceptions::InvalidServiceTypeError);
}
}
/*
Testing service construction and destruction for subnodes.
*/
TEST_F(TestGenericServiceSub, construction_and_destruction) {
auto callback = [](
rclcpp::GenericService::SharedRequest,
rclcpp::GenericService::SharedResponse) {};
{
auto generic_service = subnode->create_generic_service(
"test_generic_service", "rcl_interfaces/srv/ListParameters", callback);
EXPECT_STREQ(generic_service->get_service_name(), "/ns/sub_ns/test_generic_service");
}
{
ASSERT_THROW(
{
auto generic_service = subnode->create_generic_service(
"invalid_service?", "test_msgs/srv/Empty", callback);
}, rclcpp::exceptions::InvalidServiceNameError);
}
{
ASSERT_THROW(
{
auto generic_service = subnode->create_generic_service(
"test_generic_service", "test_msgs/srv/NotExist", callback);
}, rclcpp::exceptions::InvalidServiceTypeError);
}
}
TEST_F(TestGenericService, construction_and_destruction_rcl_errors) {
auto callback = [](
rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_service_init, RCL_RET_ERROR);
// reset() isn't necessary for this exception, it just avoids unused return value warning
EXPECT_THROW(
node->create_generic_service("service", "test_msgs/srv/Empty", callback).reset(),
rclcpp::exceptions::RCLError);
}
{
// reset() is required for this one
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_service_fini, RCL_RET_ERROR);
EXPECT_NO_THROW(
node->create_generic_service("service", "test_msgs/srv/Empty", callback).reset());
}
}
TEST_F(TestGenericService, generic_service_take_request) {
auto callback = [](
rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
auto generic_service =
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
{
auto request_id = generic_service->create_request_header();
auto request = generic_service->create_request();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_request, RCL_RET_OK);
EXPECT_TRUE(generic_service->take_request(request, *request_id.get()));
}
{
auto request_id = generic_service->create_request_header();
auto request = generic_service->create_request();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_request, RCL_RET_SERVICE_TAKE_FAILED);
EXPECT_FALSE(generic_service->take_request(request, *request_id.get()));
}
{
auto request_id = generic_service->create_request_header();
auto request = generic_service->create_request();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_take_request, RCL_RET_ERROR);
EXPECT_THROW(
generic_service->take_request(request, *request_id.get()), rclcpp::exceptions::RCLError);
}
}
TEST_F(TestGenericService, generic_service_send_response) {
auto callback = [](
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
auto generic_service =
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
{
auto request_id = generic_service->create_request_header();
auto response = generic_service->create_response();
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_response, RCL_RET_OK);
EXPECT_NO_THROW(generic_service->send_response(*request_id.get(), response));
}
{
auto request_id = generic_service->create_request_header();
auto response = generic_service->create_response();
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_send_response, RCL_RET_ERROR);
EXPECT_THROW(
generic_service->send_response(*request_id.get(), response),
rclcpp::exceptions::RCLError);
}
}
/*
Testing on_new_request callbacks.
*/
TEST_F(TestGenericService, generic_service_on_new_request_callback) {
auto server_callback = [](
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {FAIL();};
rclcpp::ServicesQoS service_qos;
service_qos.keep_last(3);
auto generic_service = node->create_generic_service(
"~/test_service", "test_msgs/srv/Empty", server_callback, service_qos);
std::atomic<size_t> c1 {0};
auto increase_c1_cb = [&c1](size_t count_msgs) {c1 += count_msgs;};
generic_service->set_on_new_request_callback(increase_c1_cb);
auto client = node->create_client<test_msgs::srv::Empty>(
"~/test_service", service_qos);
{
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
client->async_send_request(request);
}
auto start = std::chrono::steady_clock::now();
do {
std::this_thread::sleep_for(100ms);
} while (c1 == 0 && std::chrono::steady_clock::now() - start < 10s);
EXPECT_EQ(c1.load(), 1u);
std::atomic<size_t> c2 {0};
auto increase_c2_cb = [&c2](size_t count_msgs) {c2 += count_msgs;};
generic_service->set_on_new_request_callback(increase_c2_cb);
{
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
client->async_send_request(request);
}
start = std::chrono::steady_clock::now();
do {
std::this_thread::sleep_for(100ms);
} while (c2 == 0 && std::chrono::steady_clock::now() - start < 10s);
EXPECT_EQ(c1.load(), 1u);
EXPECT_EQ(c2.load(), 1u);
generic_service->clear_on_new_request_callback();
{
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
client->async_send_request(request);
client->async_send_request(request);
client->async_send_request(request);
}
std::atomic<size_t> c3 {0};
auto increase_c3_cb = [&c3](size_t count_msgs) {c3 += count_msgs;};
generic_service->set_on_new_request_callback(increase_c3_cb);
start = std::chrono::steady_clock::now();
do {
std::this_thread::sleep_for(100ms);
} while (c3 < 3 && std::chrono::steady_clock::now() - start < 10s);
EXPECT_EQ(c1.load(), 1u);
EXPECT_EQ(c2.load(), 1u);
EXPECT_EQ(c3.load(), 3u);
std::function<void(size_t)> invalid_cb = nullptr;
EXPECT_THROW(generic_service->set_on_new_request_callback(invalid_cb), std::invalid_argument);
}
TEST_F(TestGenericService, rcl_service_response_publisher_get_actual_qos_error) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_service_response_publisher_get_actual_qos, nullptr);
auto callback = [](
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
auto generic_service =
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
RCLCPP_EXPECT_THROW_EQ(
generic_service->get_response_publisher_actual_qos(),
std::runtime_error("failed to get service's response publisher qos settings: error not set"));
}
TEST_F(TestGenericService, rcl_service_request_subscription_get_actual_qos_error) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_service_request_subscription_get_actual_qos, nullptr);
auto callback = [](
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
auto generic_service =
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback);
RCLCPP_EXPECT_THROW_EQ(
generic_service->get_request_subscription_actual_qos(),
std::runtime_error("failed to get service's request subscription qos settings: error not set"));
}
TEST_F(TestGenericService, generic_service_qos) {
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);
auto callback = [](
const rclcpp::GenericService::SharedRequest, rclcpp::GenericService::SharedResponse) {};
auto generic_service =
node->create_generic_service("test_service", "test_msgs/srv/Empty", callback, qos_profile);
auto rs_qos = generic_service->get_request_subscription_actual_qos();
auto rp_qos = generic_service->get_response_publisher_actual_qos();
EXPECT_EQ(qos_profile, rp_qos);
// Lifespan has no meaning for subscription/readers
rs_qos.lifespan(qos_profile.lifespan());
EXPECT_EQ(qos_profile, rs_qos);
}
TEST_F(TestGenericService, generic_service_qos_depth) {
uint64_t server_cb_count_ = 0;
auto server_callback = [&](
const rclcpp::GenericService::SharedRequest,
rclcpp::GenericService::SharedResponse) {server_cb_count_++;};
auto server_node = std::make_shared<rclcpp::Node>("server_node", "/ns");
rclcpp::QoS server_qos_profile(2);
auto generic_service = server_node->create_generic_service(
"test_qos_depth", "test_msgs/srv/Empty", std::move(server_callback), server_qos_profile);
rclcpp::QoS client_qos_profile(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_default));
auto client = node->create_client<test_msgs::srv::Empty>("test_qos_depth", client_qos_profile);
::testing::AssertionResult request_result = ::testing::AssertionSuccess();
auto request = std::make_shared<test_msgs::srv::Empty::Request>();
auto client_callback = [&request_result](
rclcpp::Client<test_msgs::srv::Empty>::SharedFuture future_response) {
if (nullptr == future_response.get()) {
request_result = ::testing::AssertionFailure() << "Future response was null";
}
};
uint64_t client_requests = 5;
for (uint64_t i = 0; i < client_requests; i++) {
client->async_send_request(request, client_callback);
std::this_thread::sleep_for(10ms);
}
auto start = std::chrono::steady_clock::now();
while ((server_cb_count_ < server_qos_profile.depth()) &&
(std::chrono::steady_clock::now() - start) < 1s)
{
rclcpp::spin_some(server_node);
std::this_thread::sleep_for(1ms);
}
// Spin an extra time to check if server QoS depth has been ignored,
// so more server responses might be processed than expected.
rclcpp::spin_some(server_node);
EXPECT_EQ(server_cb_count_, server_qos_profile.depth());
}
TEST_F(TestGenericService, generic_service_and_client) {
const std::string service_name = "test_service";
const std::string service_type = "test_msgs/srv/BasicTypes";
int64_t expected_change = 87654321;
auto callback = [&expected_change](
const rclcpp::GenericService::SharedRequest request,
rclcpp::GenericService::SharedResponse response) {
auto typed_request = static_cast<test_msgs::srv::BasicTypes_Request *>(request.get());
auto typed_response = static_cast<test_msgs::srv::BasicTypes_Response *>(response.get());
typed_response->int64_value = typed_request->int64_value + expected_change;
};
auto generic_service = node->create_generic_service(service_name, service_type, callback);
auto client = node->create_client<test_msgs::srv::BasicTypes>(service_name);
ASSERT_TRUE(client->wait_for_service(std::chrono::seconds(5)));
ASSERT_TRUE(client->service_is_ready());
auto request = std::make_shared<test_msgs::srv::BasicTypes::Request>();
request->int64_value = 12345678;
auto generic_client_callback = [&request, &expected_change](
std::shared_future<test_msgs::srv::BasicTypes_Response::SharedPtr> future) {
auto response = future.get();
EXPECT_EQ(response->int64_value, (request->int64_value + expected_change));
};
auto future =
client->async_send_request(request, generic_client_callback);
rclcpp::spin_until_future_complete(
node->get_node_base_interface(), future, std::chrono::seconds(5));
}

View File

@@ -27,6 +27,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
/*

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()
@@ -205,10 +206,12 @@ public:
return false;
}
rclcpp::QoS qos_profile;
std::string topic_name;
uint64_t intra_process_publisher_id_;
IntraProcessManagerWeakPtr weak_ipm_;
private:
std::string topic_name;
rclcpp::QoS qos_profile;
};
template<typename T, typename Alloc = std::allocator<void>>
@@ -223,8 +226,8 @@ public:
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>)
explicit Publisher(rclcpp::QoS qos = rclcpp::QoS(10))
: PublisherBase(qos)
explicit Publisher(const std::string & topic, const rclcpp::QoS & qos)
: PublisherBase(topic, qos)
{
auto allocator = std::make_shared<Alloc>();
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
@@ -258,9 +261,9 @@ public:
explicit SubscriptionIntraProcessBase(
rclcpp::Context::SharedPtr context,
const std::string & topic = "topic",
rclcpp::QoS qos = rclcpp::QoS(10))
: qos_profile(qos), topic_name(topic)
const std::string & topic,
const rclcpp::QoS & qos)
: topic_name(topic), qos_profile(qos)
{
(void)context;
}
@@ -292,8 +295,8 @@ public:
size_t
available_capacity() const = 0;
rclcpp::QoS qos_profile;
std::string topic_name;
rclcpp::QoS qos_profile;
};
template<
@@ -307,8 +310,8 @@ class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
explicit SubscriptionIntraProcessBuffer(rclcpp::QoS qos)
: SubscriptionIntraProcessBase(nullptr, "topic", qos), take_shared_method(false)
explicit SubscriptionIntraProcessBuffer(const std::string & topic, const rclcpp::QoS & qos)
: SubscriptionIntraProcessBase(nullptr, topic, qos), take_shared_method(false)
{
buffer = std::make_unique<rclcpp::experimental::buffers::mock::IntraProcessBuffer<MessageT>>();
}
@@ -375,8 +378,8 @@ class SubscriptionIntraProcess : public SubscriptionIntraProcessBuffer<
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
explicit SubscriptionIntraProcess(rclcpp::QoS qos = rclcpp::QoS(10))
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(qos)
explicit SubscriptionIntraProcess(const std::string & topic, const rclcpp::QoS & qos)
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(topic, qos)
{
}
};
@@ -466,12 +469,11 @@ TEST(TestIntraProcessManager, add_pub_sub) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10).best_effort());
auto p2 = std::make_shared<PublisherT>(rclcpp::QoS(10).best_effort());
p2->topic_name = "different_topic_name";
auto p2 = std::make_shared<PublisherT>("different_topic_name", rclcpp::QoS(10).best_effort());
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).best_effort());
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10).best_effort());
auto p1_id = ipm->add_publisher(p1);
auto p2_id = ipm->add_publisher(p2);
@@ -480,24 +482,42 @@ TEST(TestIntraProcessManager, add_pub_sub) {
bool unique_ids = p1_id != p2_id && p2_id != s1_id;
ASSERT_TRUE(unique_ids);
// p1 has 1 subcription, s1
size_t p1_subs = ipm->get_subscription_count(p1_id);
// p2 has 0 subscriptions
size_t p2_subs = ipm->get_subscription_count(p2_id);
// Non-existent publisher_id has 0 subscriptions
size_t non_existing_pub_subs = ipm->get_subscription_count(42);
ASSERT_EQ(1u, p1_subs);
ASSERT_EQ(0u, p2_subs);
ASSERT_EQ(0u, non_existing_pub_subs);
auto p3 = std::make_shared<PublisherT>(rclcpp::QoS(10).reliable());
auto p3 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10).reliable());
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).reliable());
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10).reliable());
// s2 may be able to communicate with p1 depending on the RMW
auto s2_id = ipm->template add_subscription<MessageT>(s2);
// p3 can definitely communicate with s2, may be able to communicate with s1 depending on the RMW
auto p3_id = ipm->add_publisher(p3);
// p1 definitely matches subscription s1, since the topic name and QoS match exactly.
// If the RMW can match best-effort publishers to reliable subscriptions (like Zenoh can),
// then p1 will also match s2.
p1_subs = ipm->get_subscription_count(p1_id);
// No subscriptions with a topic name of "different_topic_name" were added.
p2_subs = ipm->get_subscription_count(p2_id);
// On all current RMWs (DDS and Zenoh), a reliable publisher like p3 can communicate with both
// reliable and best-effort subscriptions (s1 and s2).
size_t p3_subs = ipm->get_subscription_count(p3_id);
ASSERT_EQ(1u, p1_subs);
rclcpp::QoSCheckCompatibleResult qos_compatible =
rclcpp::qos_check_compatible(p1->get_actual_qos(), s2->get_actual_qos());
if (qos_compatible.compatibility == rclcpp::QoSCompatibility::Error) {
ASSERT_EQ(1u, p1_subs);
} else {
ASSERT_EQ(2u, p1_subs);
}
ASSERT_EQ(0u, p2_subs);
ASSERT_EQ(2u, p3_subs);
@@ -528,11 +548,11 @@ TEST(TestIntraProcessManager, single_subscription) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>();
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s1->take_shared_method = false;
auto s1_id = ipm->template add_subscription<MessageT>(s1);
@@ -543,7 +563,7 @@ TEST(TestIntraProcessManager, single_subscription) {
ASSERT_EQ(original_message_pointer, received_message_pointer_1);
ipm->remove_subscription(s1_id);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s2->take_shared_method = true;
auto s2_id = ipm->template add_subscription<MessageT>(s2);
(void)s2_id;
@@ -582,15 +602,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>();
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s1->take_shared_method = false;
auto s1_id = ipm->template add_subscription<MessageT>(s1);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s2->take_shared_method = false;
auto s2_id = ipm->template add_subscription<MessageT>(s2);
@@ -606,11 +626,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
ipm->remove_subscription(s1_id);
ipm->remove_subscription(s2_id);
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
auto s3 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s3->take_shared_method = true;
auto s3_id = ipm->template add_subscription<MessageT>(s3);
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
auto s4 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s4->take_shared_method = true;
auto s4_id = ipm->template add_subscription<MessageT>(s4);
@@ -625,11 +645,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
ipm->remove_subscription(s3_id);
ipm->remove_subscription(s4_id);
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
auto s5 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s5->take_shared_method = false;
auto s5_id = ipm->template add_subscription<MessageT>(s5);
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
auto s6 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s6->take_shared_method = false;
auto s6_id = ipm->template add_subscription<MessageT>(s6);
@@ -645,12 +665,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
ipm->remove_subscription(s5_id);
ipm->remove_subscription(s6_id);
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
auto s7 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s7->take_shared_method = true;
auto s7_id = ipm->template add_subscription<MessageT>(s7);
(void)s7_id;
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
auto s8 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s8->take_shared_method = true;
auto s8_id = ipm->template add_subscription<MessageT>(s8);
(void)s8_id;
@@ -688,15 +708,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>();
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(10));
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
auto s1 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s1->take_shared_method = true;
auto s1_id = ipm->template add_subscription<MessageT>(s1);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
auto s2 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s2->take_shared_method = false;
auto s2_id = ipm->template add_subscription<MessageT>(s2);
@@ -711,15 +731,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
ipm->remove_subscription(s1_id);
ipm->remove_subscription(s2_id);
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
auto s3 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s3->take_shared_method = false;
auto s3_id = ipm->template add_subscription<MessageT>(s3);
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
auto s4 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s4->take_shared_method = false;
auto s4_id = ipm->template add_subscription<MessageT>(s4);
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
auto s5 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s5->take_shared_method = true;
auto s5_id = ipm->template add_subscription<MessageT>(s5);
@@ -743,19 +763,19 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
ipm->remove_subscription(s4_id);
ipm->remove_subscription(s5_id);
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
auto s6 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s6->take_shared_method = true;
auto s6_id = ipm->template add_subscription<MessageT>(s6);
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
auto s7 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s7->take_shared_method = true;
auto s7_id = ipm->template add_subscription<MessageT>(s7);
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
auto s8 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s8->take_shared_method = false;
auto s8_id = ipm->template add_subscription<MessageT>(s8);
auto s9 = std::make_shared<SubscriptionIntraProcessT>();
auto s9 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s9->take_shared_method = false;
auto s9_id = ipm->template add_subscription<MessageT>(s9);
@@ -781,12 +801,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
ipm->remove_subscription(s8_id);
ipm->remove_subscription(s9_id);
auto s10 = std::make_shared<SubscriptionIntraProcessT>();
auto s10 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s10->take_shared_method = false;
auto s10_id = ipm->template add_subscription<MessageT>(s10);
(void)s10_id;
auto s11 = std::make_shared<SubscriptionIntraProcessT>();
auto s11 = std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(10));
s11->take_shared_method = true;
auto s11_id = ipm->template add_subscription<MessageT>(s11);
(void)s11_id;
@@ -831,10 +851,12 @@ TEST(TestIntraProcessManager, lowest_available_capacity) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).best_effort());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).best_effort());
auto s1 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).best_effort());
auto s1 =
std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(history_depth).best_effort());
auto s2 =
std::make_shared<SubscriptionIntraProcessT>("topic", rclcpp::QoS(history_depth).best_effort());
auto p1_id = ipm->add_publisher(p1);
p1->set_intra_process_manager(p1_id, ipm);
@@ -902,7 +924,7 @@ TEST(TestIntraProcessManager, transient_local_invalid_buffer) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).transient_local());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).transient_local());
ASSERT_THROW(
{
@@ -926,14 +948,14 @@ TEST(TestIntraProcessManager, transient_local) {
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).transient_local());
auto p1 = std::make_shared<PublisherT>("topic", rclcpp::QoS(history_depth).transient_local());
auto s1 =
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
auto s2 =
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
auto s3 =
std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(history_depth).transient_local());
auto s1 = std::make_shared<SubscriptionIntraProcessT>(
"topic", rclcpp::QoS(history_depth).transient_local());
auto s2 = std::make_shared<SubscriptionIntraProcessT>(
"topic", rclcpp::QoS(history_depth).transient_local());
auto s3 = std::make_shared<SubscriptionIntraProcessT>(
"topic", rclcpp::QoS(history_depth).transient_local());
s1->take_shared_method = false;
s2->take_shared_method = true;

View File

@@ -40,6 +40,11 @@ public:
std::shared_ptr<void> take_data() override {return nullptr;}
void execute(const std::shared_ptr<void> &) override {}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
};
class TestMemoryStrategy : public ::testing::Test

View File

@@ -32,6 +32,11 @@ protected:
const int argc = sizeof(args) / sizeof(const char *);
rclcpp::init(argc, args);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
TEST_F(TestNodeWithGlobalArgs, local_arguments_before_global) {

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

@@ -31,6 +31,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
TEST_F(TestParameter, construct_destruct) {

View File

@@ -60,6 +60,11 @@ protected:
node_with_option.reset();
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
// "start_type_description_service" and "use_sim_time"
const uint64_t builtin_param_count = 2;
rclcpp::Node::SharedPtr node;

View File

@@ -107,6 +107,11 @@ protected:
param_handler.reset();
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
rcl_interfaces::msg::ParameterEvent::SharedPtr same_node_int;
rcl_interfaces::msg::ParameterEvent::SharedPtr same_node_double;
rcl_interfaces::msg::ParameterEvent::SharedPtr diff_node_int;

View File

@@ -14,6 +14,7 @@
#include <gtest/gtest.h>
#include <future>
#include <chrono>
#include <memory>
#include <string>
@@ -38,9 +39,7 @@ class TestPublisher : public ::testing::Test
public:
static void SetUpTestCase()
{
if (!rclcpp::ok()) {
rclcpp::init(0, nullptr);
}
rclcpp::init(0, nullptr);
}
protected:
@@ -54,6 +53,11 @@ protected:
node.reset();
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
rclcpp::Node::SharedPtr node;
};
@@ -81,6 +85,7 @@ class TestPublisherSub : public ::testing::Test
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
@@ -94,6 +99,11 @@ protected:
node.reset();
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
rclcpp::Node::SharedPtr node;
rclcpp::Node::SharedPtr subnode;
};
@@ -178,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
*/
@@ -423,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) {
@@ -702,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;
@@ -763,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

@@ -17,6 +17,7 @@
#include <string>
#include "rclcpp/qos.hpp"
#include "rmw/rmw.h"
#include "rmw/types.h"
@@ -241,13 +242,20 @@ TEST(TestQoS, qos_check_compatible)
// TODO(jacobperron): programmatically check if current RMW is one of the officially
// supported DDS middlewares before running the following tests
// If the RMW implementation is rmw_zenoh_cpp, we do not expect any QoS incompatibilities.
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
// Incompatible
{
rclcpp::QoS pub_qos = rclcpp::QoS(1).best_effort();
rclcpp::QoS sub_qos = rclcpp::QoS(1).reliable();
rclcpp::QoSCheckCompatibleResult ret = rclcpp::qos_check_compatible(pub_qos, sub_qos);
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Error);
EXPECT_FALSE(ret.reason.empty());
if (rmw_implementation_str == "rmw_zenoh_cpp") {
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Ok);
EXPECT_TRUE(ret.reason.empty());
} else {
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Error);
EXPECT_FALSE(ret.reason.empty());
}
}
// Warn of possible incompatibility
@@ -255,7 +263,12 @@ TEST(TestQoS, qos_check_compatible)
rclcpp::SystemDefaultsQoS pub_qos;
rclcpp::QoS sub_qos = rclcpp::QoS(1).reliable();
rclcpp::QoSCheckCompatibleResult ret = rclcpp::qos_check_compatible(pub_qos, sub_qos);
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Warning);
EXPECT_FALSE(ret.reason.empty());
if (rmw_implementation_str == "rmw_zenoh_cpp") {
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Ok);
EXPECT_TRUE(ret.reason.empty());
} else {
EXPECT_EQ(ret.compatibility, rclcpp::QoSCompatibility::Warning);
EXPECT_FALSE(ret.reason.empty());
}
}
}

View File

@@ -33,13 +33,14 @@ using namespace std::chrono_literals;
class TestQosEvent : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
// We initialize and shutdown the context (and hence also the rmw_context),
// for each test case to reset the ROS graph for each test case.
rclcpp::init(0, nullptr);
rmw_implementation_str = std::string(rmw_get_implementation_identifier());
node = std::make_shared<rclcpp::Node>("test_qos_event", "/ns");
message_callback = [node = node.get()](test_msgs::msg::Empty::ConstSharedPtr /*msg*/) {
@@ -50,8 +51,10 @@ protected:
void TearDown()
{
node.reset();
rclcpp::shutdown();
}
std::string rmw_implementation_str;
static constexpr char topic_name[] = "test_topic";
rclcpp::Node::SharedPtr node;
std::function<void(test_msgs::msg::Empty::ConstSharedPtr)> message_callback;
@@ -70,28 +73,29 @@ TEST_F(TestQosEvent, test_publisher_constructor)
auto publisher = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, options);
// options arg with one of the callbacks
options.event_callbacks.deadline_callback =
[node = node.get()](rclcpp::QOSDeadlineOfferedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Offered deadline missed - total %d (delta %d)",
event.total_count, event.total_count_change);
};
publisher = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, options);
// options arg with two of the callbacks
options.event_callbacks.liveliness_callback =
[node = node.get()](rclcpp::QOSLivelinessLostInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Liveliness lost - total %d (delta %d)",
event.total_count, event.total_count_change);
};
publisher = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, options);
if (rmw_implementation_str != "rmw_zenoh_cpp") {
// options arg with one of the callbacks
options.event_callbacks.deadline_callback =
[node = node.get()](rclcpp::QOSDeadlineOfferedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Offered deadline missed - total %d (delta %d)",
event.total_count, event.total_count_change);
};
publisher = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, options);
// options arg with two of the callbacks
options.event_callbacks.liveliness_callback =
[node = node.get()](rclcpp::QOSLivelinessLostInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Liveliness lost - total %d (delta %d)",
event.total_count, event.total_count_change);
};
publisher = node->create_publisher<test_msgs::msg::Empty>(
topic_name, 10, options);
}
// options arg with three of the callbacks
options.event_callbacks.incompatible_qos_callback =
[node = node.get()](rclcpp::QOSOfferedIncompatibleQoSInfo & event) {
@@ -109,35 +113,38 @@ TEST_F(TestQosEvent, test_publisher_constructor)
*/
TEST_F(TestQosEvent, test_subscription_constructor)
{
// While rmw_zenoh does not support Deadline/LivelinessChanged events,
// it does support IncompatibleQoS
rclcpp::SubscriptionOptions options;
// options arg with no callbacks
auto subscription = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, options);
// options arg with one of the callbacks
options.event_callbacks.deadline_callback =
[node = node.get()](rclcpp::QOSDeadlineRequestedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Requested deadline missed - total %d (delta %d)",
event.total_count, event.total_count_change);
};
subscription = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, options);
// options arg with two of the callbacks
options.event_callbacks.liveliness_callback =
[node = node.get()](rclcpp::QOSLivelinessChangedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Liveliness changed - alive %d (delta %d), not alive %d (delta %d)",
event.alive_count, event.alive_count_change,
event.not_alive_count, event.not_alive_count_change);
};
subscription = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, options);
if (rmw_implementation_str != "rmw_zenoh_cpp") {
// options arg with one of the callbacks
options.event_callbacks.deadline_callback =
[node = node.get()](rclcpp::QOSDeadlineRequestedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Requested deadline missed - total %d (delta %d)",
event.total_count, event.total_count_change);
};
subscription = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, options);
// options arg with two of the callbacks
options.event_callbacks.liveliness_callback =
[node = node.get()](rclcpp::QOSLivelinessChangedInfo & event) {
RCLCPP_INFO(
node->get_logger(),
"Liveliness changed - alive %d (delta %d), not alive %d (delta %d)",
event.alive_count, event.alive_count_change,
event.not_alive_count, event.not_alive_count_change);
};
subscription = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, options);
}
// options arg with three of the callbacks
options.event_callbacks.incompatible_qos_callback =
[node = node.get()](rclcpp::QOSRequestedIncompatibleQoSInfo & event) {
@@ -204,14 +211,19 @@ TEST_F(TestQosEvent, test_default_incompatible_qos_callbacks)
const auto timeout = std::chrono::seconds(10);
ex.spin_until_future_complete(log_msgs_future, timeout);
EXPECT_EQ(
"New subscription discovered on topic '/ns/test_topic', requesting incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
pub_log_msg);
EXPECT_EQ(
"New publisher discovered on topic '/ns/test_topic', offering incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
sub_log_msg);
if (rmw_implementation_str == "rmw_zenoh_cpp") {
EXPECT_EQ(rclcpp::QoSCompatibility::Ok,
qos_check_compatible(qos_profile_publisher, qos_profile_subscription).compatibility);
} else {
EXPECT_EQ(
"New subscription discovered on topic '/ns/test_topic', requesting incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
pub_log_msg);
EXPECT_EQ(
"New publisher discovered on topic '/ns/test_topic', offering incompatible QoS. "
"No messages will be sent to it. Last incompatible policy: DURABILITY_QOS_POLICY",
sub_log_msg);
}
rcutils_logging_set_output_handler(original_output_handler);
}
@@ -223,7 +235,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 = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
const rcl_publisher_event_type_t event_type = rmw_implementation_str == "rmw_zenoh_cpp" ?
RCL_PUBLISHER_MATCHED : RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
{
// Logs error and returns
@@ -260,13 +273,16 @@ TEST_F(TestQosEvent, construct_destruct_rcl_error) {
}
TEST_F(TestQosEvent, execute) {
if (rmw_implementation_str == "rmw_zenoh_cpp") {
GTEST_SKIP();
}
auto publisher = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10);
auto rcl_handle = publisher->get_publisher_handle();
bool handler_callback_executed = false;
// This callback requires some type of parameter, but it could be anything
auto callback = [&handler_callback_executed](int) {handler_callback_executed = true;};
rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
const rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
callback, rcl_publisher_event_init, rcl_handle, event_type);
@@ -292,8 +308,9 @@ TEST_F(TestQosEvent, add_to_wait_set) {
// This callback requires some type of parameter, but it could be anything
auto callback = [](int) {};
rcl_publisher_event_type_t event_type = RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
const rcl_publisher_event_type_t event_type = rmw_implementation_str == "rmw_zenoh_cpp" ?
RCL_PUBLISHER_MATCHED : RCL_PUBLISHER_OFFERED_DEADLINE_MISSED;
rclcpp::EventHandler<decltype(callback), decltype(rcl_handle)> handler(
callback, rcl_publisher_event_init, rcl_handle, event_type);
EXPECT_EQ(1u, handler.get_number_of_ready_events());
@@ -314,6 +331,10 @@ TEST_F(TestQosEvent, add_to_wait_set) {
TEST_F(TestQosEvent, test_on_new_event_callback)
{
if (rmw_implementation_str == "rmw_zenoh_cpp") {
GTEST_SKIP();
}
auto offered_deadline = rclcpp::Duration(std::chrono::milliseconds(1));
auto requested_deadline = rclcpp::Duration(std::chrono::milliseconds(2));
@@ -359,18 +380,19 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
auto sub = node->create_subscription<test_msgs::msg::Empty>(topic_name, 10, message_callback);
auto dummy_cb = [](size_t count_events) {(void)count_events;};
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
if (rmw_implementation_str != "rmw_zenoh_cpp") {
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
EXPECT_NO_THROW(
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
EXPECT_NO_THROW(
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_OFFERED_DEADLINE_MISSED));
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_LIVELINESS_LOST));
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_LIVELINESS_LOST));
EXPECT_NO_THROW(
EXPECT_NO_THROW(
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_LIVELINESS_LOST));
}
EXPECT_NO_THROW(
pub->set_on_new_qos_event_callback(dummy_cb, RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS));
@@ -383,18 +405,19 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
EXPECT_NO_THROW(
pub->clear_on_new_qos_event_callback(RCL_PUBLISHER_MATCHED));
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
if (rmw_implementation_str == "rmw_zenoh_cpp") {
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED));
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_LIVELINESS_CHANGED));
}
EXPECT_NO_THROW(
sub->set_on_new_qos_event_callback(dummy_cb, RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS));
@@ -407,24 +430,26 @@ TEST_F(TestQosEvent, test_invalid_on_new_event_callback)
EXPECT_NO_THROW(
sub->clear_on_new_qos_event_callback(RCL_SUBSCRIPTION_MATCHED));
std::function<void(size_t)> invalid_cb;
if (rmw_implementation_str != "rmw_zenoh_cpp") {
std::function<void(size_t)> invalid_cb;
rclcpp::SubscriptionOptions sub_options;
sub_options.event_callbacks.deadline_callback = [](auto) {};
sub = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, sub_options);
rclcpp::SubscriptionOptions sub_options;
sub_options.event_callbacks.deadline_callback = [](auto) {};
sub = node->create_subscription<test_msgs::msg::Empty>(
topic_name, 10, message_callback, sub_options);
EXPECT_THROW(
sub->set_on_new_qos_event_callback(invalid_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED),
std::invalid_argument);
EXPECT_THROW(
sub->set_on_new_qos_event_callback(invalid_cb, RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED),
std::invalid_argument);
rclcpp::PublisherOptions pub_options;
pub_options.event_callbacks.deadline_callback = [](auto) {};
pub = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10, pub_options);
rclcpp::PublisherOptions pub_options;
pub_options.event_callbacks.deadline_callback = [](auto) {};
pub = node->create_publisher<test_msgs::msg::Empty>(topic_name, 10, pub_options);
EXPECT_THROW(
pub->set_on_new_qos_event_callback(invalid_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED),
std::invalid_argument);
EXPECT_THROW(
pub->set_on_new_qos_event_callback(invalid_cb, RCL_PUBLISHER_OFFERED_DEADLINE_MISSED),
std::invalid_argument);
}
}
TEST_F(TestQosEvent, test_pub_matched_event_by_set_event_callback)

View File

@@ -123,7 +123,7 @@ TEST_F(TestServiceSub, construction_and_destruction) {
{
ASSERT_THROW(
{
auto service = node->create_service<rcl_interfaces::srv::ListParameters>(
auto service = subnode->create_service<rcl_interfaces::srv::ListParameters>(
"invalid_service?", callback);
}, rclcpp::exceptions::InvalidServiceNameError);
}
@@ -335,7 +335,7 @@ TEST_F(TestService, rcl_service_request_subscription_get_actual_qos_error) {
TEST_F(TestService, server_qos) {
rclcpp::ServicesQoS qos_profile;
qos_profile.liveliness(rclcpp::LivelinessPolicy::Automatic);
rclcpp::Duration duration(std::chrono::nanoseconds(1));
rclcpp::Duration duration(std::chrono::milliseconds(1));
qos_profile.deadline(duration);
qos_profile.lifespan(duration);
qos_profile.liveliness_lease_duration(duration);

View File

@@ -92,9 +92,16 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
request->set__int64_value(42);
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
auto future = client->async_send_request(request);
ASSERT_EQ(
@@ -124,6 +131,20 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
ASSERT_THAT(
client_gid_arr,
testing::Eq(event_map[ServiceEventInfo::REQUEST_SENT]->info.client_gid));
// TODO(@fujitatomoya): Remove this if statement once rmw implementations support test.
// rmw_cyclonedds_cpp does not pass this test requirement for now.
// See more details for https://github.com/ros2/rmw/issues/357
if (std::string(rmw_get_implementation_identifier()).find("rmw_cyclonedds_cpp") != 0) {
ASSERT_THAT(
client_gid_arr,
testing::Eq(event_map[ServiceEventInfo::REQUEST_RECEIVED]->info.client_gid));
ASSERT_THAT(
client_gid_arr,
testing::Eq(event_map[ServiceEventInfo::RESPONSE_SENT]->info.client_gid));
}
ASSERT_THAT(
client_gid_arr,
testing::Eq(event_map[ServiceEventInfo::RESPONSE_RECEIVED]->info.client_gid));
ASSERT_EQ(
event_map[ServiceEventInfo::REQUEST_SENT]->info.sequence_number,
@@ -149,9 +170,11 @@ TEST_F(TestServiceIntrospection, service_introspection_nominal)
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
{
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
ASSERT_EQ(sub->get_publisher_count(), 0);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
@@ -169,9 +192,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 1 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 1u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -186,9 +216,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_OFF);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_OFF);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 1 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 1u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -203,9 +240,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -221,9 +265,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_events)
TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_content)
{
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
size_t tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
auto request = std::make_shared<BasicTypes::Request>();
request->set__bool_value(true);
@@ -245,9 +296,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -278,9 +336,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_METADATA);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(
@@ -311,9 +376,16 @@ TEST_F(TestServiceIntrospection, service_introspection_enable_disable_event_cont
events.clear();
client->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
service->configure_introspection(
node->get_clock(), rclcpp::SystemDefaultsQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
node->get_clock(), rclcpp::ServicesQoS(), RCL_SERVICE_INTROSPECTION_CONTENTS);
// Wait for the introspection to attach to our subscription
tries = 1000;
while (this->sub->get_publisher_count() < 2 && tries-- > 0) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
ASSERT_EQ(sub->get_publisher_count(), 2u);
future = client->async_send_request(request);
ASSERT_EQ(

View File

@@ -30,14 +30,7 @@
#include "test_msgs/msg/basic_types.hpp"
#ifdef RMW_IMPLEMENTATION
# define CLASSNAME_(NAME, SUFFIX) NAME ## __ ## SUFFIX
# define CLASSNAME(NAME, SUFFIX) CLASSNAME_(NAME, SUFFIX)
#else
# define CLASSNAME(NAME, SUFFIX) NAME
#endif
class CLASSNAME (TestContentFilterSubscription, RMW_IMPLEMENTATION) : public ::testing::Test
class TestContentFilterSubscription : public ::testing::Test
{
public:
static void SetUpTestCase()
@@ -113,7 +106,8 @@ bool operator==(const test_msgs::msg::BasicTypes & m1, const test_msgs::msg::Bas
m1.uint64_value == m2.uint64_value;
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), is_cft_enabled) {
TEST_F(TestContentFilterSubscription, is_cft_enabled)
{
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_subscription_is_cft_enabled, false);
@@ -127,7 +121,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), is_cft_enab
}
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content_filter_error) {
TEST_F(TestContentFilterSubscription, get_content_filter_error)
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_subscription_get_content_filter, RCL_RET_ERROR);
@@ -137,7 +132,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content
rclcpp::exceptions::RCLError);
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content_filter_error) {
TEST_F(TestContentFilterSubscription, set_content_filter_error)
{
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_subscription_set_content_filter, RCL_RET_ERROR);
@@ -148,7 +144,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content
rclcpp::exceptions::RCLError);
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content_filter) {
TEST_F(TestContentFilterSubscription, get_content_filter)
{
rclcpp::ContentFilterOptions options;
if (sub->is_cft_enabled()) {
@@ -164,7 +161,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), get_content
}
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content_filter) {
TEST_F(TestContentFilterSubscription, set_content_filter)
{
if (sub->is_cft_enabled()) {
EXPECT_NO_THROW(
sub->set_content_filter(filter_expression_init, expression_parameters_2));
@@ -175,7 +173,13 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), set_content
}
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_get_begin) {
TEST_F(TestContentFilterSubscription, content_filter_get_begin)
{
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
if (rmw_implementation_str == "rmw_zenoh_cpp") {
GTEST_SKIP();
}
using namespace std::chrono_literals;
{
test_msgs::msg::BasicTypes msg;
@@ -217,7 +221,13 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
}
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_get_later) {
TEST_F(TestContentFilterSubscription, content_filter_get_later)
{
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
if (rmw_implementation_str == "rmw_zenoh_cpp") {
GTEST_SKIP();
}
using namespace std::chrono_literals;
{
test_msgs::msg::BasicTypes msg;
@@ -264,7 +274,13 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
}
}
TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_filter_reset) {
TEST_F(TestContentFilterSubscription, content_filter_reset)
{
std::string rmw_implementation_str = std::string(rmw_get_implementation_identifier());
if (rmw_implementation_str == "rmw_zenoh_cpp") {
GTEST_SKIP();
}
using namespace std::chrono_literals;
{
test_msgs::msg::BasicTypes msg;
@@ -311,11 +327,8 @@ TEST_F(CLASSNAME(TestContentFilterSubscription, RMW_IMPLEMENTATION), content_fil
}
}
TEST_F(
CLASSNAME(
TestContentFilterSubscription,
RMW_IMPLEMENTATION), create_two_content_filters_with_same_topic_name_and_destroy) {
TEST_F(TestContentFilterSubscription, create_two_content_filters_with_same_topic_name_and_destroy)
{
// Create another content filter
auto options = rclcpp::SubscriptionOptions();

View File

@@ -51,6 +51,11 @@ protected:
node.reset();
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
rclcpp::Node::SharedPtr node;
};
@@ -60,7 +65,7 @@ TEST_F(TestSubscriptionOptions, topic_statistics_options_default_and_set) {
EXPECT_EQ(options.topic_stats_options.state, rclcpp::TopicStatisticsState::NodeDefault);
EXPECT_EQ(options.topic_stats_options.publish_topic, defaultPublishTopic);
EXPECT_EQ(options.topic_stats_options.publish_period, 1s);
EXPECT_EQ(options.topic_stats_options.qos, rclcpp::SystemDefaultsQoS());
EXPECT_EQ(options.topic_stats_options.qos, rclcpp::SystemDefaultsQoS().keep_last(10));
options.topic_stats_options.state = rclcpp::TopicStatisticsState::Enable;
options.topic_stats_options.publish_topic = "topic_statistics";

View File

@@ -212,6 +212,9 @@ TEST_F(TestTimeSource, ROS_time_valid_attach_detach) {
ts.attachNode(node);
EXPECT_FALSE(ros_clock->ros_time_is_active());
ts.attachClock(ros_clock);
EXPECT_FALSE(ros_clock->ros_time_is_active());
ts.detachClock(ros_clock);
EXPECT_FALSE(ros_clock->ros_time_is_active());
}

View File

@@ -178,11 +178,11 @@ MOCKING_UTILS_BOOL_OPERATOR_RETURNS_FALSE(rcl_guard_condition_options_t, <)
TEST(TestUtilities, test_context_init_shutdown_fails) {
{
auto context = std::make_shared<rclcpp::contexts::DefaultContext>();
auto context_fail_init = std::make_shared<rclcpp::contexts::DefaultContext>();
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_init, RCL_RET_ERROR);
EXPECT_THROW(context_fail_init->init(0, nullptr), rclcpp::exceptions::RCLError);
EXPECT_FALSE(context_fail_init->is_valid());
}
{
@@ -190,6 +190,7 @@ TEST(TestUtilities, test_context_init_shutdown_fails) {
auto mock = mocking_utils::patch_and_return(
"lib:rclcpp", rcl_logging_configure_with_output_handler, RCL_RET_ERROR);
EXPECT_THROW(context_fail_init->init(0, nullptr), rclcpp::exceptions::RCLError);
EXPECT_FALSE(context_fail_init->is_valid());
}
{

View File

@@ -31,6 +31,11 @@ protected:
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
/*

View File

@@ -343,7 +343,6 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_for_message_no
uint64_t message_age_count{0};
uint64_t message_period_count{0};
std::set<std::string> received_metrics;
for (const auto & msg : received_messages) {
if (msg.metrics_source == kMessageAgeSourceLabel) {
message_age_count++;

View File

@@ -52,16 +52,18 @@ public:
: is_ready_(false) {}
void add_to_wait_set(rcl_wait_set_t &) override {}
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
std::shared_ptr<void> take_data() override {return nullptr;}
void
execute(const std::shared_ptr<void> &) override {}
void execute(const std::shared_ptr<void> &) override {}
void set_is_ready(bool value) {is_ready_ = value;}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
private:
bool is_ready_;
};

View File

@@ -52,16 +52,18 @@ public:
: is_ready_(false) {}
void add_to_wait_set(rcl_wait_set_t &) override {}
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
std::shared_ptr<void> take_data() override {return nullptr;}
void
execute(const std::shared_ptr<void> &) override {}
void execute(const std::shared_ptr<void> &) override {}
void set_is_ready(bool value) {is_ready_ = value;}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
private:
bool is_ready_;
};

View File

@@ -61,14 +61,17 @@ public:
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
std::shared_ptr<void> take_data() override {return nullptr;}
void
execute(const std::shared_ptr<void> & data) override {(void)data;}
void execute(const std::shared_ptr<void> &) override {}
void set_is_ready(bool value) {is_ready_ = value;}
void set_add_to_wait_set(bool value) {add_to_wait_set_ = value;}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
private:
bool is_ready_;
bool add_to_wait_set_;

View File

@@ -52,16 +52,18 @@ public:
: is_ready_(false) {}
void add_to_wait_set(rcl_wait_set_t &) override {}
bool is_ready(const rcl_wait_set_t &) override {return is_ready_;}
std::shared_ptr<void> take_data() override {return nullptr;}
void
execute(const std::shared_ptr<void> &) override {}
void execute(const std::shared_ptr<void> &) override {}
void set_is_ready(bool value) {is_ready_ = value;}
void set_on_ready_callback(std::function<void(size_t, int)>) override {}
void clear_on_ready_callback() override {}
std::shared_ptr<void> take_data_by_entity_id(size_t) override {return nullptr;}
private:
bool is_ready_;
};

View File

@@ -3,6 +3,28 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29.3.0 (2024-12-20)
-------------------
* Make ament_cmake a buildtool dependency (`#2689 <https://github.com/ros2/rclcpp/issues/2689>`_)
* Contributors: Nathan Wiebe Neufeldt
29.2.0 (2024-11-25)
-------------------
29.1.0 (2024-11-20)
-------------------
* Fix documentation typo in server_goal_handle.hpp (`#2669 <https://github.com/ros2/rclcpp/issues/2669>`_)
* Contributors: YR
29.0.0 (2024-10-03)
-------------------
* Increase the timeout for the cppcheck on rclcpp_action. (`#2640 <https://github.com/ros2/rclcpp/issues/2640>`_)
* add smart pointer macros definitions to action server and client base classes (`#2631 <https://github.com/ros2/rclcpp/issues/2631>`_)
* Contributors: Alberto Soragna, Chris Lalancette
28.3.3 (2024-07-29)
-------------------
28.3.2 (2024-07-24)
-------------------

View File

@@ -135,3 +135,8 @@ if(BUILD_TESTING)
endif()
ament_package()
if(TEST cppcheck)
# must set the property after ament_package()
set_tests_properties(cppcheck PROPERTIES TIMEOUT 600)
endif()

View File

@@ -62,6 +62,8 @@ class ClientBaseImpl;
class ClientBase : public rclcpp::Waitable
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientBase)
RCLCPP_ACTION_PUBLIC
virtual ~ClientBase();

View File

@@ -72,6 +72,8 @@ enum class CancelResponse : int8_t
class ServerBase : public rclcpp::Waitable
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ServerBase)
/// Enum to identify entities belonging to the action server
enum class EntityType : std::size_t
{

View File

@@ -43,7 +43,7 @@ class ServerGoalHandleBase
{
public:
/// Indicate if client has requested this goal be cancelled.
/// \return true if a cancelation request has been accepted for this goal.
/// \return true if a cancellation request has been accepted for this goal.
RCLCPP_ACTION_PUBLIC
bool
is_canceling() const;

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