Compare commits

...

52 Commits

Author SHA1 Message Date
Chris Lalancette
676897df9d 26.0.0 2024-01-24 14:08:12 +00:00
Chris Lalancette
6602fcf7bc Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-01-24 14:08:05 +00:00
Chris Lalancette
a5221336f6 Increase the cppcheck timeout to 600 seconds. (#2409)
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-01-18 16:40:13 -05:00
Jeffery Hsu
7901bb9da4 Add transient local durability support to publisher and subscriptions when using intra-process communication (#2303)
* Add intra process transient local durability support to publisher and subscription

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>

* Remove durability_is_transient_local_ from publisher_base
Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>

* Design changes that move most transient local publish functionalities out of
intra process manager into intra process manager

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>

* Move transient local publish to a separate function

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>

* Remove publisher buffer weak ptr from intra process manager when it associated publisher is removed.

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>

* Remove incorrectly placed RCLCPP_PUBLIC

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>

* Add missing RCLCPP_PUBLIC

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>

* Expand RingBufferImplementation beyond shared_ptr and unique_ptr

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>

* Comment and format fix

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>

---------

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>
2024-01-18 08:39:59 -08:00
Chris Lalancette
6f6b5f2e36 Stop storing the context in the guard condition. (#2400)
* Stop storing the context in the guard condition.

This was creating a circular reference between GuardCondition
and Context, so that Context would never be cleaned up.
Since we never really need the GuardCondition to know
about its own Context, remove that part of the circular
reference.

While we are in here, we also change the get_context()
lambda to a straight weak_ptr; there is no reason for the
indirection since the context for the guard condition
cannot change at runtime.

We also remove the deprecated version of the
get_notify_guard_condition().  That's because there is
no way to properly implement it in the new scheme, and
it seems to be unused outside of rclcpp.

Finally, we add in a test that guarantees the use_count
is what we expect when inside and leaving a scope, ensuring
that contexts will properly be destroyed.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2024-01-08 08:23:33 -05:00
Jorge Perez
126d517193 Increase timeout for rclcpp_lifecycle to 360 (#2395)
* Increase timeout for rclcpp_lifecycle to 360

Signed-off-by: Jorge Perez <jjperez@ekumenlabs.com>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2024-01-03 08:17:47 -05:00
Chris Lalancette
5d2b32b5c3 25.0.0 2023-12-26 17:17:23 +00:00
Chris Lalancette
21f8a22311 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-12-26 17:17:15 +00:00
DensoADAS
2bb1dc2c71 Updated GenericSubscription to AnySubscriptionCallback (#1928)
* added rclcpp::SerializedMessage support for AnySubscriptionCallback

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>
Signed-off-by: Joshua Hampp <j.hampp@eu.denso.com>

* using AnySubscription callback for generic subscriptiion

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>
Signed-off-by: Joshua Hampp <j.hampp@eu.denso.com>

* updated tests

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>
Signed-off-by: Joshua Hampp <j.hampp@eu.denso.com>

* Remove comment

Signed-off-by: Joshua Hampp <j.hampp@eu.denso.com>

---------

Signed-off-by: Joshua Hampp <j.hampp@denso-adas.de>
Signed-off-by: Joshua Hampp <j.hampp@eu.denso.com>
Co-authored-by: Joshua Hampp <j.hampp@denso-adas.de>
Co-authored-by: Jacob Perron <jacob@openrobotics.org>
2023-12-19 08:35:13 -08:00
Chen Lihui
d9b2744057 make type support helper supported for service (#2209)
* make type support helper supported for service and action as well

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* not to use template and only add the necessary service type currently

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* update comment

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* add deprecated cycle for `get_typesupport_handle`

Signed-off-by: Chen Lihui <lihui.chen@sony.com>

---------

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2023-12-11 17:33:28 -08:00
Lucas Wendland
a19ad2134b Adding QoS to subscription options (#2323)
* Adding QoS to subscription options

Signed-off-by: CursedRock17 <mtglucas1@gmail.com>
2023-12-01 10:01:27 -05:00
Chris Lalancette
26f6efa840 Switch to target_link_libraries. (#2374)
That way we can hide more of the implementation by using
the PRIVATE keyword.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-11-27 16:39:20 -05:00
Daisuke Nishimatsu
d08d9cf5ff feat(rclcpp_components): support events executor in node main template (#2366)
Signed-off-by: wep21 <daisuke.nishimatsu1021@gmail.com>
2023-11-17 08:20:30 -05:00
Chen Lihui
2446fd000b aligh with rcl that a rosout publisher of a node might not exist (#2357)
* aligh with rcl

* keep same behavior with rclpy

1. to not throw exception durning rcl_logging_rosout_remove_sublogger
2. reset error message for RCL_RET_NOT_FOUND

* test for a node with rosout disabled

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2023-11-15 10:44:25 -05:00
mauropasse
411dbe8212 Fix data race in EventHandlerBase (#2349)
Both the EventHandler and its associated pubs/subs share
the same underlying rmw event listener.
When a pub/sub is destroyed, the listener is destroyed.
There is a data race when the ~EventHandlerBase wants
to access the listener after it has been destroyed.

The EventHandler stores a shared_ptr of its associated pub/sub.
But since we were clearing the listener event callbacks on the
base class destructor ~EventHandlerBase, the pub/sub was
already destroyed, which means the rmw event listener was also
destroyed, thus causing a segfault when trying to obtain it
to clear the callbacks.

Clearing the callbacks on ~EventHandler instead of
~EventHandlerBase avoids the race, since the pub/sub are still valid.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
2023-11-13 21:24:07 -05:00
Chris Lalancette
de841d9c8b Support users holding onto shared pointers in the message memory pool (#2336)
* Support users holding onto shared pointers in the message memory pool

Before this commit, the MessageMemoryPool would actually
reuse messages in the pool, even if the user had taken
additional shared_ptr copies.

This commit fixes things so that we properly handle that situation.  In particular, 
we allocate memory during class initialization, and delete
it during destruction.  We then run the constructor when
we hand the pointer out, and the destructor (only) when
we return it to the pool.  This keeps things consistent.
We also add in locks, since in a multi-threaded scenario we need
to protect against multiple threads accessing the pool
at the same time.

With this in place, things work as expected when users hold
shared_ptr copies.  We also add in a test for this situation.

One note about performance: this update preserves the
"no-allocations-at-runtime" aspect of the MessagePool.  However,
there are some tradeoffs with CPU time here, particularly with
very large message pools.  This could probably be optimized
further to do less work when trying to add items back to the
free_list, but I view that as a further enhancement.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-11-13 15:32:42 -05:00
M. Fatih Cırıt
9c098e544e fix(rclcpp_components): increase the service queue sizes in component_container (#2363)
* fix(rclcpp_components): increase the service queue sizes in component_container

Signed-off-by: M. Fatih Cırıt <mfc@leodrive.ai>
2023-11-09 15:09:18 -05:00
Chris Lalancette
0f331f90a9 24.0.0 2023-11-06 17:36:49 +00:00
Chris Lalancette
620fcf1e05 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-11-06 17:36:40 +00:00
Zard-C
d407a5e331 fix (signal_handler.hpp): spelling (#2356)
Signed-off-by: Zard-C <patrick.zhang5233@gmail.com>
2023-11-05 14:28:17 -05:00
Chris Lalancette
7f411371b3 Updates to not use std::move in some places. (#2353)
gcc 13.1.1 complains that these uses inhibit copy elision.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-11-05 14:27:40 -05:00
Tomoya Fujita
76e2b2677b rclcpp::Time::max() clock type support. (#2352)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-11-04 09:08:11 -07:00
Lucas Wendland
5049c45f85 Serialized Messages with Topic Statistics (#2274)
Signed-off-by: CursedRock17 <mtglucas1@gmail.com>
2023-11-03 15:06:25 -07:00
Michael Carroll
4691063a61 Add a custom deleter when constructing rcl_service_t (#2351)
* Add a custom deleter when constructing rcl_service_t

In the type description service construction, we were previously passing
the shared_ptr to the rcl_service_t with the assumption that
rclcpp::Service would do the clean up.  This was an incorrect
assumption, and so I have added a custom deleter to fini the service and
delete when the shared_ptr is cleaned up.

Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-11-03 08:59:06 -04:00
Chris Lalancette
f294488e17 Disable the loaned messages inside the executor. (#2335)
* Disable the loaned messages inside the executor.

They are currently unsafe to use; see the comment in the
commit for more information.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-11-02 18:29:12 -04:00
Michael Orlov
8a02e3934c Use message_info in SubscriptionTopicStatistics instead of typed message (#2337)
* Use message_info in SubscriptionTopicStatistics instead of typed message

- Untemplatize the rclcpp::topic_statistics::SubscriptionTopicStatistics
class. Now we will be using message_info instead of typed deserialized
messages in the handle_message callbacks.

* Fix test_receive_stats_include_window_reset by using publisher emulator

- Emulate publishing messages by directly calling
rclcpp::Subscription::handle_message(msg_shared_ptr, message_info) and
settling up needed message_info.source_timestamp

Signed-off-by: Michael Orlov <michael.orlov@apex.ai>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-11-02 13:26:33 -04:00
Jiaqi Li
fff009a751 Add missing 'enable_rosout' comments (#2345)
Signed-off-by: Jiaqi Li <ljq0831@qq.com>
2023-10-31 00:21:28 -07:00
Michael Carroll
2204e44305 Adjust rclcpp usage of type description service (#2344)
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-10-30 19:24:24 +00:00
Tomoya Fujita
fcbe64cff4 address rate related flaky tests. (#2329)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-10-26 00:08:18 -07:00
Chris Lalancette
c366e531fa Fixes pointed out by the clang analyzer. (#2339)
1. Remove the default Logger copy constructor without copy
assignment (rule of three -> rule of zero).
2. Remove an unnecessary capture in a lambda.
3. Mark a variable unused.
4. Mark a method as override.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-10-23 11:10:18 -05:00
Alexey Merzlyakov
5ffc963e1a Remove useless ROSRate class (#2326)
Signed-off-by: Alexey Merzlyakov <alexey.merzlyakov@samsung.com>
2023-10-16 09:38:38 -04:00
Chris Lalancette
7f7ffcf6ed Fix rclcpp_lifecycle inclusion on Windows. (#2331)
The comment in the commit explains this clearly, but
on Windows ERROR is a macro.  The reuse of it, even
as an enum, causes compilation errors on downstream
users.  Push the macro and undefine it so downstream
consumers can freely include it.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-10-10 13:25:55 -04:00
Chris Lalancette
13abc1beed 23.2.0 2023-10-09 15:31:54 +00:00
Chris Lalancette
e77c1fe550 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-10-09 15:31:47 +00:00
Minju, Lee
00b9d0a018 add clients & services count (#2072)
* add clients & services count

* add count clients,services tests

Signed-off-by: leeminju531 <dlalswn531@naver.com>
2023-10-09 10:36:00 -04:00
Tomoya Fujita
77c7aaf917 remove invalid sized allocation test for SerializedMessage. (#2330)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-10-06 15:15:34 -07:00
Steve Macenski
9019a8d9b7 Adding API to copy all parameters from one node to another (#2304)
Signed-off-by: stevemacenski <stevenmacenski@gmail.com>
2023-10-05 13:00:16 -07:00
Chris Lalancette
0644f220a2 23.1.0 2023-10-04 13:09:05 +00:00
Chris Lalancette
32438a6a67 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-10-04 13:08:56 +00:00
Ignacio Vizzo
d6bd8baac5 Add missing header required by the rclcpp::NodeOptions type (#2324)
Signed-off-by: Ignacio Vizzo <ignacio@dexory.com>
2023-10-04 08:18:21 -04:00
Chris Lalancette
623c3eb874 Add locking to protect the TimeSource::NodeState::node_base_ (#2320)
We need this because it is possible for one thread to
be handling the on_parameter_event callback while another
one is detaching the node.  This lock will protect that
from happening.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-10-03 14:16:49 -04:00
Tully Foote
7c1143dc15 Update SignalHandler get_global_signal_handler to avoid complex types in static memory (#2316)
* Update SignalHandler get_global_signal_handler to avoid complex types in static memory

This was flagged by msan as a problem.

There's a description of why this is a potential problem here: https://google.github.io/styleguide/cppguide.html#Static_and_Global_Variables

Signed-off-by: Tully Foote <tullyfoote@intrinsic.ai>
Co-authored-by: William Woodall <william+github@osrfoundation.org>
2023-09-29 16:13:43 -07:00
Lucas Wendland
9ff864c6ae Removing Old Connext Tests (#2313)
* Removing Old Connext Tests

Signed-off-by: CursedRock17 <mtglucas1@gmail.com>
2023-09-28 08:29:47 -04:00
Lucas Wendland
13182b5aad Documentation for list_parameters (#2315)
* list_parameters documentation

Signed-off-by: CursedRock17 <mtglucas1@gmail.com>
2023-09-27 17:14:53 -04:00
Tomoya Fujita
9284d7cefa Decouple rosout publisher init from node init. (#2174)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-09-21 18:44:13 -07:00
Minju, Lee
c42745c5ba fix the depth to relative in list_parameters (#2300)
* fix the depth to relative in list_parameters

Signed-off-by: leeminju531 <dlalswn531@naver.com>
2023-09-18 17:13:11 -04:00
Chris Lalancette
ea31f94eb4 23.0.0 2023-09-08 20:47:00 +00:00
Chris Lalancette
f496291e81 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-09-08 20:46:51 +00:00
Chris Lalancette
dd6fad6d42 Fix the return type of Rate::period. (#2301)
In a recent commit (bc435776a2),
we reworked how the Rate class worked so it could be
used with ROS time as well.  Unfortunately, we also
accidentally broke the API of it by changing the return
type of Rate::period to a Duration instead of a
std::chrono::nanoseconds .  Put this back to a std::chrono::nanoseconds;
if we want to change it to a Duration, we'll have to
add a new method and deprecate this one.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-09-08 16:40:09 -04:00
Christophe Bedard
38734d769a Update API docs links in package READMEs (#2302)
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2023-09-08 13:59:15 -04:00
jmachowinski
e103b8d37e fix(ClientGoalHandle): Made mutex recursive to prevent deadlocks (#2267)
* fix(ClientGoalHandle): Made mutex recursive to prevent deadlocks

This prevents deadlocks in cases, were e.g. get_status() would
be called on the handle in a callback of the handle.

* test(rclcpp_action): Added test for deadlocks during access of a goal handle

This test checks, if the code deadlocks, if methods on the goal handle are
called from the callbacks.

Signed-off-by: Janosch Machowinski <J.Machowinski@cellumation.com>
Co-authored-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-09-07 17:37:35 -04:00
Chris Lalancette
253d395d4e Cleanup flaky timers_manager tests. (#2299)
* Cleanup flaky timers_manager tests.

The timers_manager tests were relying too heavily on
specific timings; this caused them to be flaky on the
buildfarm, particularly on Windows.

Here, we increase the timeouts, and remove one test which
just relies too heavily on specific timeouts.  This should
make this test much less flaky on the buildfarm.

Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-09-07 15:17:16 -04:00
111 changed files with 2430 additions and 1404 deletions

View File

@@ -2,6 +2,64 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26.0.0 (2024-01-24)
-------------------
* Increase the cppcheck timeout to 600 seconds. (`#2409 <https://github.com/ros2/rclcpp/issues/2409>`_)
* Add transient local durability support to publisher and subscriptions when using intra-process communication (`#2303 <https://github.com/ros2/rclcpp/issues/2303>`_)
* Stop storing the context in the guard condition. (`#2400 <https://github.com/ros2/rclcpp/issues/2400>`_)
* Contributors: Chris Lalancette, Jeffery Hsu
25.0.0 (2023-12-26)
-------------------
* Updated GenericSubscription to AnySubscriptionCallback (`#1928 <https://github.com/ros2/rclcpp/issues/1928>`_)
* make type support helper supported for service (`#2209 <https://github.com/ros2/rclcpp/issues/2209>`_)
* Adding QoS to subscription options (`#2323 <https://github.com/ros2/rclcpp/issues/2323>`_)
* Switch to target_link_libraries. (`#2374 <https://github.com/ros2/rclcpp/issues/2374>`_)
* aligh with rcl that a rosout publisher of a node might not exist (`#2357 <https://github.com/ros2/rclcpp/issues/2357>`_)
* Fix data race in EventHandlerBase (`#2349 <https://github.com/ros2/rclcpp/issues/2349>`_)
* Support users holding onto shared pointers in the message memory pool (`#2336 <https://github.com/ros2/rclcpp/issues/2336>`_)
* Contributors: Chen Lihui, Chris Lalancette, DensoADAS, Lucas Wendland, mauropasse
24.0.0 (2023-11-06)
-------------------
* fix (signal_handler.hpp): spelling (`#2356 <https://github.com/ros2/rclcpp/issues/2356>`_)
* Updates to not use std::move in some places. (`#2353 <https://github.com/ros2/rclcpp/issues/2353>`_)
* rclcpp::Time::max() clock type support. (`#2352 <https://github.com/ros2/rclcpp/issues/2352>`_)
* Serialized Messages with Topic Statistics (`#2274 <https://github.com/ros2/rclcpp/issues/2274>`_)
* Add a custom deleter when constructing rcl_service_t (`#2351 <https://github.com/ros2/rclcpp/issues/2351>`_)
* Disable the loaned messages inside the executor. (`#2335 <https://github.com/ros2/rclcpp/issues/2335>`_)
* Use message_info in SubscriptionTopicStatistics instead of typed message (`#2337 <https://github.com/ros2/rclcpp/issues/2337>`_)
* Add missing 'enable_rosout' comments (`#2345 <https://github.com/ros2/rclcpp/issues/2345>`_)
* Adjust rclcpp usage of type description service (`#2344 <https://github.com/ros2/rclcpp/issues/2344>`_)
* address rate related flaky tests. (`#2329 <https://github.com/ros2/rclcpp/issues/2329>`_)
* Fixes pointed out by the clang analyzer. (`#2339 <https://github.com/ros2/rclcpp/issues/2339>`_)
* Remove useless ROSRate class (`#2326 <https://github.com/ros2/rclcpp/issues/2326>`_)
* Contributors: Alexey Merzlyakov, Chris Lalancette, Jiaqi Li, Lucas Wendland, Michael Carroll, Michael Orlov, Tomoya Fujita, Zard-C
23.2.0 (2023-10-09)
-------------------
* add clients & services count (`#2072 <https://github.com/ros2/rclcpp/issues/2072>`_)
* remove invalid sized allocation test for SerializedMessage. (`#2330 <https://github.com/ros2/rclcpp/issues/2330>`_)
* Adding API to copy all parameters from one node to another (`#2304 <https://github.com/ros2/rclcpp/issues/2304>`_)
* Contributors: Minju, Lee, Steve Macenski, Tomoya Fujita
23.1.0 (2023-10-04)
-------------------
* Add locking to protect the TimeSource::NodeState::node_base\_ (`#2320 <https://github.com/ros2/rclcpp/issues/2320>`_)
* Update SignalHandler get_global_signal_handler to avoid complex types in static memory (`#2316 <https://github.com/ros2/rclcpp/issues/2316>`_)
* Removing Old Connext Tests (`#2313 <https://github.com/ros2/rclcpp/issues/2313>`_)
* Documentation for list_parameters (`#2315 <https://github.com/ros2/rclcpp/issues/2315>`_)
* Decouple rosout publisher init from node init. (`#2174 <https://github.com/ros2/rclcpp/issues/2174>`_)
* fix the depth to relative in list_parameters (`#2300 <https://github.com/ros2/rclcpp/issues/2300>`_)
* Contributors: Chris Lalancette, Lucas Wendland, Minju, Lee, Tomoya Fujita, Tully Foote
23.0.0 (2023-09-08)
-------------------
* Fix the return type of Rate::period. (`#2301 <https://github.com/ros2/rclcpp/issues/2301>`_)
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
* Cleanup flaky timers_manager tests. (`#2299 <https://github.com/ros2/rclcpp/issues/2299>`_)
* Contributors: Chris Lalancette, Christophe Bedard
22.2.0 (2023-09-07)
-------------------
* Topic correct typeadapter deduction (`#2294 <https://github.com/ros2/rclcpp/issues/2294>`_)

View File

@@ -16,6 +16,8 @@ find_package(rcpputils REQUIRED)
find_package(rcutils REQUIRED)
find_package(rmw REQUIRED)
find_package(rosgraph_msgs REQUIRED)
find_package(rosidl_dynamic_typesupport REQUIRED)
find_package(rosidl_runtime_c REQUIRED)
find_package(rosidl_runtime_cpp REQUIRED)
find_package(rosidl_typesupport_c REQUIRED)
find_package(rosidl_typesupport_cpp REQUIRED)
@@ -44,6 +46,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/context.cpp
src/rclcpp/contexts/default_context.cpp
src/rclcpp/detail/add_guard_condition_to_rcl_wait_set.cpp
src/rclcpp/detail/resolve_intra_process_buffer_type.cpp
src/rclcpp/detail/resolve_parameter_overrides.cpp
src/rclcpp/detail/rmw_implementation_specific_payload.cpp
src/rclcpp/detail/rmw_implementation_specific_publisher_payload.cpp
@@ -202,23 +205,28 @@ target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>")
target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
# specific order: dependents before dependencies
ament_target_dependencies(${PROJECT_NAME}
"ament_index_cpp"
"libstatistics_collector"
"rcl"
"rcl_interfaces"
"rcl_logging_interface"
"rcl_yaml_param_parser"
"rcpputils"
"rcutils"
"builtin_interfaces"
"rosgraph_msgs"
"rosidl_typesupport_cpp"
"rosidl_runtime_cpp"
"statistics_msgs"
"tracetools"
target_link_libraries(${PROJECT_NAME} PUBLIC
${builtin_interfaces_TARGETS}
libstatistics_collector::libstatistics_collector
rcl::rcl
${rcl_interfaces_TARGETS}
rcl_yaml_param_parser::rcl_yaml_param_parser
rcpputils::rcpputils
rcutils::rcutils
rmw::rmw
${rosgraph_msgs_TARGETS}
rosidl_dynamic_typesupport::rosidl_dynamic_typesupport
rosidl_runtime_c::rosidl_runtime_c
rosidl_runtime_cpp::rosidl_runtime_cpp
rosidl_typesupport_cpp::rosidl_typesupport_cpp
${statistics_msgs_TARGETS}
tracetools::tracetools
${CMAKE_THREAD_LIBS_INIT}
)
target_link_libraries(${PROJECT_NAME} PRIVATE
ament_index_cpp::ament_index_cpp
rcl_logging_interface::rcl_logging_interface
)
# Causes the visibility macros to use dllexport rather than dllimport,
@@ -240,20 +248,23 @@ ament_export_libraries(${PROJECT_NAME})
# Export modern CMake targets
ament_export_targets(${PROJECT_NAME})
# specific order: dependents before dependencies
ament_export_dependencies(ament_index_cpp)
ament_export_dependencies(libstatistics_collector)
ament_export_dependencies(rcl)
ament_export_dependencies(rcpputils)
ament_export_dependencies(rcutils)
ament_export_dependencies(builtin_interfaces)
ament_export_dependencies(rosgraph_msgs)
ament_export_dependencies(rosidl_typesupport_cpp)
ament_export_dependencies(rosidl_typesupport_c)
ament_export_dependencies(rosidl_runtime_cpp)
ament_export_dependencies(rcl_yaml_param_parser)
ament_export_dependencies(statistics_msgs)
ament_export_dependencies(tracetools)
ament_export_dependencies(
builtin_interfaces
libstatistics_collector
rcl
rcl_interfaces
rcl_yaml_param_parser
rcpputils
rcutils
rmw
rosgraph_msgs
rosidl_dynamic_typesupport
rosidl_runtime_c
rosidl_runtime_cpp
rosidl_typesupport_cpp
statistics_msgs
tracetools
)
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
@@ -271,7 +282,7 @@ install(
if(TEST cppcheck)
# must set the property after ament_package()
set_tests_properties(cppcheck PROPERTIES TIMEOUT 500)
set_tests_properties(cppcheck PROPERTIES TIMEOUT 600)
endif()
if(TEST cpplint)

View File

@@ -2,7 +2,7 @@
The ROS client library in C++.
Visit the [rclcpp API documentation](http://docs.ros2.org/latest/api/rclcpp/) for a complete list of its main components and features.
The link to the latest rclcpp API documentation, which includes a complete list of its main components and features, can be found on the rclcpp package info page, at the [ROS Index](https://index.ros.org/p/rclcpp/).
## Quality Declaration

View File

@@ -30,6 +30,7 @@
#include "rclcpp/detail/subscription_callback_type_helper.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/message_info.hpp"
#include "rclcpp/serialization.hpp"
#include "rclcpp/serialized_message.hpp"
#include "rclcpp/type_adapter.hpp"
@@ -158,13 +159,14 @@ struct AnySubscriptionCallbackPossibleTypes
template<
typename MessageT,
typename AllocatorT,
bool is_adapted_type = rclcpp::TypeAdapter<MessageT>::is_specialized::value
bool is_adapted_type = rclcpp::TypeAdapter<MessageT>::is_specialized::value,
bool is_serialized_type = serialization_traits::is_serialized_message_class<MessageT>::value
>
struct AnySubscriptionCallbackHelper;
/// Specialization for when MessageT is not a TypeAdapter.
template<typename MessageT, typename AllocatorT>
struct AnySubscriptionCallbackHelper<MessageT, AllocatorT, false>
struct AnySubscriptionCallbackHelper<MessageT, AllocatorT, false, false>
{
using CallbackTypes = AnySubscriptionCallbackPossibleTypes<MessageT, AllocatorT>;
@@ -194,7 +196,7 @@ struct AnySubscriptionCallbackHelper<MessageT, AllocatorT, false>
/// Specialization for when MessageT is a TypeAdapter.
template<typename MessageT, typename AllocatorT>
struct AnySubscriptionCallbackHelper<MessageT, AllocatorT, true>
struct AnySubscriptionCallbackHelper<MessageT, AllocatorT, true, false>
{
using CallbackTypes = AnySubscriptionCallbackPossibleTypes<MessageT, AllocatorT>;
@@ -232,6 +234,26 @@ struct AnySubscriptionCallbackHelper<MessageT, AllocatorT, true>
>;
};
/// Specialization for when MessageT is a SerializedMessage to exclude duplicated declarations.
template<typename MessageT, typename AllocatorT>
struct AnySubscriptionCallbackHelper<MessageT, AllocatorT, false, true>
{
using CallbackTypes = AnySubscriptionCallbackPossibleTypes<MessageT, AllocatorT>;
using variant_type = std::variant<
typename CallbackTypes::ConstRefSerializedMessageCallback,
typename CallbackTypes::ConstRefSerializedMessageWithInfoCallback,
typename CallbackTypes::UniquePtrSerializedMessageCallback,
typename CallbackTypes::UniquePtrSerializedMessageWithInfoCallback,
typename CallbackTypes::SharedConstPtrSerializedMessageCallback,
typename CallbackTypes::SharedConstPtrSerializedMessageWithInfoCallback,
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageCallback,
typename CallbackTypes::ConstRefSharedConstPtrSerializedMessageWithInfoCallback,
typename CallbackTypes::SharedPtrSerializedMessageCallback,
typename CallbackTypes::SharedPtrSerializedMessageWithInfoCallback
>;
};
} // namespace detail
template<
@@ -487,7 +509,9 @@ public:
}
// Dispatch when input is a ros message and the output could be anything.
void
template<typename TMsg = ROSMessageType>
typename std::enable_if<!serialization_traits::is_serialized_message_class<TMsg>::value,
void>::type
dispatch(
std::shared_ptr<ROSMessageType> message,
const rclcpp::MessageInfo & message_info)
@@ -589,7 +613,7 @@ public:
// Dispatch when input is a serialized message and the output could be anything.
void
dispatch(
std::shared_ptr<rclcpp::SerializedMessage> serialized_message,
std::shared_ptr<const rclcpp::SerializedMessage> serialized_message,
const rclcpp::MessageInfo & message_info)
{
TRACETOOLS_TRACEPOINT(callback_start, static_cast<const void *>(this), false);

View File

@@ -129,8 +129,7 @@ public:
* added to the executor in either case.
*
* \param[in] group_type The type of the callback group.
* \param[in] get_node_context Lambda to retrieve the node context when
* checking that the creating node is valid and using the guard condition.
* \param[in] context A weak pointer to the context associated with this callback group.
* \param[in] automatically_add_to_executor_with_node A boolean that
* determines whether a callback group is automatically added to an executor
* with the node with which it is associated.
@@ -138,7 +137,7 @@ public:
RCLCPP_PUBLIC
explicit CallbackGroup(
CallbackGroupType group_type,
std::function<rclcpp::Context::SharedPtr(void)> get_node_context,
rclcpp::Context::WeakPtr context,
bool automatically_add_to_executor_with_node = true);
/// Default destructor.
@@ -228,16 +227,6 @@ public:
bool
automatically_add_to_executor_with_node() const;
/// Retrieve the guard condition used to signal changes to this callback group.
/**
* \param[in] context_ptr context to use when creating the guard condition
* \return guard condition if it is valid, otherwise nullptr.
*/
[[deprecated("Use get_notify_guard_condition() without arguments")]]
RCLCPP_PUBLIC
rclcpp::GuardCondition::SharedPtr
get_notify_guard_condition(const rclcpp::Context::SharedPtr context_ptr);
/// Retrieve the guard condition used to signal changes to this callback group.
/**
* \return guard condition if it is valid, otherwise nullptr.
@@ -297,7 +286,7 @@ protected:
std::shared_ptr<rclcpp::GuardCondition> notify_guard_condition_ = nullptr;
std::recursive_mutex notify_guard_condition_mutex_;
std::function<rclcpp::Context::SharedPtr(void)> get_context_;
rclcpp::Context::WeakPtr context_;
private:
template<typename TypeT, typename Function>

View File

@@ -0,0 +1,82 @@
// Copyright 2023 Open Navigation LLC
//
// 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__COPY_ALL_PARAMETER_VALUES_HPP_
#define RCLCPP__COPY_ALL_PARAMETER_VALUES_HPP_
#include <string>
#include <vector>
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rcl_interfaces/msg/set_parameters_result.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/logger.hpp"
#include "rclcpp/logging.hpp"
namespace rclcpp
{
/**
* Copy all parameters from one source node to another destination node.
* May throw exceptions if parameters from source are uninitialized or undeclared.
* \param source Node to copy parameters from
* \param destination Node to copy parameters to
* \param override_existing_params Default false. Whether to override existing destination params
* if both the source and destination contain the same parameter.
*/
template<typename NodeT1, typename NodeT2>
void
copy_all_parameter_values(
const NodeT1 & source, const NodeT2 & destination, const bool override_existing_params = false)
{
using Parameters = std::vector<rclcpp::Parameter>;
using Descriptions = std::vector<rcl_interfaces::msg::ParameterDescriptor>;
auto source_params = source->get_node_parameters_interface();
auto dest_params = destination->get_node_parameters_interface();
rclcpp::Logger logger = destination->get_node_logging_interface()->get_logger();
std::vector<std::string> param_names = source_params->list_parameters({}, 0).names;
Parameters params = source_params->get_parameters(param_names);
Descriptions descriptions = source_params->describe_parameters(param_names);
for (unsigned int idx = 0; idx != params.size(); idx++) {
if (!dest_params->has_parameter(params[idx].get_name())) {
dest_params->declare_parameter(
params[idx].get_name(), params[idx].get_parameter_value(), descriptions[idx]);
} else if (override_existing_params) {
try {
rcl_interfaces::msg::SetParametersResult result =
dest_params->set_parameters_atomically({params[idx]});
if (!result.successful) {
// Parameter update rejected or read-only
RCLCPP_WARN(
logger,
"Unable to set parameter (%s): %s!",
params[idx].get_name().c_str(), result.reason.c_str());
}
} catch (const rclcpp::exceptions::InvalidParameterTypeException & e) {
RCLCPP_WARN(
logger,
"Unable to set parameter (%s): incompatable parameter type (%s)!",
params[idx].get_name().c_str(), e.what());
}
}
}
}
} // namespace rclcpp
#endif // RCLCPP__COPY_ALL_PARAMETER_VALUES_HPP_

View File

@@ -45,13 +45,15 @@ namespace rclcpp
* Not all publisher options are currently respected, the only relevant options for this
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
*/
template<typename AllocatorT = std::allocator<void>>
template<
typename CallbackT,
typename AllocatorT = std::allocator<void>>
std::shared_ptr<GenericSubscription> create_generic_subscription(
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr topics_interface,
const std::string & topic_name,
const std::string & topic_type,
const rclcpp::QoS & qos,
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
CallbackT && callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
)
@@ -60,13 +62,20 @@ std::shared_ptr<GenericSubscription> create_generic_subscription(
auto ts_lib = rclcpp::get_typesupport_library(
topic_type, "rosidl_typesupport_cpp");
auto allocator = options.get_allocator();
using rclcpp::AnySubscriptionCallback;
AnySubscriptionCallback<rclcpp::SerializedMessage, AllocatorT>
any_subscription_callback(*allocator);
any_subscription_callback.set(std::forward<CallbackT>(callback));
auto subscription = std::make_shared<GenericSubscription>(
topics_interface->get_node_base_interface(),
std::move(ts_lib),
topic_name,
topic_type,
qos,
callback,
any_subscription_callback,
options);
topics_interface->add_subscription(subscription, options.callback_group);

View File

@@ -50,8 +50,8 @@ template<
typename SubscriptionT,
typename MessageMemoryStrategyT,
typename NodeParametersT,
typename NodeTopicsT,
typename ROSMessageType = typename SubscriptionT::ROSMessageType>
typename NodeTopicsT
>
typename std::shared_ptr<SubscriptionT>
create_subscription(
NodeParametersT & node_parameters,
@@ -70,7 +70,7 @@ create_subscription(
using rclcpp::node_interfaces::get_node_topics_interface;
auto node_topics_interface = get_node_topics_interface(node_topics);
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics>
subscription_topic_stats = nullptr;
if (rclcpp::detail::resolve_enable_topic_statistics(
@@ -80,8 +80,7 @@ create_subscription(
if (options.topic_stats_options.publish_period <= std::chrono::milliseconds(0)) {
throw std::invalid_argument(
"topic_stats_options.publish_period must be greater than 0, specified value of " +
std::to_string(options.topic_stats_options.publish_period.count()) +
" ms");
std::to_string(options.topic_stats_options.publish_period.count()) + " ms");
}
std::shared_ptr<Publisher<statistics_msgs::msg::MetricsMessage>>
@@ -89,14 +88,14 @@ create_subscription(
node_parameters,
node_topics_interface,
options.topic_stats_options.publish_topic,
qos);
options.topic_stats_options.qos);
subscription_topic_stats = std::make_shared<
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>
>(node_topics_interface->get_node_base_interface()->get_name(), publisher);
subscription_topic_stats =
std::make_shared<rclcpp::topic_statistics::SubscriptionTopicStatistics>(
node_topics_interface->get_node_base_interface()->get_name(), publisher);
std::weak_ptr<
rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>
rclcpp::topic_statistics::SubscriptionTopicStatistics
> weak_subscription_topic_stats(subscription_topic_stats);
auto sub_call_back = [weak_subscription_topic_stats]() {
auto subscription_topic_stats = weak_subscription_topic_stats.lock();

View File

@@ -47,6 +47,11 @@ resolve_intra_process_buffer_type(
return resolved_buffer_type;
}
RCLCPP_PUBLIC
rclcpp::IntraProcessBufferType
resolve_intra_process_buffer_type(
const rclcpp::IntraProcessBufferType buffer_type);
} // namespace detail
} // namespace rclcpp

View File

@@ -260,6 +260,16 @@ public:
}
}
~EventHandler()
{
// Since the rmw event listener holds a reference to the
// "on ready" callback, we need to clear it on destruction of this class.
// This clearing is not needed for other rclcpp entities like pub/subs, since
// they do own the underlying rmw entities, which are destroyed
// on their rclcpp destructors, thus no risk of dangling pointers.
clear_on_ready_callback();
}
/// Take data so that the callback cannot be scheduled again
std::shared_ptr<void>
take_data() override

View File

@@ -15,6 +15,8 @@
#ifndef RCLCPP__EXPERIMENTAL__BUFFERS__BUFFER_IMPLEMENTATION_BASE_HPP_
#define RCLCPP__EXPERIMENTAL__BUFFERS__BUFFER_IMPLEMENTATION_BASE_HPP_
#include <vector>
namespace rclcpp
{
namespace experimental
@@ -31,6 +33,8 @@ public:
virtual BufferT dequeue() = 0;
virtual void enqueue(BufferT request) = 0;
virtual std::vector<BufferT> get_all_data() = 0;
virtual void clear() = 0;
virtual bool has_data() const = 0;
virtual size_t available_capacity() const = 0;

View File

@@ -19,6 +19,7 @@
#include <stdexcept>
#include <type_traits>
#include <utility>
#include <vector>
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
@@ -66,6 +67,9 @@ public:
virtual MessageSharedPtr consume_shared() = 0;
virtual MessageUniquePtr consume_unique() = 0;
virtual std::vector<MessageSharedPtr> get_all_data_shared() = 0;
virtual std::vector<MessageUniquePtr> get_all_data_unique() = 0;
};
template<
@@ -129,6 +133,16 @@ public:
return consume_unique_impl<BufferT>();
}
std::vector<MessageSharedPtr> get_all_data_shared() override
{
return get_all_data_shared_impl();
}
std::vector<MessageUniquePtr> get_all_data_unique() override
{
return get_all_data_unique_impl();
}
bool has_data() const override
{
return buffer_->has_data();
@@ -243,6 +257,71 @@ private:
{
return buffer_->dequeue();
}
// MessageSharedPtr to MessageSharedPtr
template<typename T = BufferT>
typename std::enable_if<
std::is_same<T, MessageSharedPtr>::value,
std::vector<MessageSharedPtr>
>::type
get_all_data_shared_impl()
{
return buffer_->get_all_data();
}
// MessageUniquePtr to MessageSharedPtr
template<typename T = BufferT>
typename std::enable_if<
std::is_same<T, MessageUniquePtr>::value,
std::vector<MessageSharedPtr>
>::type
get_all_data_shared_impl()
{
std::vector<MessageSharedPtr> result;
auto uni_ptr_vec = buffer_->get_all_data();
result.reserve(uni_ptr_vec.size());
for (MessageUniquePtr & uni_ptr : uni_ptr_vec) {
result.emplace_back(std::move(uni_ptr));
}
return result;
}
// MessageSharedPtr to MessageUniquePtr
template<typename T = BufferT>
typename std::enable_if<
std::is_same<T, MessageSharedPtr>::value,
std::vector<MessageUniquePtr>
>::type
get_all_data_unique_impl()
{
std::vector<MessageUniquePtr> result;
auto shared_ptr_vec = buffer_->get_all_data();
result.reserve(shared_ptr_vec.size());
for (MessageSharedPtr shared_msg : shared_ptr_vec) {
MessageUniquePtr unique_msg;
MessageDeleter * deleter = std::get_deleter<MessageDeleter, const MessageT>(shared_msg);
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
MessageAllocTraits::construct(*message_allocator_.get(), ptr, *shared_msg);
if (deleter) {
unique_msg = MessageUniquePtr(ptr, *deleter);
} else {
unique_msg = MessageUniquePtr(ptr);
}
result.push_back(std::move(unique_msg));
}
return result;
}
// MessageUniquePtr to MessageUniquePtr
template<typename T = BufferT>
typename std::enable_if<
std::is_same<T, MessageUniquePtr>::value,
std::vector<MessageUniquePtr>
>::type
get_all_data_unique_impl()
{
return buffer_->get_all_data();
}
};
} // namespace buffers

View File

@@ -15,6 +15,7 @@
#ifndef RCLCPP__EXPERIMENTAL__BUFFERS__RING_BUFFER_IMPLEMENTATION_HPP_
#define RCLCPP__EXPERIMENTAL__BUFFERS__RING_BUFFER_IMPLEMENTATION_HPP_
#include <memory>
#include <mutex>
#include <stdexcept>
#include <utility>
@@ -113,6 +114,17 @@ public:
return request;
}
/// Get all the elements from the ring buffer
/**
* This member function is thread-safe.
*
* \return a vector containing all the elements from the ring buffer
*/
std::vector<BufferT> get_all_data() override
{
return get_all_data_impl();
}
/// Get the next index value for the ring buffer
/**
* This member function is thread-safe.
@@ -215,6 +227,71 @@ private:
return capacity_ - size_;
}
/// Traits for checking if a type is std::unique_ptr
template<typename ...>
struct is_std_unique_ptr final : std::false_type {};
template<class T, typename ... Args>
struct is_std_unique_ptr<std::unique_ptr<T, Args...>> final : std::true_type
{
typedef T Ptr_type;
};
/// Get all the elements from the ring buffer
/**
* This member function is thread-safe.
* Two versions for the implementation of the function.
* One for buffer containing unique_ptr and the other for other types
*
* \return a vector containing all the elements from the ring buffer
*/
template<typename T = BufferT, std::enable_if_t<is_std_unique_ptr<T>::value &&
std::is_copy_constructible<
typename is_std_unique_ptr<T>::Ptr_type
>::value,
void> * = nullptr>
std::vector<BufferT> get_all_data_impl()
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<BufferT> result_vtr;
result_vtr.reserve(size_);
for (size_t id = 0; id < size_; ++id) {
result_vtr.emplace_back(
new typename is_std_unique_ptr<T>::Ptr_type(
*(ring_buffer_[(read_index_ + id) % capacity_])));
}
return result_vtr;
}
template<typename T = BufferT, std::enable_if_t<
std::is_copy_constructible<T>::value, void> * = nullptr>
std::vector<BufferT> get_all_data_impl()
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<BufferT> result_vtr;
result_vtr.reserve(size_);
for (size_t id = 0; id < size_; ++id) {
result_vtr.emplace_back(ring_buffer_[(read_index_ + id) % capacity_]);
}
return result_vtr;
}
template<typename T = BufferT, std::enable_if_t<!is_std_unique_ptr<T>::value &&
!std::is_copy_constructible<T>::value, void> * = nullptr>
std::vector<BufferT> get_all_data_impl()
{
throw std::logic_error("Underlined type results in invalid get_all_data_impl()");
return {};
}
template<typename T = BufferT, std::enable_if_t<is_std_unique_ptr<T>::value &&
!std::is_copy_constructible<typename is_std_unique_ptr<T>::Ptr_type>::value,
void> * = nullptr>
std::vector<BufferT> get_all_data_impl()
{
throw std::logic_error("Underlined type in unique_ptr results in invalid get_all_data_impl()");
return {};
}
size_t capacity_;
std::vector<BufferT> ring_buffer_;

View File

@@ -28,6 +28,7 @@
#include <typeinfo>
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
#include "rclcpp/experimental/ros_message_intra_process_buffer.hpp"
#include "rclcpp/experimental/subscription_intra_process.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
@@ -112,9 +113,40 @@ public:
* \param subscription the SubscriptionIntraProcess to register.
* \return an unsigned 64-bit integer which is the subscription's unique id.
*/
RCLCPP_PUBLIC
template<
typename ROSMessageType,
typename Alloc = std::allocator<ROSMessageType>
>
uint64_t
add_subscription(rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr subscription);
add_subscription(rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr subscription)
{
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
uint64_t sub_id = IntraProcessManager::get_next_unique_id();
subscriptions_[sub_id] = subscription;
// adds the subscription id to all the matchable publishers
for (auto & pair : publishers_) {
auto publisher = pair.second.lock();
if (!publisher) {
continue;
}
if (can_communicate(publisher, subscription)) {
uint64_t pub_id = pair.first;
insert_sub_id_for_pub(sub_id, pub_id, subscription->use_take_shared_method());
if (publisher->is_durability_transient_local() &&
subscription->is_durability_transient_local())
{
do_transient_local_publish<ROSMessageType, Alloc>(
pub_id, sub_id,
subscription->use_take_shared_method());
}
}
}
return sub_id;
}
/// Unregister a subscription using the subscription's unique id.
/**
@@ -131,14 +163,21 @@ public:
* This method stores the publisher intra process object, together with
* the information of its wrapped publisher (i.e. topic name and QoS).
*
* If the publisher's durability is transient local, its buffer pointer should
* be passed and the method will store it as well.
*
* In addition this generates a unique intra process id for the publisher.
*
* \param publisher publisher to be registered with the manager.
* \param buffer publisher's buffer to be stored if its duability is transient local.
* \return an unsigned 64-bit integer which is the publisher's unique id.
*/
RCLCPP_PUBLIC
uint64_t
add_publisher(rclcpp::PublisherBase::SharedPtr publisher);
add_publisher(
rclcpp::PublisherBase::SharedPtr publisher,
rclcpp::experimental::buffers::IntraProcessBufferBase::SharedPtr buffer =
rclcpp::experimental::buffers::IntraProcessBufferBase::SharedPtr());
/// Unregister a publisher using the publisher's unique id.
/**
@@ -292,6 +331,34 @@ public:
}
}
template<
typename MessageT,
typename Alloc,
typename Deleter,
typename ROSMessageType>
void
add_shared_msg_to_buffer(
std::shared_ptr<const MessageT> message,
uint64_t subscription_id)
{
add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(message, {subscription_id});
}
template<
typename MessageT,
typename Alloc,
typename Deleter,
typename ROSMessageType>
void
add_owned_msg_to_buffer(
std::unique_ptr<MessageT, Deleter> message,
uint64_t subscription_id,
typename allocator::AllocRebind<MessageT, Alloc>::allocator_type & allocator)
{
add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
std::move(message), {subscription_id}, allocator);
}
/// Return true if the given rmw_gid_t matches any stored Publishers.
RCLCPP_PUBLIC
bool
@@ -324,6 +391,9 @@ private:
using PublisherMap =
std::unordered_map<uint64_t, rclcpp::PublisherBase::WeakPtr>;
using PublisherBufferMap =
std::unordered_map<uint64_t, rclcpp::experimental::buffers::IntraProcessBufferBase::WeakPtr>;
using PublisherToSubscriptionIdsMap =
std::unordered_map<uint64_t, SplittedSubscriptions>;
@@ -342,6 +412,54 @@ private:
rclcpp::PublisherBase::SharedPtr pub,
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr sub) const;
template<
typename ROSMessageType,
typename Alloc = std::allocator<ROSMessageType>
>
void do_transient_local_publish(
const uint64_t pub_id, const uint64_t sub_id,
const bool use_take_shared_method)
{
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
auto publisher_buffer = publisher_buffers_[pub_id].lock();
if (!publisher_buffer) {
throw std::runtime_error("publisher buffer has unexpectedly gone out of scope");
}
auto buffer = std::dynamic_pointer_cast<
rclcpp::experimental::buffers::IntraProcessBuffer<
ROSMessageType,
ROSMessageTypeAllocator,
ROSMessageTypeDeleter
>
>(publisher_buffer);
if (!buffer) {
throw std::runtime_error(
"failed to dynamic cast publisher's IntraProcessBufferBase to "
"IntraProcessBuffer<ROSMessageType,ROSMessageTypeAllocator,"
"ROSMessageTypeDeleter> which can happen when the publisher and "
"subscription use different allocator types, which is not supported");
}
if (use_take_shared_method) {
auto data_vec = buffer->get_all_data_shared();
for (auto shared_data : data_vec) {
this->template add_shared_msg_to_buffer<
ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter, ROSMessageType>(
shared_data, sub_id);
}
} else {
auto data_vec = buffer->get_all_data_unique();
for (auto & owned_data : data_vec) {
auto allocator = ROSMessageTypeAllocator();
this->template add_owned_msg_to_buffer<
ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter, ROSMessageType>(
std::move(owned_data), sub_id, allocator);
}
}
}
template<
typename MessageT,
typename Alloc,
@@ -467,7 +585,7 @@ private:
auto ptr = MessageAllocTraits::allocate(allocator, 1);
MessageAllocTraits::construct(allocator, ptr, *message);
subscription->provide_intra_process_data(std::move(MessageUniquePtr(ptr, deleter)));
subscription->provide_intra_process_data(MessageUniquePtr(ptr, deleter));
}
continue;
@@ -510,7 +628,7 @@ private:
MessageAllocTraits::construct(allocator, ptr, *message);
ros_message_subscription->provide_intra_process_message(
std::move(MessageUniquePtr(ptr, deleter)));
MessageUniquePtr(ptr, deleter));
}
}
}
@@ -520,6 +638,7 @@ private:
PublisherToSubscriptionIdsMap pub_to_subs_;
SubscriptionMap subscriptions_;
PublisherMap publishers_;
PublisherBufferMap publisher_buffers_;
mutable std::shared_timed_mutex mutex_;
};

View File

@@ -67,6 +67,10 @@ public:
size_t
available_capacity() const = 0;
RCLCPP_PUBLIC
bool
is_durability_transient_local() const;
bool
is_ready(rcl_wait_set_t * wait_set) override = 0;

View File

@@ -527,7 +527,7 @@ private:
void execute_ready_timers_unsafe();
// Callback to be called when timer is ready
std::function<void(const rclcpp::TimerBase *)> on_ready_callback_ = nullptr;
std::function<void(const rclcpp::TimerBase *)> on_ready_callback_;
// Thread used to run the timers execution task
std::thread timers_thread_;

View File

@@ -77,7 +77,7 @@ public:
: rclcpp::PublisherBase(
node_base,
topic_name,
*rclcpp::get_typesupport_handle(topic_type, "rosidl_typesupport_cpp", *ts_lib),
*rclcpp::get_message_typesupport_handle(topic_type, "rosidl_typesupport_cpp", *ts_lib),
options.template to_rcl_publisher_options<rclcpp::SerializedMessage>(qos),
// NOTE(methylDragon): Passing these args separately is necessary for event binding
options.event_callbacks,

View File

@@ -74,18 +74,21 @@ public:
const std::string & topic_name,
const std::string & topic_type,
const rclcpp::QoS & qos,
// TODO(nnmm): Add variant for callback with message info. See issue #1604.
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
AnySubscriptionCallback<rclcpp::SerializedMessage, AllocatorT> callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
: SubscriptionBase(
node_base,
*rclcpp::get_typesupport_handle(topic_type, "rosidl_typesupport_cpp", *ts_lib),
*rclcpp::get_message_typesupport_handle(topic_type, "rosidl_typesupport_cpp", *ts_lib),
topic_name,
options.to_rcl_subscription_options(qos),
options.event_callbacks,
options.use_default_callbacks,
DeliveredMessageKind::SERIALIZED_MESSAGE),
callback_(callback),
callback_([callback](
std::shared_ptr<const rclcpp::SerializedMessage> serialized_message,
const rclcpp::MessageInfo & message_info) mutable {
callback.dispatch(serialized_message, message_info);
}),
ts_lib_(ts_lib)
{}
@@ -151,7 +154,9 @@ public:
private:
RCLCPP_DISABLE_COPY(GenericSubscription)
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback_;
std::function<void(
std::shared_ptr<const rclcpp::SerializedMessage>,
const rclcpp::MessageInfo)> callback_;
// The type support library should stay loaded, so it is stored in the GenericSubscription
std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;
};

View File

@@ -48,7 +48,7 @@ public:
*/
RCLCPP_PUBLIC
explicit GuardCondition(
rclcpp::Context::SharedPtr context =
const rclcpp::Context::SharedPtr & context =
rclcpp::contexts::get_global_default_context(),
rcl_guard_condition_options_t guard_condition_options =
rcl_guard_condition_get_default_options());
@@ -57,11 +57,6 @@ public:
virtual
~GuardCondition();
/// Return the context used when creating this guard condition.
RCLCPP_PUBLIC
rclcpp::Context::SharedPtr
get_context() const;
/// Return the underlying rcl guard condition structure.
RCLCPP_PUBLIC
rcl_guard_condition_t &
@@ -128,7 +123,6 @@ public:
set_on_trigger_callback(std::function<void(size_t)> callback);
protected:
rclcpp::Context::SharedPtr context_;
rcl_guard_condition_t rcl_guard_condition_;
std::atomic<bool> in_use_by_wait_set_{false};
std::recursive_mutex reentrant_mutex_;

View File

@@ -126,9 +126,6 @@ private:
std::shared_ptr<std::pair<std::string, std::string>> logger_sublogger_pairname_ = nullptr;
public:
RCLCPP_PUBLIC
Logger(const Logger &) = default;
/// Get the name of this logger.
/**
* \return the full name of the logger including any prefixes, or

View File

@@ -358,12 +358,14 @@ public:
* `%callback_group`.
* \return Shared pointer to the created generic subscription.
*/
template<typename AllocatorT = std::allocator<void>>
template<
typename CallbackT,
typename AllocatorT = std::allocator<void>>
std::shared_ptr<rclcpp::GenericSubscription> create_generic_subscription(
const std::string & topic_name,
const std::string & topic_type,
const rclcpp::QoS & qos,
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
CallbackT && callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
)
@@ -972,7 +974,16 @@ public:
/// Return a list of parameters with any of the given prefixes, up to the given depth.
/**
* \todo: properly document and test this method.
* Parameters are separated into a hierarchy using the "." (dot) character.
* The "prefixes" argument is a way to select only particular parts of the hierarchy.
*
* \param[in] prefixes The list of prefixes that should be searched for within the
* current parameters. If this vector of prefixes is empty, then list_parameters
* will return all parameters.
* \param[in] depth An unsigned integer that represents the recursive depth to search.
* If this depth = 0, then all parameters that fit the prefixes will be returned.
* \returns A ListParametersResult message which contains both an array of unique prefixes
* and an array of names that were matched to the prefixes given.
*/
RCLCPP_PUBLIC
rcl_interfaces::msg::ListParametersResult
@@ -1305,6 +1316,26 @@ public:
size_t
count_subscribers(const std::string & topic_name) const;
/// Return the number of clients created for a given service.
/**
* \param[in] service_name the actual service name used; it will not be automatically remapped.
* \return number of clients that have been created for the given service.
* \throws std::runtime_error if clients could not be counted
*/
RCLCPP_PUBLIC
size_t
count_clients(const std::string & service_name) const;
/// Return the number of services created for a given service.
/**
* \param[in] service_name the actual service name used; it will not be automatically remapped.
* \return number of services that have been created for the given service.
* \throws std::runtime_error if services could not be counted
*/
RCLCPP_PUBLIC
size_t
count_services(const std::string & service_name) const;
/// Return the topic endpoint information about publishers on a given topic.
/**
* The returned parameter is a list of topic endpoint information, where each item will contain

View File

@@ -221,13 +221,13 @@ Node::create_generic_publisher(
);
}
template<typename AllocatorT>
template<typename CallbackT, typename AllocatorT>
std::shared_ptr<rclcpp::GenericSubscription>
Node::create_generic_subscription(
const std::string & topic_name,
const std::string & topic_type,
const rclcpp::QoS & qos,
std::function<void(std::shared_ptr<rclcpp::SerializedMessage>)> callback,
CallbackT && callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
{
return rclcpp::create_generic_subscription(
@@ -235,7 +235,7 @@ Node::create_generic_subscription(
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
topic_type,
qos,
std::move(callback),
std::forward<CallbackT>(callback),
options
);
}

View File

@@ -113,6 +113,14 @@ public:
size_t
count_subscribers(const std::string & topic_name) const override;
RCLCPP_PUBLIC
size_t
count_clients(const std::string & service_name) const override;
RCLCPP_PUBLIC
size_t
count_services(const std::string & service_name) const override;
RCLCPP_PUBLIC
const rcl_guard_condition_t *
get_graph_guard_condition() const override;

View File

@@ -305,6 +305,24 @@ public:
size_t
count_subscribers(const std::string & topic_name) const = 0;
/// Return the number of clients created for a given service.
/*
* \param[in] service_name the actual service name used; it will not be automatically remapped.
*/
RCLCPP_PUBLIC
virtual
size_t
count_clients(const std::string & service_name) const = 0;
/// Return the number of services created for a given service.
/*
* \param[in] service_name the actual service name used; it will not be automatically remapped.
*/
RCLCPP_PUBLIC
virtual
size_t
count_services(const std::string & service_name) const = 0;
/// Return the rcl guard condition which is triggered when the ROS graph changes.
RCLCPP_PUBLIC
virtual

View File

@@ -43,6 +43,7 @@ public:
* - arguments = {}
* - parameter_overrides = {}
* - use_global_arguments = true
* - enable_rosout = true
* - use_intra_process_comms = false
* - enable_topic_statistics = false
* - start_parameter_services = true

View File

@@ -32,6 +32,9 @@
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/detail/resolve_use_intra_process.hpp"
#include "rclcpp/detail/resolve_intra_process_buffer_type.hpp"
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
#include "rclcpp/experimental/intra_process_manager.hpp"
#include "rclcpp/get_message_type_support_handle.hpp"
#include "rclcpp/is_ros_compatible_type.hpp"
@@ -109,6 +112,12 @@ public:
[[deprecated("use std::shared_ptr<const PublishedType>")]] =
std::shared_ptr<const PublishedType>;
using BufferSharedPtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
ROSMessageType,
ROSMessageTypeAllocator,
ROSMessageTypeDeleter
>::SharedPtr;
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, AllocatorT>)
/// Default constructor.
@@ -171,11 +180,14 @@ public:
throw std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value");
}
if (qos.durability() != rclcpp::DurabilityPolicy::Volatile) {
throw std::invalid_argument(
"intraprocess communication allowed only with volatile durability");
if (qos.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
buffer_ = rclcpp::experimental::create_intra_process_buffer<
ROSMessageType, ROSMessageTypeAllocator, ROSMessageTypeDeleter>(
rclcpp::detail::resolve_intra_process_buffer_type(options_.intra_process_buffer_type),
qos,
std::make_shared<ROSMessageTypeAllocator>(ros_message_type_allocator_));
}
uint64_t intra_process_publisher_id = ipm->add_publisher(this->shared_from_this());
uint64_t intra_process_publisher_id = ipm->add_publisher(this->shared_from_this(), buffer_);
this->setup_intra_process(
intra_process_publisher_id,
ipm);
@@ -242,9 +254,18 @@ public:
if (inter_process_publish_needed) {
auto shared_msg =
this->do_intra_process_ros_message_publish_and_return_shared(std::move(msg));
if (buffer_) {
buffer_->add_shared(shared_msg);
}
this->do_inter_process_publish(*shared_msg);
} else {
this->do_intra_process_ros_message_publish(std::move(msg));
if (buffer_) {
auto shared_msg =
this->do_intra_process_ros_message_publish_and_return_shared(std::move(msg));
buffer_->add_shared(shared_msg);
} else {
this->do_intra_process_ros_message_publish(std::move(msg));
}
}
}
@@ -309,14 +330,22 @@ public:
get_subscription_count() > get_intra_process_subscription_count();
if (inter_process_publish_needed) {
ROSMessageType ros_msg;
auto ros_msg_ptr = std::make_shared<ROSMessageType>();
// TODO(clalancette): This is unnecessarily doing an additional conversion
// that may have already been done in do_intra_process_publish_and_return_shared().
// We should just reuse that effort.
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*msg, ros_msg);
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*msg, *ros_msg_ptr);
this->do_intra_process_publish(std::move(msg));
this->do_inter_process_publish(ros_msg);
this->do_inter_process_publish(*ros_msg_ptr);
if (buffer_) {
buffer_->add_shared(ros_msg_ptr);
}
} else {
if (buffer_) {
auto ros_msg_ptr = std::make_shared<ROSMessageType>();
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*msg, *ros_msg_ptr);
buffer_->add_shared(ros_msg_ptr);
}
this->do_intra_process_publish(std::move(msg));
}
}
@@ -390,7 +419,7 @@ public:
if (this->can_loan_messages()) {
// we release the ownership from the rclpp::LoanedMessage instance
// and let the middleware clean up the memory.
this->do_loaned_message_publish(std::move(loaned_msg.release()));
this->do_loaned_message_publish(loaned_msg.release());
} else {
// we don't release the ownership, let the middleware copy the ros message
// and thus the destructor of rclcpp::LoanedMessage cleans up the memory.
@@ -581,6 +610,8 @@ protected:
PublishedTypeDeleter published_type_deleter_;
ROSMessageTypeAllocator ros_message_type_allocator_;
ROSMessageTypeDeleter ros_message_type_deleter_;
BufferSharedPtr buffer_{nullptr};
};
} // namespace rclcpp

View File

@@ -139,6 +139,12 @@ public:
size_t
get_intra_process_subscription_count() const;
/// Get if durability is transient local
/** \return If durability is transient local*/
RCLCPP_PUBLIC
bool
is_durability_transient_local() const;
/// Manually assert that this Publisher is alive (for RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC).
/**
* If the rmw Liveliness policy is set to RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC, the creator

View File

@@ -24,6 +24,7 @@
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/detail/rmw_implementation_specific_publisher_payload.hpp"
#include "rclcpp/intra_process_buffer_type.hpp"
#include "rclcpp/intra_process_setting.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/event_handler.hpp"
@@ -40,6 +41,9 @@ struct PublisherOptionsBase
/// Setting to explicitly set intraprocess communications.
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault;
/// Setting the data-type stored in the intraprocess buffer
IntraProcessBufferType intra_process_buffer_type = IntraProcessBufferType::SharedPtr;
/// Callbacks for various events related to publishers.
PublisherEventCallbacks event_callbacks;

View File

@@ -163,7 +163,7 @@ public:
reset();
RCLCPP_PUBLIC
Duration
std::chrono::nanoseconds
period() const;
private:
@@ -184,16 +184,6 @@ public:
explicit WallRate(const Duration & period);
};
class ROSRate : public Rate
{
public:
RCLCPP_PUBLIC
explicit ROSRate(const double rate);
RCLCPP_PUBLIC
explicit ROSRate(const Duration & period);
};
} // namespace rclcpp
#endif // RCLCPP__RATE_HPP_

View File

@@ -54,6 +54,7 @@
* - rclcpp::ParameterValue
* - rclcpp::AsyncParametersClient
* - rclcpp::SyncParametersClient
* - rclcpp::copy_all_parameter_values()
* - rclcpp/parameter.hpp
* - rclcpp/parameter_value.hpp
* - rclcpp/parameter_client.hpp
@@ -95,6 +96,9 @@
* - Get the number of publishers or subscribers on a topic:
* - rclcpp::Node::count_publishers()
* - rclcpp::Node::count_subscribers()
* - Get the number of clients or servers on a service:
* - rclcpp::Node::count_clients()
* - rclcpp::Node::count_services()
*
* And components related to logging:
*
@@ -164,6 +168,7 @@
#include <csignal>
#include <memory>
#include "rclcpp/copy_all_parameter_values.hpp"
#include "rclcpp/executors.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/logging.hpp"

View File

@@ -15,10 +15,17 @@
#ifndef RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_
#define RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_
#include <array>
#include <cstring>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <type_traits>
#include "rosidl_runtime_cpp/traits.hpp"
#include "rclcpp/logger.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -50,13 +57,24 @@ class MessagePoolMemoryStrategy
public:
RCLCPP_SMART_PTR_DEFINITIONS(MessagePoolMemoryStrategy)
/// Default constructor
MessagePoolMemoryStrategy()
: next_array_index_(0)
{
pool_mutex_ = std::make_shared<std::mutex>();
pool_ = std::shared_ptr<std::array<MessageT *, Size>>(
new std::array<MessageT *, Size>,
[](std::array<MessageT *, Size> * arr) {
for (size_t i = 0; i < Size; ++i) {
free((*arr)[i]);
}
delete arr;
});
free_list_ = std::make_shared<CircularArray<Size>>();
for (size_t i = 0; i < Size; ++i) {
pool_[i].msg_ptr_ = std::make_shared<MessageT>();
pool_[i].used = false;
(*pool_)[i] = static_cast<MessageT *>(malloc(sizeof(MessageT)));
free_list_->push_back(i);
}
}
@@ -68,43 +86,85 @@ public:
*/
std::shared_ptr<MessageT> borrow_message()
{
size_t current_index = next_array_index_;
next_array_index_ = (next_array_index_ + 1) % Size;
if (pool_[current_index].used) {
throw std::runtime_error("Tried to access message that was still in use! Abort.");
std::lock_guard<std::mutex> lock(*pool_mutex_);
if (free_list_->size() == 0) {
throw std::runtime_error("No more free slots in the pool");
}
pool_[current_index].msg_ptr_->~MessageT();
new (pool_[current_index].msg_ptr_.get())MessageT;
pool_[current_index].used = true;
return pool_[current_index].msg_ptr_;
size_t current_index = free_list_->pop_front();
return std::shared_ptr<MessageT>(
new((*pool_)[current_index]) MessageT(),
[pool = this->pool_, pool_mutex = this->pool_mutex_,
free_list = this->free_list_](MessageT * p) {
std::lock_guard<std::mutex> lock(*pool_mutex);
for (size_t i = 0; i < Size; ++i) {
if ((*pool)[i] == p) {
p->~MessageT();
free_list->push_back(i);
break;
}
}
});
}
/// Return a message to the message pool.
/**
* Manage metadata in the message pool ring buffer to release the message.
* This does nothing since the message isn't returned to the pool until the user has dropped
* all references.
* \param[in] msg Shared pointer to the message to return.
*/
void return_message(std::shared_ptr<MessageT> & msg)
{
for (size_t i = 0; i < Size; ++i) {
if (pool_[i].msg_ptr_ == msg) {
pool_[i].used = false;
return;
}
}
throw std::runtime_error("Unrecognized message ptr in return_message.");
(void)msg;
}
protected:
struct PoolMember
template<size_t N>
class CircularArray
{
std::shared_ptr<MessageT> msg_ptr_;
bool used;
public:
void push_back(const size_t v)
{
if (size_ + 1 > N) {
throw std::runtime_error("Tried to push too many items into the array");
}
array_[(front_ + size_) % N] = v;
++size_;
}
size_t pop_front()
{
if (size_ < 1) {
throw std::runtime_error("Tried to pop item from empty array");
}
size_t val = array_[front_];
front_ = (front_ + 1) % N;
--size_;
return val;
}
size_t size() const
{
return size_;
}
private:
size_t front_ = 0;
size_t size_ = 0;
std::array<size_t, N> array_;
};
std::array<PoolMember, Size> pool_;
size_t next_array_index_;
// It's very important that these are shared_ptrs, since users of this class might hold a
// reference to a pool item longer than the lifetime of the class. In that scenario, the
// shared_ptr ensures that the lifetime of these variables outlives this class, and hence ensures
// the custom destructor for each pool item can successfully run.
std::shared_ptr<std::mutex> pool_mutex_;
std::shared_ptr<std::array<MessageT *, Size>> pool_;
std::shared_ptr<CircularArray<Size>> free_list_;
};
} // namespace message_pool_memory_strategy

View File

@@ -104,7 +104,7 @@ public:
private:
using SubscriptionTopicStatisticsSharedPtr =
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>;
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics>;
public:
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
@@ -163,10 +163,6 @@ public:
throw std::invalid_argument(
"intraprocess communication is not allowed with 0 depth qos policy");
}
if (qos_profile.durability() != rclcpp::DurabilityPolicy::Volatile) {
throw std::invalid_argument(
"intraprocess communication allowed only with volatile durability");
}
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
MessageT,
@@ -193,7 +189,8 @@ public:
// Add it to the intra process manager.
using rclcpp::experimental::IntraProcessManager;
auto ipm = context->get_sub_context<IntraProcessManager>();
uint64_t intra_process_subscription_id = ipm->add_subscription(subscription_intra_process_);
uint64_t intra_process_subscription_id = ipm->template add_subscription<
ROSMessageType, ROSMessageTypeAllocator>(subscription_intra_process_);
this->setup_intra_process(intra_process_subscription_id, ipm);
}
@@ -316,7 +313,7 @@ public:
if (subscription_topic_statistics_) {
const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
const auto time = rclcpp::Time(nanos.time_since_epoch().count());
subscription_topic_statistics_->handle_message(*typed_message, time);
subscription_topic_statistics_->handle_message(message_info.get_rmw_message_info(), time);
}
}
@@ -325,8 +322,20 @@ public:
const std::shared_ptr<rclcpp::SerializedMessage> & serialized_message,
const rclcpp::MessageInfo & message_info) override
{
// TODO(wjwwood): enable topic statistics for serialized messages
std::chrono::time_point<std::chrono::system_clock> now;
if (subscription_topic_statistics_) {
// get current time before executing callback to
// exclude callback duration from topic statistics result.
now = std::chrono::system_clock::now();
}
any_callback_.dispatch(serialized_message, message_info);
if (subscription_topic_statistics_) {
const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
const auto time = rclcpp::Time(nanos.time_since_epoch().count());
subscription_topic_statistics_->handle_message(message_info.get_rmw_message_info(), time);
}
}
void
@@ -357,7 +366,7 @@ public:
if (subscription_topic_statistics_) {
const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
const auto time = rclcpp::Time(nanos.time_since_epoch().count());
subscription_topic_statistics_->handle_message(*typed_message, time);
subscription_topic_statistics_->handle_message(message_info.get_rmw_message_info(), time);
}
}

View File

@@ -75,15 +75,14 @@ template<
typename CallbackT,
typename AllocatorT,
typename SubscriptionT = rclcpp::Subscription<MessageT, AllocatorT>,
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType,
typename ROSMessageType = typename SubscriptionT::ROSMessageType
typename MessageMemoryStrategyT = typename SubscriptionT::MessageMemoryStrategyType
>
SubscriptionFactory
create_subscription_factory(
CallbackT && callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat,
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics<ROSMessageType>>
std::shared_ptr<rclcpp::topic_statistics::SubscriptionTopicStatistics>
subscription_topic_stats = nullptr
)
{

View File

@@ -77,6 +77,10 @@ struct SubscriptionOptionsBase
// Topic statistics publication period in ms. Defaults to one second.
// Only values greater than zero are allowed.
std::chrono::milliseconds publish_period{std::chrono::seconds(1)};
// 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();
};
TopicStatisticsOptions topic_stats_options;

View File

@@ -189,7 +189,7 @@ public:
*/
RCLCPP_PUBLIC
static Time
max();
max(rcl_clock_type_t clock_type = RCL_SYSTEM_TIME); // NOLINT
/// Get the seconds since epoch
/**

View File

@@ -48,21 +48,12 @@ using libstatistics_collector::moving_average_statistics::StatisticData;
/**
* Class used to collect, measure, and publish topic statistics data. Current statistics
* supported for subscribers are received message age and received message period.
*
* \tparam CallbackMessageT the subscribed message type
*/
template<typename CallbackMessageT>
*/
class SubscriptionTopicStatistics
{
using TopicStatsCollector =
libstatistics_collector::topic_statistics_collector::TopicStatisticsCollector<
CallbackMessageT>;
using ReceivedMessageAge =
libstatistics_collector::topic_statistics_collector::ReceivedMessageAgeCollector<
CallbackMessageT>;
using ReceivedMessagePeriod =
libstatistics_collector::topic_statistics_collector::ReceivedMessagePeriodCollector<
CallbackMessageT>;
using TopicStatsCollector = libstatistics_collector::TopicStatisticsCollector;
using ReceivedMessageAge = libstatistics_collector::ReceivedMessageAgeCollector;
using ReceivedMessagePeriod = libstatistics_collector::ReceivedMessagePeriodCollector;
public:
/// Construct a SubscriptionTopicStatistics object.
@@ -101,16 +92,16 @@ public:
/**
* This method acquires a lock to prevent race conditions to collectors list.
*
* \param received_message the message received by the subscription
* \param message_info the message info corresponding to the received message
* \param now_nanoseconds current time in nanoseconds
*/
virtual void handle_message(
const CallbackMessageT & received_message,
const rmw_message_info_t & message_info,
const rclcpp::Time now_nanoseconds) const
{
std::lock_guard<std::mutex> lock(mutex_);
for (const auto & collector : subscriber_statistics_collectors_) {
collector->OnMessageReceived(received_message, now_nanoseconds.nanoseconds());
collector->OnMessageReceived(message_info, now_nanoseconds.nanoseconds());
}
}

View File

@@ -22,6 +22,7 @@
#include "rcpputils/shared_library.hpp"
#include "rosidl_runtime_cpp/message_type_support_decl.hpp"
#include "rosidl_runtime_cpp/service_type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -40,11 +41,15 @@ get_typesupport_library(const std::string & type, const std::string & typesuppor
/// Extract the type support handle from the library.
/**
* The library needs to match the topic type. The shared library must stay loaded for the lifetime of the result.
*
* \deprecated Use get_message_typesupport_handle() instead
*
* \param[in] type The topic type, e.g. "std_msgs/msg/String"
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
* \param[in] library The shared type support library
* \return A type support handle
*/
[[deprecated("Use `get_message_typesupport_handle` instead")]]
RCLCPP_PUBLIC
const rosidl_message_type_support_t *
get_typesupport_handle(
@@ -52,6 +57,40 @@ get_typesupport_handle(
const std::string & typesupport_identifier,
rcpputils::SharedLibrary & library);
/// Extract the message type support handle from the library.
/**
* The library needs to match the topic type. The shared library must stay loaded for the lifetime of the result.
*
* \param[in] type The topic type, e.g. "std_msgs/msg/String"
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
* \param[in] library The shared type support library
* \throws std::runtime_error if the symbol of type not found in the library.
* \return A message type support handle
*/
RCLCPP_PUBLIC
const rosidl_message_type_support_t *
get_message_typesupport_handle(
const std::string & type,
const std::string & typesupport_identifier,
rcpputils::SharedLibrary & library);
/// Extract the service type support handle from the library.
/**
* The library needs to match the topic type. The shared library must stay loaded for the lifetime of the result.
*
* \param[in] type The service type, e.g. "std_srvs/srv/Empty"
* \param[in] typesupport_identifier Type support identifier, typically "rosidl_typesupport_cpp"
* \param[in] library The shared type support library
* \throws std::runtime_error if the symbol of type not found in the library.
* \return A service type support handle
*/
RCLCPP_PUBLIC
const rosidl_service_type_support_t *
get_service_typesupport_handle(
const std::string & type,
const std::string & typesupport_identifier,
rcpputils::SharedLibrary & library);
} // namespace rclcpp
#endif // RCLCPP__TYPESUPPORT_HELPERS_HPP_

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>22.2.0</version>
<version>26.0.0</version>
<description>The ROS client library in C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
@@ -22,6 +22,7 @@
<build_depend>builtin_interfaces</build_depend>
<build_depend>rcl_interfaces</build_depend>
<build_depend>rosgraph_msgs</build_depend>
<build_depend>rosidl_runtime_c</build_depend>
<build_depend>rosidl_runtime_cpp</build_depend>
<build_depend>rosidl_typesupport_c</build_depend>
<build_depend>rosidl_typesupport_cpp</build_depend>
@@ -29,6 +30,7 @@
<build_export_depend>builtin_interfaces</build_export_depend>
<build_export_depend>rcl_interfaces</build_export_depend>
<build_export_depend>rosgraph_msgs</build_export_depend>
<build_export_depend>rosidl_runtime_c</build_export_depend>
<build_export_depend>rosidl_runtime_cpp</build_export_depend>
<build_export_depend>rosidl_typesupport_c</build_export_depend>
<build_export_depend>rosidl_typesupport_cpp</build_export_depend>

View File

@@ -31,12 +31,12 @@ using rclcpp::CallbackGroupType;
CallbackGroup::CallbackGroup(
CallbackGroupType group_type,
std::function<rclcpp::Context::SharedPtr(void)> get_context,
rclcpp::Context::WeakPtr context,
bool automatically_add_to_executor_with_node)
: type_(group_type), associated_with_executor_(false),
can_be_taken_from_(true),
automatically_add_to_executor_with_node_(automatically_add_to_executor_with_node),
get_context_(get_context)
context_(context)
{}
CallbackGroup::~CallbackGroup()
@@ -123,40 +123,12 @@ CallbackGroup::automatically_add_to_executor_with_node() const
return automatically_add_to_executor_with_node_;
}
// \TODO(mjcarroll) Deprecated, remove on tock
rclcpp::GuardCondition::SharedPtr
CallbackGroup::get_notify_guard_condition(const rclcpp::Context::SharedPtr context_ptr)
{
std::lock_guard<std::recursive_mutex> lock(notify_guard_condition_mutex_);
if (notify_guard_condition_ && context_ptr != notify_guard_condition_->get_context()) {
if (associated_with_executor_) {
trigger_notify_guard_condition();
}
notify_guard_condition_ = nullptr;
}
if (!notify_guard_condition_) {
notify_guard_condition_ = std::make_shared<rclcpp::GuardCondition>(context_ptr);
}
return notify_guard_condition_;
}
rclcpp::GuardCondition::SharedPtr
CallbackGroup::get_notify_guard_condition()
{
std::lock_guard<std::recursive_mutex> lock(notify_guard_condition_mutex_);
if (!this->get_context_) {
throw std::runtime_error("Callback group was created without context and not passed context");
}
auto context_ptr = this->get_context_();
rclcpp::Context::SharedPtr context_ptr = context_.lock();
if (context_ptr && context_ptr->is_valid()) {
if (notify_guard_condition_ && context_ptr != notify_guard_condition_->get_context()) {
if (associated_with_executor_) {
trigger_notify_guard_condition();
}
notify_guard_condition_ = nullptr;
}
if (!notify_guard_condition_) {
notify_guard_condition_ = std::make_shared<rclcpp::GuardCondition>(context_ptr);
}

View File

@@ -462,7 +462,7 @@ template<Context::ShutdownType shutdown_type>
std::vector<rclcpp::Context::ShutdownCallback>
Context::get_shutdown_callback() const
{
const auto get_callback_vector = [this](auto & mutex, auto & callback_set) {
const auto get_callback_vector = [](auto & mutex, auto & callback_set) {
const std::lock_guard<std::mutex> lock(mutex);
std::vector<rclcpp::Context::ShutdownCallback> callbacks;
for (auto & callback : callback_set) {
@@ -496,7 +496,7 @@ Context::sleep_for(const std::chrono::nanoseconds & nanoseconds)
std::unique_lock<std::mutex> lock(interrupt_mutex_);
auto start = std::chrono::steady_clock::now();
// this will release the lock while waiting
interrupt_condition_variable_.wait_for(lock, nanoseconds);
interrupt_condition_variable_.wait_for(lock, time_left);
time_left -= std::chrono::steady_clock::now() - start;
}
} while (time_left > std::chrono::nanoseconds::zero() && this->is_valid());

View File

@@ -0,0 +1,37 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <rclcpp/detail/resolve_intra_process_buffer_type.hpp>
namespace rclcpp
{
namespace detail
{
rclcpp::IntraProcessBufferType
resolve_intra_process_buffer_type(
const rclcpp::IntraProcessBufferType buffer_type)
{
if (buffer_type == IntraProcessBufferType::CallbackDefault) {
throw std::invalid_argument(
"IntraProcessBufferType::CallbackDefault is not allowed "
"when there is no callback function");
}
return buffer_type;
}
} // namespace detail
} // namespace rclcpp

View File

@@ -39,13 +39,6 @@ UnsupportedEventTypeException::UnsupportedEventTypeException(
EventHandlerBase::~EventHandlerBase()
{
// Since the rmw event listener holds a reference to
// this callback, we need to clear it on destruction of this class.
// This clearing is not needed for other rclcpp entities like pub/subs, since
// they do own the underlying rmw entities, which are destroyed
// on their rclcpp destructors, thus no risk of dangling pointers.
clear_on_ready_callback();
if (rcl_event_fini(&event_handle_) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",

View File

@@ -669,6 +669,11 @@ Executor::execute_subscription(rclcpp::SubscriptionBase::SharedPtr subscription)
subscription->get_topic_name(),
[&]() {return subscription->take_type_erased(message.get(), message_info);},
[&]() {subscription->handle_message(message, message_info);});
// TODO(clalancette): In the case that the user is using the MessageMemoryPool,
// and they take a shared_ptr reference to the message in the callback, this can
// inadvertently return the message to the pool when the user is still using it.
// This is a bug that needs to be fixed in the pool, and we should probably have
// a custom deleter for the message that actually does the return_message().
subscription->return_message(message);
}
break;

View File

@@ -28,9 +28,9 @@ using rclcpp::experimental::TimersManager;
TimersManager::TimersManager(
std::shared_ptr<rclcpp::Context> context,
std::function<void(const rclcpp::TimerBase *)> on_ready_callback)
: on_ready_callback_(on_ready_callback),
context_(context)
{
context_ = context;
on_ready_callback_ = on_ready_callback;
}
TimersManager::~TimersManager()

View File

@@ -49,9 +49,9 @@ GenericSubscription::handle_message(
void
GenericSubscription::handle_serialized_message(
const std::shared_ptr<rclcpp::SerializedMessage> & message,
const rclcpp::MessageInfo &)
const rclcpp::MessageInfo & message_info)
{
callback_(message);
callback_(message, message_info);
}
void

View File

@@ -23,16 +23,17 @@ namespace rclcpp
{
GuardCondition::GuardCondition(
rclcpp::Context::SharedPtr context,
const rclcpp::Context::SharedPtr & context,
rcl_guard_condition_options_t guard_condition_options)
: context_(context), rcl_guard_condition_{rcl_get_zero_initialized_guard_condition()}
: rcl_guard_condition_{rcl_get_zero_initialized_guard_condition()}
{
if (!context_) {
if (!context) {
throw std::invalid_argument("context argument unexpectedly nullptr");
}
rcl_ret_t ret = rcl_guard_condition_init(
&this->rcl_guard_condition_,
context_->get_rcl_context().get(),
context->get_rcl_context().get(),
guard_condition_options);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to create guard condition");
@@ -53,12 +54,6 @@ GuardCondition::~GuardCondition()
}
}
rclcpp::Context::SharedPtr
GuardCondition::get_context() const
{
return context_;
}
rcl_guard_condition_t &
GuardCondition::get_rcl_guard_condition()
{

View File

@@ -32,13 +32,24 @@ IntraProcessManager::~IntraProcessManager()
{}
uint64_t
IntraProcessManager::add_publisher(rclcpp::PublisherBase::SharedPtr publisher)
IntraProcessManager::add_publisher(
rclcpp::PublisherBase::SharedPtr publisher,
rclcpp::experimental::buffers::IntraProcessBufferBase::SharedPtr buffer)
{
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
uint64_t pub_id = IntraProcessManager::get_next_unique_id();
publishers_[pub_id] = publisher;
if (publisher->is_durability_transient_local()) {
if (buffer) {
publisher_buffers_[pub_id] = buffer;
} else {
throw std::runtime_error(
"transient_local publisher needs to pass"
"a valid publisher buffer ptr when calling add_publisher()");
}
}
// Initialize the subscriptions storage for this publisher.
pub_to_subs_[pub_id] = SplittedSubscriptions();
@@ -58,30 +69,6 @@ IntraProcessManager::add_publisher(rclcpp::PublisherBase::SharedPtr publisher)
return pub_id;
}
uint64_t
IntraProcessManager::add_subscription(SubscriptionIntraProcessBase::SharedPtr subscription)
{
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
uint64_t sub_id = IntraProcessManager::get_next_unique_id();
subscriptions_[sub_id] = subscription;
// adds the subscription id to all the matchable publishers
for (auto & pair : publishers_) {
auto publisher = pair.second.lock();
if (!publisher) {
continue;
}
if (can_communicate(publisher, subscription)) {
uint64_t pub_id = pair.first;
insert_sub_id_for_pub(sub_id, pub_id, subscription->use_take_shared_method());
}
}
return sub_id;
}
void
IntraProcessManager::remove_subscription(uint64_t intra_process_subscription_id)
{
@@ -112,6 +99,7 @@ IntraProcessManager::remove_publisher(uint64_t intra_process_publisher_id)
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
publishers_.erase(intra_process_publisher_id);
publisher_buffers_.erase(intra_process_publisher_id);
pub_to_subs_.erase(intra_process_publisher_id);
}

View File

@@ -17,6 +17,7 @@
#include <utility>
#include "rcl_logging_interface/rcl_logging_interface.h"
#include "rcl/error_handling.h"
#include "rcl/logging_rosout.h"
#include "rclcpp/exceptions.hpp"
@@ -80,10 +81,12 @@ Logger::get_child(const std::string & suffix)
{
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
rcl_ret = rcl_logging_rosout_add_sublogger((*name_).c_str(), suffix.c_str());
if (RCL_RET_OK != rcl_ret) {
if (RCL_RET_NOT_FOUND == rcl_ret) {
rcl_reset_error();
} else if (RCL_RET_OK != rcl_ret) {
exceptions::throw_from_rcl_error(
rcl_ret, "failed to call rcl_logging_rosout_add_sublogger",
rcutils_get_error_state(), rcutils_reset_error);
rcl_get_error_state(), rcl_reset_error);
}
}
@@ -98,9 +101,7 @@ Logger::get_child(const std::string & suffix)
logger_sublogger_pairname_ptr->second.c_str());
delete logger_sublogger_pairname_ptr;
if (RCL_RET_OK != rcl_ret) {
exceptions::throw_from_rcl_error(
rcl_ret, "failed to call rcl_logging_rosout_remove_sublogger",
rcutils_get_error_state(), rcutils_reset_error);
rcl_reset_error();
}
});
}

View File

@@ -522,6 +522,18 @@ Node::count_subscribers(const std::string & topic_name) const
return node_graph_->count_subscribers(topic_name);
}
size_t
Node::count_clients(const std::string & service_name) const
{
return node_graph_->count_clients(service_name);
}
size_t
Node::count_services(const std::string & service_name) const
{
return node_graph_->count_services(service_name);
}
std::vector<rclcpp::TopicEndpointInfo>
Node::get_publishers_info_by_topic(const std::string & topic_name, bool no_mangle) const
{

View File

@@ -21,6 +21,8 @@
#include "rcl/arguments.h"
#include "rcl/node_type_cache.h"
#include "rcl/logging.h"
#include "rcl/logging_rosout.h"
#include "rclcpp/exceptions.hpp"
#include "rcutils/logging_macros.h"
#include "rmw/validate_namespace.h"
@@ -55,17 +57,12 @@ NodeBase::NodeBase(
std::shared_ptr<std::recursive_mutex> logging_mutex = get_global_logging_mutex();
rcl_ret_t ret;
{
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
// TODO(ivanpauno): /rosout Qos should be reconfigurable.
// TODO(ivanpauno): Instead of mutually excluding rcl_node_init with the global logger mutex,
// rcl_logging_rosout_init_publisher_for_node could be decoupled from there and be called
// here directly.
ret = rcl_node_init(
rcl_node.get(),
node_name.c_str(), namespace_.c_str(),
context_->get_rcl_context().get(), &rcl_node_options);
}
// TODO(ivanpauno): /rosout Qos should be reconfigurable.
ret = rcl_node_init(
rcl_node.get(),
node_name.c_str(), namespace_.c_str(),
context_->get_rcl_context().get(), &rcl_node_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_NODE_INVALID_NAME) {
rcl_reset_error(); // discard rcl_node_init error
@@ -115,13 +112,29 @@ NodeBase::NodeBase(
throw_from_rcl_error(ret, "failed to initialize rcl node");
}
// The initialization for the rosout publisher
if (rcl_logging_rosout_enabled() && rcl_node_options.enable_rosout) {
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
ret = rcl_logging_rosout_init_publisher_for_node(rcl_node.get());
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "failed to initialize rosout publisher");
}
}
node_handle_.reset(
rcl_node.release(),
[logging_mutex](rcl_node_t * node) -> void {
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
// TODO(ivanpauno): Instead of mutually excluding rcl_node_fini with the global logger mutex,
// rcl_logging_rosout_fini_publisher_for_node could be decoupled from there and be called
// here directly.
[logging_mutex, rcl_node_options](rcl_node_t * node) -> void {
{
std::lock_guard<std::recursive_mutex> guard(*logging_mutex);
if (rcl_logging_rosout_enabled() && rcl_node_options.enable_rosout) {
rcl_ret_t ret = rcl_logging_rosout_fini_publisher_for_node(node);
if (ret != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Error in destruction of rosout publisher: %s", rcl_get_error_string().str);
}
}
}
if (rcl_node_fini(node) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
@@ -205,14 +218,9 @@ NodeBase::create_callback_group(
rclcpp::CallbackGroupType group_type,
bool automatically_add_to_executor_with_node)
{
auto weak_context = this->get_context()->weak_from_this();
auto get_node_context = [weak_context]() -> rclcpp::Context::SharedPtr {
return weak_context.lock();
};
auto group = std::make_shared<rclcpp::CallbackGroup>(
group_type,
get_node_context,
context_->weak_from_this(),
automatically_add_to_executor_with_node);
std::lock_guard<std::mutex> lock(callback_groups_mutex_);
callback_groups_.push_back(group);

View File

@@ -498,6 +498,50 @@ NodeGraph::count_subscribers(const std::string & topic_name) const
return count;
}
size_t
NodeGraph::count_clients(const std::string & service_name) const
{
auto rcl_node_handle = node_base_->get_rcl_node_handle();
auto fqdn = rclcpp::expand_topic_or_service_name(
service_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
true);
size_t count;
auto ret = rcl_count_clients(rcl_node_handle, fqdn.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count clients: ") + rmw_get_error_string().str);
// *INDENT-ON*
}
return count;
}
size_t
NodeGraph::count_services(const std::string & service_name) const
{
auto rcl_node_handle = node_base_->get_rcl_node_handle();
auto fqdn = rclcpp::expand_topic_or_service_name(
service_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
true);
size_t count;
auto ret = rcl_count_services(rcl_node_handle, fqdn.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count services: ") + rmw_get_error_string().str);
// *INDENT-ON*
}
return count;
}
const rcl_guard_condition_t *
NodeGraph::get_graph_guard_condition() const
{

View File

@@ -1059,7 +1059,7 @@ NodeParameters::list_parameters(const std::vector<std::string> & prefixes, uint6
if (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) {
return true;
}
std::string substr = kv.first.substr(prefix.length());
std::string substr = kv.first.substr(prefix.length() + 1);
return separators_less_than_depth(substr);
}
return false;

View File

@@ -84,23 +84,31 @@ public:
}
if (enabled) {
auto rcl_node = node_base->get_rcl_node_handle();
rcl_ret_t rcl_ret = rcl_node_type_description_service_init(rcl_node);
auto * rcl_node = node_base->get_rcl_node_handle();
std::shared_ptr<rcl_service_t> rcl_srv(
new rcl_service_t,
[rcl_node, logger = this->logger_](rcl_service_t * service)
{
if (rcl_service_fini(service, rcl_node) != RCL_RET_OK) {
RCLCPP_ERROR(
logger,
"Error in destruction of rcl service handle [~/get_type_description]: %s",
rcl_get_error_string().str);
rcl_reset_error();
}
delete service;
});
*rcl_srv = rcl_get_zero_initialized_service();
rcl_ret_t rcl_ret = rcl_node_type_description_service_init(rcl_srv.get(), rcl_node);
if (rcl_ret != RCL_RET_OK) {
RCLCPP_ERROR(
logger_, "Failed to initialize ~/get_type_description_service: %s",
logger_, "Failed to initialize ~/get_type_description service: %s",
rcl_get_error_string().str);
throw std::runtime_error(
"Failed to initialize ~/get_type_description service.");
}
rcl_service_t * rcl_srv = nullptr;
rcl_ret = rcl_node_get_type_description_service(rcl_node, &rcl_srv);
if (rcl_ret != RCL_RET_OK) {
throw std::runtime_error(
"Failed to get initialized ~/get_type_description service from rcl.");
}
rclcpp::AnyServiceCallback<ServiceT> cb;
cb.set(
[this](
@@ -124,18 +132,6 @@ public:
nullptr);
}
}
~NodeTypeDescriptionsImpl()
{
if (
type_description_srv_ &&
RCL_RET_OK != rcl_node_type_description_service_fini(node_base_->get_rcl_node_handle()))
{
RCLCPP_ERROR(
logger_,
"Error in shutdown of get_type_description service: %s", rcl_get_error_string().str);
}
}
};
NodeTypeDescriptions::NodeTypeDescriptions(

View File

@@ -270,6 +270,13 @@ PublisherBase::get_intra_process_subscription_count() const
return ipm->get_subscription_count(intra_process_publisher_id_);
}
bool
PublisherBase::is_durability_transient_local() const
{
return rcl_publisher_get_actual_qos(publisher_handle_.get())->durability ==
RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL;
}
rclcpp::QoS
PublisherBase::get_actual_qos() const
{

View File

@@ -14,6 +14,7 @@
#include "rclcpp/rate.hpp"
#include <chrono>
#include <stdexcept>
namespace rclcpp
@@ -66,7 +67,8 @@ Rate::sleep()
// Calculate the time to sleep
auto time_to_sleep = next_interval - now;
// Sleep (will get interrupted by ctrl-c, may not sleep full time)
return clock_->sleep_for(time_to_sleep);
clock_->sleep_for(time_to_sleep);
return true;
}
bool
@@ -87,10 +89,10 @@ Rate::reset()
last_interval_ = clock_->now();
}
Duration
std::chrono::nanoseconds
Rate::period() const
{
return period_;
return std::chrono::nanoseconds(period_.nanoseconds());
}
WallRate::WallRate(const double rate)
@@ -101,12 +103,4 @@ WallRate::WallRate(const Duration & period)
: Rate(period, std::make_shared<Clock>(RCL_STEADY_TIME))
{}
ROSRate::ROSRate(const double rate)
: Rate(rate, std::make_shared<Clock>(RCL_ROS_TIME))
{}
ROSRate::ROSRate(const Duration & period)
: Rate(period, std::make_shared<Clock>(RCL_ROS_TIME))
{}
} // namespace rclcpp

View File

@@ -113,7 +113,7 @@ SignalHandler::get_logger()
SignalHandler &
SignalHandler::get_global_signal_handler()
{
static SignalHandler signal_handler;
static SignalHandler & signal_handler = *new SignalHandler();
return signal_handler;
}

View File

@@ -75,7 +75,7 @@ public:
bool
install(SignalHandlerOptions signal_handler_options = SignalHandlerOptions::All);
/// Uninstall the signal handler for SIGINT/SIGTERM and join the dedicated singal handling
/// Uninstall the signal handler for SIGINT/SIGTERM and join the dedicated signal handling
/// thread.
/**
* Also restores the previous signal handler.
@@ -189,7 +189,7 @@ private:
// Whether or not a signal has been received.
std::atomic_bool signal_received_ = false;
// A thread to which singal handling tasks are deferred.
// A thread to which signal handling tasks are deferred.
std::thread signal_handler_thread_;
// A mutex used to synchronize the install() and uninstall() methods.

View File

@@ -298,7 +298,20 @@ SubscriptionBase::setup_intra_process(
bool
SubscriptionBase::can_loan_messages() const
{
return rcl_subscription_can_loan_messages(subscription_handle_.get());
bool retval = rcl_subscription_can_loan_messages(subscription_handle_.get());
if (retval) {
// TODO(clalancette): The loaned message interface is currently not safe to use with
// shared_ptr callbacks. If a user takes a copy of the shared_ptr, it can get freed from
// underneath them via rcl_return_loaned_message_from_subscription(). The correct solution is
// to return the loaned message in a custom deleter, but that needs to be carefully handled
// with locking. Warn the user about this until we fix it.
RCLCPP_WARN_ONCE(
this->node_logger_,
"Loaned messages are only safe with const ref subscription callbacks. "
"If you are using any other kind of subscriptions, "
"set the ROS_DISABLE_LOANED_MESSAGES environment variable to 1 (the default).");
}
return retval;
}
rclcpp::Waitable::SharedPtr

View File

@@ -34,3 +34,9 @@ SubscriptionIntraProcessBase::get_actual_qos() const
{
return qos_profile_;
}
bool
SubscriptionIntraProcessBase::is_durability_transient_local() const
{
return qos_profile_.durability() == rclcpp::DurabilityPolicy::TransientLocal;
}

View File

@@ -276,9 +276,9 @@ Time::operator-=(const rclcpp::Duration & rhs)
}
Time
Time::max()
Time::max(rcl_clock_type_t clock_type)
{
return Time(std::numeric_limits<int32_t>::max(), 999999999);
return Time(std::numeric_limits<int32_t>::max(), 999999999, clock_type);
}

View File

@@ -236,6 +236,7 @@ public:
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface,
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface)
{
std::lock_guard<std::mutex> guard(node_base_lock_);
node_base_ = node_base_interface;
node_topics_ = node_topics_interface;
node_graph_ = node_graph_interface;
@@ -280,17 +281,14 @@ public:
parameter_subscription_ = rclcpp::AsyncParametersClient::on_parameter_event(
node_topics_,
[this](std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event) {
if (node_base_ != nullptr) {
this->on_parameter_event(event);
}
// Do nothing if node_base_ is nullptr because it means the TimeSource is now
// without an attached node
this->on_parameter_event(event);
});
}
// Detach the attached node
void detachNode()
{
std::lock_guard<std::mutex> guard(node_base_lock_);
// destroy_clock_sub() *must* be first here, to ensure that the executor
// can't possibly call any of the callbacks as we are cleaning up.
destroy_clock_sub();
@@ -327,6 +325,7 @@ private:
std::thread clock_executor_thread_;
// Preserve the node reference
std::mutex node_base_lock_;
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_{nullptr};
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_{nullptr};
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_{nullptr};
@@ -464,6 +463,14 @@ private:
// Callback for parameter updates
void on_parameter_event(std::shared_ptr<const rcl_interfaces::msg::ParameterEvent> event)
{
std::lock_guard<std::mutex> guard(node_base_lock_);
if (node_base_ == nullptr) {
// Do nothing if node_base_ is nullptr because it means the TimeSource is now
// without an attached node
return;
}
// Filter out events on 'use_sim_time' parameter instances in other nodes.
if (event->node != node_base_->get_fully_qualified_name()) {
return;

View File

@@ -91,6 +91,44 @@ extract_type_identifier(const std::string & full_type)
return std::make_tuple(package_name, middle_module, type_name);
}
const void * get_typesupport_handle_impl(
const std::string & type,
const std::string & typesupport_identifier,
const std::string & typesupport_name,
const std::string & symbol_part_name,
const std::string & middle_module_additional,
rcpputils::SharedLibrary & library)
{
std::string package_name;
std::string middle_module;
std::string type_name;
std::tie(package_name, middle_module, type_name) = extract_type_identifier(type);
if (middle_module.empty()) {
middle_module = middle_module_additional;
}
auto mk_error = [&package_name, &type_name, &typesupport_name](auto reason) {
std::stringstream rcutils_dynamic_loading_error;
rcutils_dynamic_loading_error <<
"Something went wrong loading the typesupport library for " <<
typesupport_name << " type " << package_name <<
"/" << type_name << ". " << reason;
return rcutils_dynamic_loading_error.str();
};
try {
std::string symbol_name = typesupport_identifier + symbol_part_name +
package_name + "__" + middle_module + "__" + type_name;
const void * (* get_ts)() = nullptr;
// This will throw runtime_error if the symbol was not found.
get_ts = reinterpret_cast<decltype(get_ts)>(library.get_symbol(symbol_name));
return get_ts();
} catch (std::runtime_error &) {
throw std::runtime_error{mk_error("Library could not be found.")};
}
}
} // anonymous namespace
std::shared_ptr<rcpputils::SharedLibrary>
@@ -101,36 +139,42 @@ get_typesupport_library(const std::string & type, const std::string & typesuppor
return std::make_shared<rcpputils::SharedLibrary>(library_path);
}
const rosidl_message_type_support_t *
get_typesupport_handle(
const rosidl_message_type_support_t * get_typesupport_handle(
const std::string & type,
const std::string & typesupport_identifier,
rcpputils::SharedLibrary & library)
{
std::string package_name;
std::string middle_module;
std::string type_name;
std::tie(package_name, middle_module, type_name) = extract_type_identifier(type);
return get_message_typesupport_handle(type, typesupport_identifier, library);
}
auto mk_error = [&package_name, &type_name](auto reason) {
std::stringstream rcutils_dynamic_loading_error;
rcutils_dynamic_loading_error <<
"Something went wrong loading the typesupport library for message type " << package_name <<
"/" << type_name << ". " << reason;
return rcutils_dynamic_loading_error.str();
};
const rosidl_message_type_support_t * get_message_typesupport_handle(
const std::string & type,
const std::string & typesupport_identifier,
rcpputils::SharedLibrary & library)
{
static const std::string typesupport_name = "message";
static const std::string symbol_part_name = "__get_message_type_support_handle__";
static const std::string middle_module_additional = "msg";
try {
std::string symbol_name = typesupport_identifier + "__get_message_type_support_handle__" +
package_name + "__" + (middle_module.empty() ? "msg" : middle_module) + "__" + type_name;
return static_cast<const rosidl_message_type_support_t *>(get_typesupport_handle_impl(
type, typesupport_identifier, typesupport_name, symbol_part_name,
middle_module_additional, library
));
}
const rosidl_message_type_support_t * (* get_ts)() = nullptr;
// This will throw runtime_error if the symbol was not found.
get_ts = reinterpret_cast<decltype(get_ts)>(library.get_symbol(symbol_name));
return get_ts();
} catch (std::runtime_error &) {
throw std::runtime_error{mk_error("Library could not be found.")};
}
const rosidl_service_type_support_t * get_service_typesupport_handle(
const std::string & type,
const std::string & typesupport_identifier,
rcpputils::SharedLibrary & library)
{
static const std::string typesupport_name = "service";
static const std::string symbol_part_name = "__get_service_type_support_handle__";
static const std::string middle_module_additional = "srv";
return static_cast<const rosidl_service_type_support_t *>(get_typesupport_handle_impl(
type, typesupport_identifier, typesupport_name, symbol_part_name,
middle_module_additional, library
));
}
} // namespace rclcpp

View File

@@ -7,14 +7,12 @@ find_package(performance_test_fixture REQUIRED)
add_performance_test(benchmark_client benchmark_client.cpp)
if(TARGET benchmark_client)
target_link_libraries(benchmark_client ${PROJECT_NAME})
ament_target_dependencies(benchmark_client test_msgs rcl_interfaces)
target_link_libraries(benchmark_client ${PROJECT_NAME} ${rcl_interfaces_TARGETS} ${test_msgs_TARGETS})
endif()
add_performance_test(benchmark_executor benchmark_executor.cpp)
if(TARGET benchmark_executor)
target_link_libraries(benchmark_executor ${PROJECT_NAME})
ament_target_dependencies(benchmark_executor test_msgs)
target_link_libraries(benchmark_executor ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
add_performance_test(benchmark_init_shutdown benchmark_init_shutdown.cpp)
@@ -39,6 +37,5 @@ endif()
add_performance_test(benchmark_service benchmark_service.cpp)
if(TARGET benchmark_service)
target_link_libraries(benchmark_service ${PROJECT_NAME})
ament_target_dependencies(benchmark_service test_msgs rcl_interfaces)
target_link_libraries(benchmark_service ${PROJECT_NAME} ${rcl_interfaces_TARGETS} ${test_msgs_TARGETS})
endif()

View File

@@ -34,71 +34,38 @@ if(TARGET test_exceptions)
target_link_libraries(test_exceptions ${PROJECT_NAME} mimick)
endif()
# Increasing timeout because connext can take a long time to destroy nodes
# TODO(brawner) remove when destroying Node for Connext is resolved. See:
# https://github.com/ros2/rclcpp/issues/1250
ament_add_gtest(
test_allocator_memory_strategy
strategies/test_allocator_memory_strategy.cpp
TIMEOUT 360)
ament_add_gtest(test_allocator_memory_strategy strategies/test_allocator_memory_strategy.cpp)
if(TARGET test_allocator_memory_strategy)
ament_target_dependencies(test_allocator_memory_strategy
"rcl"
"test_msgs"
)
target_link_libraries(test_allocator_memory_strategy ${PROJECT_NAME})
target_link_libraries(test_allocator_memory_strategy ${PROJECT_NAME} rcpputils::rcpputils ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_message_pool_memory_strategy strategies/test_message_pool_memory_strategy.cpp)
if(TARGET test_message_pool_memory_strategy)
ament_target_dependencies(test_message_pool_memory_strategy
"rcl"
"test_msgs"
)
target_link_libraries(test_message_pool_memory_strategy ${PROJECT_NAME})
target_link_libraries(test_message_pool_memory_strategy ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_any_service_callback test_any_service_callback.cpp)
if(TARGET test_any_service_callback)
ament_target_dependencies(test_any_service_callback
"test_msgs"
)
target_link_libraries(test_any_service_callback ${PROJECT_NAME})
target_link_libraries(test_any_service_callback ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_any_subscription_callback test_any_subscription_callback.cpp)
if(TARGET test_any_subscription_callback)
ament_target_dependencies(test_any_subscription_callback
"test_msgs"
)
target_link_libraries(test_any_subscription_callback ${PROJECT_NAME})
target_link_libraries(test_any_subscription_callback ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_client test_client.cpp)
if(TARGET test_client)
ament_target_dependencies(test_client
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_client ${PROJECT_NAME} mimick)
target_link_libraries(test_client ${PROJECT_NAME} mimick ${rcl_interfaces_TARGETS} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_copy_all_parameter_values test_copy_all_parameter_values.cpp)
if(TARGET test_copy_all_parameter_values)
target_link_libraries(test_copy_all_parameter_values ${PROJECT_NAME})
endif()
ament_add_gtest(test_create_timer test_create_timer.cpp)
if(TARGET test_create_timer)
ament_target_dependencies(test_create_timer
"rcl_interfaces"
"rmw"
"rcl"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_create_timer ${PROJECT_NAME})
target_include_directories(test_create_timer PRIVATE ./)
endif()
ament_add_gtest(test_create_subscription test_create_subscription.cpp)
if(TARGET test_create_subscription)
target_link_libraries(test_create_subscription ${PROJECT_NAME})
ament_target_dependencies(test_create_subscription
"test_msgs"
)
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})
@@ -107,32 +74,17 @@ function(test_add_callback_groups_to_executor_for_rmw_implementation)
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})
ament_target_dependencies(test_add_callback_groups_to_executor${target_suffix}
"test_msgs"
)
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)
if(TARGET test_expand_topic_or_service_name)
ament_target_dependencies(test_expand_topic_or_service_name
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_expand_topic_or_service_name ${PROJECT_NAME} mimick)
target_link_libraries(test_expand_topic_or_service_name ${PROJECT_NAME} mimick rcl::rcl rmw::rmw)
endif()
ament_add_gtest(test_function_traits test_function_traits.cpp)
if(TARGET test_function_traits)
target_include_directories(test_function_traits PUBLIC ../../include)
ament_target_dependencies(test_function_traits
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_function_traits ${PROJECT_NAME})
endif()
ament_add_gtest(
test_future_return_code
@@ -142,72 +94,33 @@ if(TARGET test_future_return_code)
endif()
ament_add_gmock(test_intra_process_manager test_intra_process_manager.cpp)
if(TARGET test_intra_process_manager)
ament_target_dependencies(test_intra_process_manager
"rcl"
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_intra_process_manager ${PROJECT_NAME})
target_link_libraries(test_intra_process_manager ${PROJECT_NAME} ${rcl_interfaces_TARGETS} rmw::rmw)
endif()
ament_add_gmock(test_intra_process_manager_with_allocators test_intra_process_manager_with_allocators.cpp)
if(TARGET test_intra_process_manager_with_allocators)
ament_target_dependencies(test_intra_process_manager_with_allocators
"test_msgs"
)
target_link_libraries(test_intra_process_manager_with_allocators ${PROJECT_NAME})
target_link_libraries(test_intra_process_manager_with_allocators ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_ring_buffer_implementation test_ring_buffer_implementation.cpp)
if(TARGET test_ring_buffer_implementation)
ament_target_dependencies(test_ring_buffer_implementation
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_ring_buffer_implementation ${PROJECT_NAME})
endif()
ament_add_gtest(test_intra_process_buffer test_intra_process_buffer.cpp)
if(TARGET test_intra_process_buffer)
ament_target_dependencies(test_intra_process_buffer
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_intra_process_buffer ${PROJECT_NAME})
endif()
ament_add_gtest(test_loaned_message test_loaned_message.cpp)
ament_target_dependencies(test_loaned_message
"test_msgs"
)
target_link_libraries(test_loaned_message ${PROJECT_NAME} mimick)
target_link_libraries(test_loaned_message ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
ament_add_gtest(test_memory_strategy test_memory_strategy.cpp)
ament_target_dependencies(test_memory_strategy
"test_msgs"
)
target_link_libraries(test_memory_strategy ${PROJECT_NAME})
target_link_libraries(test_memory_strategy ${PROJECT_NAME} ${test_msgs_TARGETS})
ament_add_gtest(test_message_memory_strategy test_message_memory_strategy.cpp)
ament_target_dependencies(test_message_memory_strategy
"test_msgs"
)
target_link_libraries(test_message_memory_strategy ${PROJECT_NAME})
target_link_libraries(test_message_memory_strategy ${PROJECT_NAME} ${test_msgs_TARGETS})
ament_add_gtest(test_node test_node.cpp TIMEOUT 240)
if(TARGET test_node)
ament_target_dependencies(test_node
"rcl_interfaces"
"rcpputils"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_node ${PROJECT_NAME} mimick)
target_link_libraries(test_node ${PROJECT_NAME} mimick rcpputils::rcpputils rmw::rmw ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_node_interfaces__get_node_interfaces
@@ -218,7 +131,7 @@ endif()
ament_add_gtest(test_node_interfaces__node_base
node_interfaces/test_node_base.cpp)
if(TARGET test_node_interfaces__node_base)
target_link_libraries(test_node_interfaces__node_base ${PROJECT_NAME} mimick)
target_link_libraries(test_node_interfaces__node_base ${PROJECT_NAME} mimick rcl::rcl rmw::rmw)
endif()
ament_add_gtest(test_node_interfaces__node_clock
node_interfaces/test_node_clock.cpp)
@@ -229,48 +142,42 @@ ament_add_gtest(test_node_interfaces__node_graph
node_interfaces/test_node_graph.cpp
TIMEOUT 120)
if(TARGET test_node_interfaces__node_graph)
ament_target_dependencies(
test_node_interfaces__node_graph
"test_msgs")
target_link_libraries(test_node_interfaces__node_graph ${PROJECT_NAME} mimick)
target_link_libraries(test_node_interfaces__node_graph ${PROJECT_NAME} mimick rcl::rcl ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_node_interfaces__node_interfaces
node_interfaces/test_node_interfaces.cpp)
if(TARGET test_node_interfaces__node_interfaces)
target_link_libraries(test_node_interfaces__node_interfaces ${PROJECT_NAME} mimick)
target_link_libraries(test_node_interfaces__node_interfaces ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_parameters
node_interfaces/test_node_parameters.cpp)
if(TARGET test_node_interfaces__node_parameters)
target_link_libraries(test_node_interfaces__node_parameters ${PROJECT_NAME} mimick)
target_link_libraries(test_node_interfaces__node_parameters ${PROJECT_NAME} mimick rcpputils::rcpputils)
endif()
ament_add_gtest(test_node_interfaces__node_services
node_interfaces/test_node_services.cpp)
if(TARGET test_node_interfaces__node_services)
target_link_libraries(test_node_interfaces__node_services ${PROJECT_NAME} mimick)
target_link_libraries(test_node_interfaces__node_services ${PROJECT_NAME} mimick rcl::rcl)
endif()
ament_add_gtest(test_node_interfaces__node_timers
node_interfaces/test_node_timers.cpp)
if(TARGET test_node_interfaces__node_timers)
target_link_libraries(test_node_interfaces__node_timers ${PROJECT_NAME} mimick)
target_link_libraries(test_node_interfaces__node_timers ${PROJECT_NAME} mimick rcl::rcl)
endif()
ament_add_gtest(test_node_interfaces__node_topics
node_interfaces/test_node_topics.cpp)
if(TARGET test_node_interfaces__node_topics)
ament_target_dependencies(
test_node_interfaces__node_topics
"test_msgs")
target_link_libraries(test_node_interfaces__node_topics ${PROJECT_NAME} mimick)
target_link_libraries(test_node_interfaces__node_topics ${PROJECT_NAME} mimick rcl::rcl ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_node_interfaces__node_type_descriptions
node_interfaces/test_node_type_descriptions.cpp)
if(TARGET test_node_interfaces__node_type_descriptions)
target_link_libraries(test_node_interfaces__node_type_descriptions ${PROJECT_NAME} mimick)
target_link_libraries(test_node_interfaces__node_type_descriptions ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__node_waitables
node_interfaces/test_node_waitables.cpp)
if(TARGET test_node_interfaces__node_waitables)
target_link_libraries(test_node_interfaces__node_waitables ${PROJECT_NAME} mimick)
target_link_libraries(test_node_interfaces__node_waitables ${PROJECT_NAME} mimick rcl::rcl)
endif()
ament_add_gtest(test_node_interfaces__test_template_utils # Compile time test
node_interfaces/detail/test_template_utils.cpp)
@@ -301,82 +208,43 @@ endif()
ament_add_gtest(test_node_global_args test_node_global_args.cpp)
if(TARGET test_node_global_args)
ament_target_dependencies(test_node_global_args
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_node_global_args ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_options test_node_options.cpp)
if(TARGET test_node_options)
ament_target_dependencies(test_node_options "rcl")
target_link_libraries(test_node_options ${PROJECT_NAME} mimick)
target_link_libraries(test_node_options ${PROJECT_NAME} mimick rcl::rcl)
endif()
ament_add_gtest(test_init_options test_init_options.cpp)
if(TARGET test_init_options)
ament_target_dependencies(test_init_options "rcl")
target_link_libraries(test_init_options ${PROJECT_NAME} mimick)
target_link_libraries(test_init_options ${PROJECT_NAME} mimick rcl::rcl)
endif()
ament_add_gmock(test_parameter_client test_parameter_client.cpp)
if(TARGET test_parameter_client)
ament_target_dependencies(test_parameter_client
"rcl_interfaces"
)
target_link_libraries(test_parameter_client ${PROJECT_NAME})
target_link_libraries(test_parameter_client ${PROJECT_NAME} ${rcl_interfaces_TARGETS})
endif()
ament_add_gtest(test_parameter_service test_parameter_service.cpp)
if(TARGET test_parameter_service)
ament_target_dependencies(test_parameter_service
"rcl_interfaces"
)
target_link_libraries(test_parameter_service ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_events_filter test_parameter_events_filter.cpp)
if(TARGET test_parameter_events_filter)
ament_target_dependencies(test_parameter_events_filter
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_parameter_events_filter ${PROJECT_NAME})
target_link_libraries(test_parameter_events_filter ${PROJECT_NAME} ${rcl_interfaces_TARGETS})
endif()
ament_add_gtest(test_parameter test_parameter.cpp)
if(TARGET test_parameter)
ament_target_dependencies(test_parameter
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_parameter ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_event_handler test_parameter_event_handler.cpp)
if(TARGET test_parameter_event_handler)
ament_target_dependencies(test_parameter_event_handler
"rcl_interfaces"
"rmw"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_parameter_event_handler ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_map test_parameter_map.cpp)
if(TARGET test_parameter_map)
target_link_libraries(test_parameter_map ${PROJECT_NAME})
target_link_libraries(test_parameter_map ${PROJECT_NAME} rcl::rcl rcl_yaml_param_parser::rcl_yaml_param_parser rcutils::rcutils)
endif()
ament_add_gtest(test_publisher test_publisher.cpp TIMEOUT 120)
if(TARGET test_publisher)
ament_target_dependencies(test_publisher
"rcl"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_publisher ${PROJECT_NAME} mimick)
target_link_libraries(test_publisher ${PROJECT_NAME} mimick rcl::rcl rcutils::rcutils ${test_msgs_TARGETS})
endif()
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
@@ -406,32 +274,22 @@ ament_add_gtest(test_subscription_publisher_with_same_type_adapter test_subscrip
APPEND_LIBRARY_DIRS "${append_library_dirs}"
)
if(TARGET test_subscription_publisher_with_same_type_adapter)
ament_target_dependencies(test_subscription_publisher_with_same_type_adapter
"statistics_msgs"
)
target_link_libraries(test_subscription_publisher_with_same_type_adapter
${PROJECT_NAME}
${cpp_typesupport_target})
${cpp_typesupport_target}
${statistics_msgs_TARGETS}
)
endif()
ament_add_gtest(test_publisher_subscription_count_api test_publisher_subscription_count_api.cpp)
if(TARGET test_publisher_subscription_count_api)
ament_target_dependencies(test_publisher_subscription_count_api
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_publisher_subscription_count_api ${PROJECT_NAME})
target_link_libraries(test_publisher_subscription_count_api ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_qos test_qos.cpp)
if(TARGET test_qos)
ament_target_dependencies(test_qos
"rmw"
)
target_link_libraries(test_qos
${PROJECT_NAME}
rmw::rmw
)
endif()
function(test_generic_pubsub_for_rmw_implementation)
@@ -440,12 +298,7 @@ function(test_generic_pubsub_for_rmw_implementation)
ENV ${rmw_implementation_env_var}
)
if(TARGET test_generic_pubsub${target_suffix})
target_link_libraries(test_generic_pubsub${target_suffix} ${PROJECT_NAME})
ament_target_dependencies(test_generic_pubsub${target_suffix}
"rcpputils"
"rosidl_typesupport_cpp"
"test_msgs"
)
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)
@@ -456,153 +309,79 @@ function(test_qos_event_for_rmw_implementation)
ENV ${rmw_implementation_env_var}
)
if(TARGET test_qos_event${target_suffix})
target_link_libraries(test_qos_event${target_suffix} ${PROJECT_NAME} mimick)
ament_target_dependencies(test_qos_event${target_suffix}
"rmw"
"rosidl_typesupport_cpp"
"test_msgs"
)
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)
target_link_libraries(test_qos_overriding_options
${PROJECT_NAME}
)
target_link_libraries(test_qos_overriding_options ${PROJECT_NAME})
endif()
ament_add_gmock(test_qos_parameters test_qos_parameters.cpp)
if(TARGET test_qos_parameters)
target_link_libraries(test_qos_parameters
${PROJECT_NAME}
)
target_link_libraries(test_qos_parameters ${PROJECT_NAME})
endif()
ament_add_gtest(test_rate test_rate.cpp)
if(TARGET test_rate)
ament_target_dependencies(test_rate
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_rate
${PROJECT_NAME}
)
target_link_libraries(test_rate ${PROJECT_NAME})
endif()
ament_add_gtest(test_serialized_message_allocator test_serialized_message_allocator.cpp)
if(TARGET test_serialized_message_allocator)
ament_target_dependencies(test_serialized_message_allocator
test_msgs
)
target_link_libraries(test_serialized_message_allocator
${PROJECT_NAME}
)
target_link_libraries(test_serialized_message_allocator ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_serialized_message test_serialized_message.cpp)
if(TARGET test_serialized_message)
ament_target_dependencies(test_serialized_message
test_msgs
)
target_link_libraries(test_serialized_message
${PROJECT_NAME}
)
target_link_libraries(test_serialized_message ${PROJECT_NAME} rcpputils::rcpputils ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_service test_service.cpp)
if(TARGET test_service)
ament_target_dependencies(test_service
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_service ${PROJECT_NAME} mimick)
target_link_libraries(test_service ${PROJECT_NAME} mimick ${rcl_interfaces_TARGES} ${test_msgs_TARGETS})
endif()
ament_add_gmock(test_service_introspection test_service_introspection.cpp)
if(TARGET test_service)
ament_target_dependencies(test_service_introspection
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_service_introspection ${PROJECT_NAME} mimick)
if(TARGET test_service_introspection)
target_link_libraries(test_service_introspection ${PROJECT_NAME} mimick ${service_msgs_TARGETS} ${test_msgs_TARGETS})
endif()
# Creating and destroying nodes is slow with Connext, so this needs larger timeout.
ament_add_gtest(test_subscription test_subscription.cpp TIMEOUT 120)
if(TARGET test_subscription)
ament_target_dependencies(test_subscription
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_subscription ${PROJECT_NAME} mimick)
target_link_libraries(test_subscription ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_subscription_publisher_count_api test_subscription_publisher_count_api.cpp)
if(TARGET test_subscription_publisher_count_api)
ament_target_dependencies(test_subscription_publisher_count_api
"rcl_interfaces"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_subscription_publisher_count_api ${PROJECT_NAME})
target_link_libraries(test_subscription_publisher_count_api ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_subscription_traits test_subscription_traits.cpp)
if(TARGET test_subscription_traits)
ament_target_dependencies(test_subscription_traits
"rcl"
"test_msgs"
)
target_link_libraries(test_subscription_traits ${PROJECT_NAME})
target_link_libraries(test_subscription_traits ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_type_support test_type_support.cpp)
if(TARGET test_type_support)
ament_target_dependencies(test_type_support
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"test_msgs"
)
target_link_libraries(test_type_support ${PROJECT_NAME})
target_link_libraries(test_type_support ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gmock(test_typesupport_helpers test_typesupport_helpers.cpp)
if(TARGET test_typesupport_helpers)
target_link_libraries(test_typesupport_helpers ${PROJECT_NAME})
target_link_libraries(test_typesupport_helpers ${PROJECT_NAME} rcpputils::rcpputils)
endif()
ament_add_gtest(test_find_weak_nodes test_find_weak_nodes.cpp)
if(TARGET test_find_weak_nodes)
ament_target_dependencies(test_find_weak_nodes
"rcl"
)
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
endif()
ament_add_gtest(test_externally_defined_services test_externally_defined_services.cpp)
ament_target_dependencies(test_externally_defined_services
"rcl"
"test_msgs"
)
target_link_libraries(test_externally_defined_services ${PROJECT_NAME})
target_link_libraries(test_externally_defined_services ${PROJECT_NAME} rcl::rcl ${test_msgs_TARGETS})
ament_add_gtest(test_duration test_duration.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_duration)
ament_target_dependencies(test_duration
"rcl")
target_link_libraries(test_duration ${PROJECT_NAME})
target_link_libraries(test_duration ${PROJECT_NAME} rcl::rcl)
endif()
ament_add_gtest(test_logger test_logger.cpp)
target_link_libraries(test_logger ${PROJECT_NAME})
target_link_libraries(test_logger ${PROJECT_NAME} rcutils::rcutils)
ament_add_gmock(test_logging test_logging.cpp)
target_link_libraries(test_logging ${PROJECT_NAME})
target_link_libraries(test_logging ${PROJECT_NAME} rcutils::rcutils)
ament_add_gmock(test_context test_context.cpp)
target_link_libraries(test_context ${PROJECT_NAME})
@@ -610,134 +389,96 @@ target_link_libraries(test_context ${PROJECT_NAME})
ament_add_gtest(test_time test_time.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_time)
ament_target_dependencies(test_time
"rcl")
target_link_libraries(test_time ${PROJECT_NAME})
target_link_libraries(test_time ${PROJECT_NAME} rcl::rcl rcutils::rcutils)
endif()
ament_add_gtest(test_timer test_timer.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_timer)
ament_target_dependencies(test_timer
"rcl")
target_link_libraries(test_timer ${PROJECT_NAME} mimick)
target_link_libraries(test_timer ${PROJECT_NAME} mimick rcl::rcl)
endif()
ament_add_gtest(test_timers_manager test_timers_manager.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_timers_manager)
ament_target_dependencies(test_timers_manager
"rcl")
target_link_libraries(test_timers_manager ${PROJECT_NAME} mimick)
target_link_libraries(test_timers_manager ${PROJECT_NAME})
endif()
ament_add_gtest(test_time_source test_time_source.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_time_source)
ament_target_dependencies(test_time_source
"rcl")
target_link_libraries(test_time_source ${PROJECT_NAME})
target_link_libraries(test_time_source ${PROJECT_NAME} rcl::rcl)
endif()
ament_add_gtest(test_utilities test_utilities.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_utilities)
ament_target_dependencies(test_utilities
"rcl")
target_link_libraries(test_utilities ${PROJECT_NAME} mimick)
target_link_libraries(test_utilities ${PROJECT_NAME} mimick rcl::rcl)
endif()
ament_add_gtest(test_wait_for_message test_wait_for_message.cpp)
if(TARGET test_wait_for_message)
ament_target_dependencies(test_wait_for_message
"test_msgs")
target_link_libraries(test_wait_for_message ${PROJECT_NAME})
target_link_libraries(test_wait_for_message ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_logger_service test_logger_service.cpp)
if(TARGET test_logger_service)
ament_target_dependencies(test_logger_service
"rcl_interfaces")
target_link_libraries(test_logger_service ${PROJECT_NAME})
target_link_libraries(test_logger_service ${PROJECT_NAME} ${rcl_interfaces_TARGETS})
endif()
ament_add_gtest(test_interface_traits test_interface_traits.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_interface_traits)
ament_target_dependencies(test_interface_traits
"rcl")
target_link_libraries(test_interface_traits ${PROJECT_NAME})
endif()
# TODO(brawner) remove when destroying Node for Connext is resolved. See:
# https://github.com/ros2/rclcpp/issues/1250
ament_add_gtest(
test_executors
executors/test_executors.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 180)
if(TARGET test_executors)
ament_target_dependencies(test_executors
"rcl"
"test_msgs")
target_link_libraries(test_executors ${PROJECT_NAME})
target_link_libraries(test_executors ${PROJECT_NAME} rcl::rcl ${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}")
if(TARGET test_static_single_threaded_executor)
ament_target_dependencies(test_static_single_threaded_executor
"test_msgs")
target_link_libraries(test_static_single_threaded_executor ${PROJECT_NAME} mimick)
target_link_libraries(test_static_single_threaded_executor ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_multi_threaded_executor executors/test_multi_threaded_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_multi_threaded_executor)
ament_target_dependencies(test_multi_threaded_executor
"rcl")
target_link_libraries(test_multi_threaded_executor ${PROJECT_NAME})
endif()
ament_add_gtest(test_static_executor_entities_collector executors/test_static_executor_entities_collector.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}" TIMEOUT 120)
if(TARGET test_static_executor_entities_collector)
ament_target_dependencies(test_static_executor_entities_collector
"rcl"
"test_msgs")
target_link_libraries(test_static_executor_entities_collector ${PROJECT_NAME} mimick)
target_link_libraries(test_static_executor_entities_collector ${PROJECT_NAME} mimick rcpputils::rcpputils ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_entities_collector executors/test_entities_collector.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}" TIMEOUT 120)
if(TARGET test_entities_collector)
ament_target_dependencies(test_entities_collector
"rcl"
"test_msgs")
target_link_libraries(test_entities_collector ${PROJECT_NAME} mimick)
target_link_libraries(test_entities_collector ${PROJECT_NAME})
endif()
ament_add_gtest(test_executor_notify_waitable executors/test_executor_notify_waitable.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}" TIMEOUT 120)
if(TARGET test_executor_notify_waitable)
ament_target_dependencies(test_executor_notify_waitable
"rcl"
"test_msgs")
target_link_libraries(test_executor_notify_waitable ${PROJECT_NAME} mimick)
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)
if(TARGET test_events_executor)
ament_target_dependencies(test_events_executor
"test_msgs")
target_link_libraries(test_events_executor ${PROJECT_NAME})
target_link_libraries(test_events_executor ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_events_queue executors/test_events_queue.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_events_queue)
ament_target_dependencies(test_events_queue
"test_msgs")
target_link_libraries(test_events_queue ${PROJECT_NAME})
endif()
@@ -750,76 +491,60 @@ endif()
ament_add_gtest(test_wait_set test_wait_set.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_wait_set)
ament_target_dependencies(test_wait_set "test_msgs")
target_link_libraries(test_wait_set ${PROJECT_NAME})
target_link_libraries(test_wait_set ${PROJECT_NAME} ${rcl_interfaces_TARGETS} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_subscription_topic_statistics topic_statistics/test_subscription_topic_statistics.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
)
if(TARGET test_subscription_topic_statistics)
ament_target_dependencies(test_subscription_topic_statistics
"builtin_interfaces"
"libstatistics_collector"
"rcl_interfaces"
"rcutils"
"rmw"
"rosidl_runtime_cpp"
"rosidl_typesupport_cpp"
"statistics_msgs"
"test_msgs")
target_link_libraries(test_subscription_topic_statistics
${PROJECT_NAME}
${cpp_typesupport_target})
libstatistics_collector::libstatistics_collector
${statistics_msgs_TARGETS}
${test_msgs_TARGETS}
)
endif()
ament_add_gtest(test_subscription_options test_subscription_options.cpp)
if(TARGET test_subscription_options)
ament_target_dependencies(test_subscription_options "rcl")
target_link_libraries(test_subscription_options ${PROJECT_NAME})
endif()
ament_add_gtest(test_dynamic_storage wait_set_policies/test_dynamic_storage.cpp)
if(TARGET test_dynamic_storage)
ament_target_dependencies(test_dynamic_storage "rcl" "test_msgs")
target_link_libraries(test_dynamic_storage ${PROJECT_NAME})
target_link_libraries(test_dynamic_storage ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_storage_policy_common wait_set_policies/test_storage_policy_common.cpp)
if(TARGET test_storage_policy_common)
ament_target_dependencies(test_storage_policy_common "rcl" "test_msgs")
target_link_libraries(test_storage_policy_common ${PROJECT_NAME} mimick)
target_link_libraries(test_storage_policy_common ${PROJECT_NAME} mimick ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_static_storage wait_set_policies/test_static_storage.cpp)
if(TARGET test_static_storage)
ament_target_dependencies(test_static_storage "rcl" "test_msgs")
target_link_libraries(test_static_storage ${PROJECT_NAME})
target_link_libraries(test_static_storage ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_thread_safe_synchronization wait_set_policies/test_thread_safe_synchronization.cpp)
if(TARGET test_thread_safe_synchronization)
ament_target_dependencies(test_thread_safe_synchronization "rcl" "test_msgs")
target_link_libraries(test_thread_safe_synchronization ${PROJECT_NAME})
target_link_libraries(test_thread_safe_synchronization ${PROJECT_NAME} ${test_msgs_TARGETS})
endif()
ament_add_gtest(test_rosout_qos test_rosout_qos.cpp)
if(TARGET test_rosout_qos)
ament_target_dependencies(test_rosout_qos "rcl")
target_link_libraries(test_rosout_qos ${PROJECT_NAME})
target_link_libraries(test_rosout_qos ${PROJECT_NAME} rcl::rcl rmw::rmw)
endif()
ament_add_gtest(test_rosout_subscription test_rosout_subscription.cpp)
if(TARGET test_rosout_subscription)
ament_target_dependencies(test_rosout_subscription "rcl")
target_link_libraries(test_rosout_subscription ${PROJECT_NAME})
target_link_libraries(test_rosout_subscription ${PROJECT_NAME} ${rcl_interfaces_TARGETS})
endif()
ament_add_gtest(test_executor test_executor.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}"
TIMEOUT 120)
if(TARGET test_executor)
ament_target_dependencies(test_executor "rcl")
target_link_libraries(test_executor ${PROJECT_NAME} mimick)
endif()
@@ -836,12 +561,7 @@ function(test_subscription_content_filter_for_rmw_implementation)
TIMEOUT 120
)
if(TARGET test_subscription_content_filter${target_suffix})
target_link_libraries(test_subscription_content_filter${target_suffix} ${PROJECT_NAME} mimick)
ament_target_dependencies(test_subscription_content_filter${target_suffix}
"rcpputils"
"rosidl_typesupport_cpp"
"test_msgs"
)
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)

View File

@@ -644,6 +644,7 @@ TYPED_TEST(TestExecutors, testRaceConditionAddNode)
break;
}
total += k * (i + 42);
(void)total;
}
});
}

View File

@@ -129,6 +129,9 @@ TEST_F(TestNodeGraph, construct_from_node)
EXPECT_EQ(0u, node_graph()->count_publishers("not_a_topic"));
EXPECT_EQ(0u, node_graph()->count_subscribers("not_a_topic"));
EXPECT_EQ(0u, node_graph()->count_clients("not_a_service"));
EXPECT_EQ(0u, node_graph()->count_services("not_a_service"));
EXPECT_NE(nullptr, node_graph()->get_graph_guard_condition());
// get_graph_event is non-const
@@ -534,6 +537,22 @@ TEST_F(TestNodeGraph, count_subscribers_rcl_error)
std::runtime_error("could not count subscribers: error not set"));
}
TEST_F(TestNodeGraph, count_clients_rcl_error)
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_count_clients, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->count_clients("service"),
std::runtime_error("could not count clients: error not set"));
}
TEST_F(TestNodeGraph, count_services_rcl_error)
{
auto mock = mocking_utils::patch_and_return("lib:rclcpp", rcl_count_services, RCL_RET_ERROR);
RCLCPP_EXPECT_THROW_EQ(
node_graph()->count_services("service"),
std::runtime_error("could not count services: error not set"));
}
TEST_F(TestNodeGraph, notify_shutdown)
{
EXPECT_NO_THROW(node()->get_node_graph_interface()->notify_shutdown());

View File

@@ -95,15 +95,15 @@ TEST_F(TestNodeParameters, list_parameters)
std::find(list_result2.names.begin(), list_result2.names.end(), parameter_name),
list_result2.names.end());
// Check prefixes
// Check prefixes and the depth relative to the given prefixes
const std::string parameter_name2 = "prefix.new_parameter";
const rclcpp::ParameterValue value2(true);
const rcl_interfaces::msg::ParameterDescriptor descriptor2;
const auto added_parameter_value2 =
node_parameters->declare_parameter(parameter_name2, value2, descriptor2, false);
EXPECT_EQ(value.get<bool>(), added_parameter_value.get<bool>());
EXPECT_EQ(value2.get<bool>(), added_parameter_value2.get<bool>());
prefixes = {"prefix"};
auto list_result3 = node_parameters->list_parameters(prefixes, 2u);
auto list_result3 = node_parameters->list_parameters(prefixes, 1u);
EXPECT_EQ(1u, list_result3.names.size());
EXPECT_NE(
std::find(list_result3.names.begin(), list_result3.names.end(), parameter_name2),
@@ -116,6 +116,13 @@ TEST_F(TestNodeParameters, list_parameters)
EXPECT_NE(
std::find(list_result4.names.begin(), list_result4.names.end(), parameter_name),
list_result4.names.end());
// Return all parameters when the depth = 0
auto list_result5 = node_parameters->list_parameters(prefixes, 0u);
EXPECT_EQ(1u, list_result5.names.size());
EXPECT_NE(
std::find(list_result5.names.begin(), list_result5.names.end(), parameter_name),
list_result5.names.end());
}
TEST_F(TestNodeParameters, parameter_overrides)

View File

@@ -43,10 +43,9 @@ TEST_F(TestNodeTypeDescriptions, disabled_no_service)
node_options.append_parameter_override("start_type_description_service", false);
rclcpp::Node node{"node", "ns", node_options};
rcl_node_t * rcl_node = node.get_node_base_interface()->get_rcl_node_handle();
rcl_service_t * srv = nullptr;
rcl_ret_t ret = rcl_node_get_type_description_service(rcl_node, &srv);
ASSERT_EQ(RCL_RET_NOT_INIT, ret);
auto services = node.get_node_graph_interface()->get_service_names_and_types_by_node(
"node", "/ns");
EXPECT_TRUE(services.find("/ns/node/get_type_description") == services.end());
}
TEST_F(TestNodeTypeDescriptions, enabled_creates_service)
@@ -55,9 +54,8 @@ TEST_F(TestNodeTypeDescriptions, enabled_creates_service)
node_options.append_parameter_override("start_type_description_service", true);
rclcpp::Node node{"node", "ns", node_options};
rcl_node_t * rcl_node = node.get_node_base_interface()->get_rcl_node_handle();
rcl_service_t * srv = nullptr;
rcl_ret_t ret = rcl_node_get_type_description_service(rcl_node, &srv);
ASSERT_EQ(RCL_RET_OK, ret);
ASSERT_NE(nullptr, srv);
auto services = node.get_node_graph_interface()->get_service_names_and_types_by_node(
"node", "/ns");
EXPECT_TRUE(services.find("/ns/node/get_type_description") != services.end());
}

View File

@@ -56,18 +56,28 @@ TEST_F(TestMessagePoolMemoryStrategy, borrow_too_many) {
// Size is 1, borrowing second time should fail
RCLCPP_EXPECT_THROW_EQ(
message_memory_strategy_->borrow_message(),
std::runtime_error("Tried to access message that was still in use! Abort."));
std::runtime_error("No more free slots in the pool"));
EXPECT_NO_THROW(message_memory_strategy_->return_message(message));
}
TEST_F(TestMessagePoolMemoryStrategy, return_unrecognized) {
auto message = message_memory_strategy_->borrow_message();
ASSERT_NE(nullptr, message);
TEST_F(TestMessagePoolMemoryStrategy, borrow_hold_reference) {
{
auto message = message_memory_strategy_->borrow_message();
ASSERT_NE(nullptr, message);
auto unrecognized = std::make_shared<test_msgs::msg::Empty>();
// Unrecognized does not belong to pool
RCLCPP_EXPECT_THROW_EQ(
message_memory_strategy_->return_message(unrecognized),
std::runtime_error("Unrecognized message ptr in return_message."));
EXPECT_NO_THROW(message_memory_strategy_->return_message(message));
// Return it.
EXPECT_NO_THROW(message_memory_strategy_->return_message(message));
// But we are still holding the reference, so we expect that there is still no room in the pool.
RCLCPP_EXPECT_THROW_EQ(
message_memory_strategy_->borrow_message(),
std::runtime_error("No more free slots in the pool"));
}
// Now that we've dropped the reference (left the scope), we expect to be able to borrow again.
auto message2 = message_memory_strategy_->borrow_message();
ASSERT_NE(nullptr, message2);
EXPECT_NO_THROW(message_memory_strategy_->return_message(message2));
}

View File

@@ -214,3 +214,25 @@ TEST(TestContext, check_on_shutdown_callback_order_after_del) {
EXPECT_TRUE(result[0] == 1 && result[1] == 3 && result[2] == 4 && result[3] == 0);
}
// This test checks that contexts will be properly destroyed when leaving a scope, after a
// guard condition has been created.
TEST(TestContext, check_context_destroyed) {
rclcpp::Context::SharedPtr ctx;
{
ctx = std::make_shared<rclcpp::Context>();
ctx->init(0, nullptr);
auto group = std::make_shared<rclcpp::CallbackGroup>(
rclcpp::CallbackGroupType::MutuallyExclusive,
ctx->weak_from_this(),
false);
rclcpp::GuardCondition::SharedPtr gc = group->get_notify_guard_condition();
ASSERT_NE(gc, nullptr);
ASSERT_EQ(ctx.use_count(), 1u);
}
ASSERT_EQ(ctx.use_count(), 1u);
}

View File

@@ -0,0 +1,88 @@
// Copyright 2023 Open Navigation LLC
//
// 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 "rclcpp/copy_all_parameter_values.hpp"
#include "rclcpp/rclcpp.hpp"
class TestNode : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
static void TearDownTestCase()
{
rclcpp::shutdown();
}
};
TEST_F(TestNode, TestParamCopying)
{
auto node1 = std::make_shared<rclcpp::Node>("test_node1");
auto node2 = std::make_shared<rclcpp::Node>("test_node2");
// Tests for (1) multiple types, (2) recursion, (3) overriding values
node1->declare_parameter("Foo1", rclcpp::ParameterValue(std::string(("bar1"))));
node1->declare_parameter("Foo2", rclcpp::ParameterValue(0.123));
node1->declare_parameter("Foo", rclcpp::ParameterValue(std::string(("bar"))));
node1->declare_parameter("Foo.bar", rclcpp::ParameterValue(std::string(("steve"))));
node2->declare_parameter("Foo", rclcpp::ParameterValue(std::string(("barz2"))));
// Show Node2 is empty of Node1's parameters, but contains its own
EXPECT_FALSE(node2->has_parameter("Foo1"));
EXPECT_FALSE(node2->has_parameter("Foo2"));
EXPECT_FALSE(node2->has_parameter("Foo.bar"));
EXPECT_TRUE(node2->has_parameter("Foo"));
EXPECT_EQ(node2->get_parameter("Foo").as_string(), std::string("barz2"));
bool override = false;
rclcpp::copy_all_parameter_values(node1, node2, override);
// Test new parameters exist, of expected value, and original param is not overridden
EXPECT_TRUE(node2->has_parameter("Foo1"));
EXPECT_EQ(node2->get_parameter("Foo1").as_string(), std::string("bar1"));
EXPECT_TRUE(node2->has_parameter("Foo2"));
EXPECT_EQ(node2->get_parameter("Foo2").as_double(), 0.123);
EXPECT_TRUE(node2->has_parameter("Foo.bar"));
EXPECT_EQ(node2->get_parameter("Foo.bar").as_string(), std::string("steve"));
EXPECT_TRUE(node2->has_parameter("Foo"));
EXPECT_EQ(node2->get_parameter("Foo").as_string(), std::string("barz2"));
// Test if parameter overrides are permissible that Node2's value is overridden
override = true;
rclcpp::copy_all_parameter_values(node1, node2, override);
EXPECT_EQ(node2->get_parameter("Foo").as_string(), std::string("bar"));
}
TEST_F(TestNode, TestParamCopyingExceptions)
{
auto node1 = std::make_shared<rclcpp::Node>("test_node1");
auto node2 = std::make_shared<rclcpp::Node>("test_node2");
// Tests for Parameter value conflicts handled
node1->declare_parameter("Foo", rclcpp::ParameterValue(std::string(("bar"))));
node2->declare_parameter("Foo", rclcpp::ParameterValue(0.123));
bool override = true;
EXPECT_NO_THROW(
rclcpp::copy_all_parameter_values(node1, node2, override));
// Tests for Parameter read-only handled
node1->declare_parameter("Foo1", rclcpp::ParameterValue(std::string(("bar"))));
node2->declare_parameter("Foo1", rclcpp::ParameterValue(0.123));
EXPECT_NO_THROW(rclcpp::copy_all_parameter_values(node1, node2, override));
}

View File

@@ -69,7 +69,7 @@ public:
size_t counter = 0;
auto subscription = node_->create_generic_subscription(
topic_name, type, rclcpp::QoS(1),
[&counter, &messages, this](std::shared_ptr<rclcpp::SerializedMessage> message) {
[&counter, &messages, this](const std::shared_ptr<const rclcpp::SerializedMessage> message) {
T2 deserialized_message;
rclcpp::Serialization<T2> serializer;
serializer.deserialize_message(message.get(), &deserialized_message);
@@ -236,7 +236,7 @@ TEST_F(RclcppGenericNodeFixture, generic_subscription_uses_qos)
auto publisher = node_->create_publisher<test_msgs::msg::Strings>(topic_name, qos);
auto subscription = node_->create_generic_subscription(
topic_name, topic_type, qos,
[](std::shared_ptr<rclcpp::SerializedMessage>/* message */) {});
[](std::shared_ptr<const rclcpp::SerializedMessage>/* message */) {});
auto connected = [publisher, subscription]() -> bool {
return publisher->get_subscription_count() && subscription->get_publisher_count();
};
@@ -263,3 +263,49 @@ TEST_F(RclcppGenericNodeFixture, generic_publisher_uses_qos)
// It normally takes < 20ms, 5s chosen as "a very long time"
ASSERT_TRUE(wait_for(connected, 5s));
}
TEST_F(RclcppGenericNodeFixture, generic_subscription_different_callbacks)
{
using namespace std::chrono_literals;
std::string topic_name = "string_topic";
std::string topic_type = "test_msgs/msg/Strings";
rclcpp::QoS qos = rclcpp::QoS(1);
auto publisher = node_->create_publisher<test_msgs::msg::Strings>(topic_name, qos);
// Test shared_ptr for const messages
{
auto subscription = node_->create_generic_subscription(
topic_name, topic_type, qos,
[](const std::shared_ptr<const rclcpp::SerializedMessage>/* message */) {});
auto connected = [publisher, subscription]() -> bool {
return publisher->get_subscription_count() && subscription->get_publisher_count();
};
// It normally takes < 20ms, 5s chosen as "a very long time"
ASSERT_TRUE(wait_for(connected, 5s));
}
// Test unique_ptr
{
auto subscription = node_->create_generic_subscription(
topic_name, topic_type, qos,
[](std::unique_ptr<rclcpp::SerializedMessage>/* message */) {});
auto connected = [publisher, subscription]() -> bool {
return publisher->get_subscription_count() && subscription->get_publisher_count();
};
// It normally takes < 20ms, 5s chosen as "a very long time"
ASSERT_TRUE(wait_for(connected, 5s));
}
// Test message callback
{
auto subscription = node_->create_generic_subscription(
topic_name, topic_type, qos,
[](rclcpp::SerializedMessage /* message */) {});
auto connected = [publisher, subscription]() -> bool {
return publisher->get_subscription_count() && subscription->get_publisher_count();
};
// It normally takes < 20ms, 5s chosen as "a very long time"
ASSERT_TRUE(wait_for(connected, 5s));
}
}

View File

@@ -66,16 +66,6 @@ TEST_F(TestGuardCondition, construction_and_destruction) {
}
}
/*
* Testing context accessor.
*/
TEST_F(TestGuardCondition, get_context) {
{
auto gc = std::make_shared<rclcpp::GuardCondition>();
gc->get_context();
}
}
/*
* Testing rcl guard condition accessor.
*/

View File

@@ -190,6 +190,33 @@ TEST(TestIntraProcessBuffer, shared_buffer_consume) {
EXPECT_EQ(1L, original_shared_msg.use_count());
EXPECT_EQ(*original_shared_msg, *popped_unique_msg);
EXPECT_NE(original_message_pointer, popped_message_pointer);
original_shared_msg = std::make_shared<char>('c');
original_message_pointer = reinterpret_cast<std::uintptr_t>(original_shared_msg.get());
auto original_shared_msg_2 = std::make_shared<char>('d');
auto original_message_pointer_2 = reinterpret_cast<std::uintptr_t>(original_shared_msg_2.get());
intra_process_buffer.add_shared(original_shared_msg);
intra_process_buffer.add_shared(original_shared_msg_2);
auto shared_data_vec = intra_process_buffer.get_all_data_shared();
EXPECT_EQ(2L, shared_data_vec.size());
EXPECT_EQ(3L, original_shared_msg.use_count());
EXPECT_EQ(original_shared_msg.use_count(), shared_data_vec[0].use_count());
EXPECT_EQ(*original_shared_msg, *shared_data_vec[0]);
EXPECT_EQ(original_message_pointer, reinterpret_cast<std::uintptr_t>(shared_data_vec[0].get()));
EXPECT_EQ(3L, original_shared_msg_2.use_count());
EXPECT_EQ(original_shared_msg_2.use_count(), shared_data_vec[1].use_count());
EXPECT_EQ(*original_shared_msg_2, *shared_data_vec[1]);
EXPECT_EQ(original_message_pointer_2, reinterpret_cast<std::uintptr_t>(shared_data_vec[1].get()));
auto unique_data_vec = intra_process_buffer.get_all_data_unique();
EXPECT_EQ(2L, unique_data_vec.size());
EXPECT_EQ(3L, original_shared_msg.use_count());
EXPECT_EQ(*original_shared_msg, *unique_data_vec[0]);
EXPECT_NE(original_message_pointer, reinterpret_cast<std::uintptr_t>(unique_data_vec[0].get()));
EXPECT_EQ(3L, original_shared_msg_2.use_count());
EXPECT_EQ(*original_shared_msg_2, *unique_data_vec[1]);
EXPECT_NE(original_message_pointer_2, reinterpret_cast<std::uintptr_t>(unique_data_vec[1].get()));
}
/*
@@ -237,6 +264,33 @@ TEST(TestIntraProcessBuffer, unique_buffer_consume) {
EXPECT_EQ(original_value, *popped_unique_msg);
EXPECT_EQ(original_message_pointer, popped_message_pointer);
original_unique_msg = std::make_unique<char>('c');
original_message_pointer = reinterpret_cast<std::uintptr_t>(original_unique_msg.get());
original_value = *original_unique_msg;
auto original_unique_msg_2 = std::make_unique<char>('d');
auto original_message_pointer_2 = reinterpret_cast<std::uintptr_t>(original_unique_msg.get());
auto original_value_2 = *original_unique_msg_2;
intra_process_buffer.add_unique(std::move(original_unique_msg));
intra_process_buffer.add_unique(std::move(original_unique_msg_2));
auto shared_data_vec = intra_process_buffer.get_all_data_shared();
EXPECT_EQ(2L, shared_data_vec.size());
EXPECT_EQ(1L, shared_data_vec[0].use_count());
EXPECT_EQ(original_value, *shared_data_vec[0]);
EXPECT_NE(original_message_pointer, reinterpret_cast<std::uintptr_t>(shared_data_vec[0].get()));
EXPECT_EQ(1L, shared_data_vec[1].use_count());
EXPECT_EQ(original_value_2, *shared_data_vec[1]);
EXPECT_NE(original_message_pointer_2, reinterpret_cast<std::uintptr_t>(shared_data_vec[1].get()));
auto unique_data_vec = intra_process_buffer.get_all_data_unique();
EXPECT_EQ(2L, unique_data_vec.size());
EXPECT_EQ(1L, shared_data_vec[0].use_count());
EXPECT_EQ(original_value, *unique_data_vec[0]);
EXPECT_NE(original_message_pointer, reinterpret_cast<std::uintptr_t>(unique_data_vec[0].get()));
EXPECT_EQ(1L, shared_data_vec[1].use_count());
EXPECT_EQ(original_value_2, *unique_data_vec[1]);
EXPECT_NE(original_message_pointer_2, reinterpret_cast<std::uintptr_t>(unique_data_vec[1].get()));
}
/*

View File

@@ -31,6 +31,111 @@
// NOLINTNEXTLINE(build/include_order)
#include <rcl_interfaces/msg/log.hpp>
namespace rclcpp
{
namespace experimental
{
namespace buffers
{
namespace mock
{
class IntraProcessBufferBase
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(IntraProcessBufferBase)
virtual ~IntraProcessBufferBase() {}
};
} // namespace mock
} // namespace buffers
} // namespace experimental
} // namespace rclcpp
namespace rclcpp
{
namespace experimental
{
namespace buffers
{
namespace mock
{
template<
typename MessageT,
typename Alloc = std::allocator<MessageT>,
typename MessageDeleter = std::default_delete<MessageT>>
class IntraProcessBuffer : public IntraProcessBufferBase
{
public:
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT>;
RCLCPP_SMART_PTR_DEFINITIONS(IntraProcessBuffer)
IntraProcessBuffer()
{}
void add(ConstMessageSharedPtr msg)
{
message_ptr = reinterpret_cast<std::uintptr_t>(msg.get());
shared_msg = msg;
++num_msgs;
}
void add(MessageUniquePtr msg)
{
message_ptr = reinterpret_cast<std::uintptr_t>(msg.get());
unique_msg = std::move(msg);
++num_msgs;
}
void pop(std::uintptr_t & msg_ptr)
{
msg_ptr = message_ptr;
message_ptr = 0;
--num_msgs;
}
size_t size() const
{
return num_msgs;
}
std::vector<ConstMessageSharedPtr> get_all_data_shared()
{
if (shared_msg) {
return {shared_msg};
} else if (unique_msg) {
return {std::make_shared<const MessageT>(*unique_msg)};
}
return {};
}
std::vector<MessageUniquePtr> get_all_data_unique()
{
std::vector<MessageUniquePtr> result;
if (shared_msg) {
result.push_back(std::make_unique<MessageT>(*shared_msg));
} else if (unique_msg) {
result.push_back(std::make_unique<MessageT>(*unique_msg));
}
return result;
}
// need to store the messages somewhere otherwise the memory address will be reused
ConstMessageSharedPtr shared_msg;
MessageUniquePtr unique_msg;
std::uintptr_t message_ptr;
// count add and pop
size_t num_msgs = 0u;
};
} // namespace mock
} // namespace buffers
} // namespace experimental
} // namespace rclcpp
namespace rclcpp
{
// forward declaration
@@ -80,6 +185,12 @@ public:
return qos_profile;
}
bool
is_durability_transient_local() const
{
return qos_profile.durability() == rclcpp::DurabilityPolicy::TransientLocal;
}
bool
operator==(const rmw_gid_t & gid) const
{
@@ -117,6 +228,9 @@ public:
{
auto allocator = std::make_shared<Alloc>();
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
if (qos.durability() == rclcpp::DurabilityPolicy::TransientLocal) {
buffer = std::make_shared<rclcpp::experimental::buffers::mock::IntraProcessBuffer<T>>();
}
}
// The following functions use the IntraProcessManager
@@ -124,75 +238,12 @@ public:
void publish(MessageUniquePtr msg);
std::shared_ptr<MessageAlloc> message_allocator_;
typename rclcpp::experimental::buffers::mock::IntraProcessBuffer<T>::SharedPtr buffer{nullptr};
};
} // namespace mock
} // namespace rclcpp
namespace rclcpp
{
namespace experimental
{
namespace buffers
{
namespace mock
{
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename MessageDeleter = std::default_delete<MessageT>>
class IntraProcessBuffer
{
public:
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT>;
RCLCPP_SMART_PTR_DEFINITIONS(IntraProcessBuffer)
IntraProcessBuffer()
{}
void add(ConstMessageSharedPtr msg)
{
message_ptr = reinterpret_cast<std::uintptr_t>(msg.get());
shared_msg = msg;
++num_msgs;
}
void add(MessageUniquePtr msg)
{
message_ptr = reinterpret_cast<std::uintptr_t>(msg.get());
unique_msg = std::move(msg);
++num_msgs;
}
void pop(std::uintptr_t & msg_ptr)
{
msg_ptr = message_ptr;
message_ptr = 0;
--num_msgs;
}
size_t size() const
{
return num_msgs;
}
// need to store the messages somewhere otherwise the memory address will be reused
ConstMessageSharedPtr shared_msg;
MessageUniquePtr unique_msg;
std::uintptr_t message_ptr;
// count add and pop
size_t num_msgs = 0u;
};
} // namespace mock
} // namespace buffers
} // namespace experimental
} // namespace rclcpp
namespace rclcpp
{
namespace experimental
@@ -231,6 +282,12 @@ public:
return topic_name.c_str();
}
bool
is_durability_transient_local() const
{
return qos_profile.durability() == rclcpp::DurabilityPolicy::TransientLocal;
}
virtual
size_t
available_capacity() const = 0;
@@ -289,7 +346,7 @@ public:
}
bool
use_take_shared_method() const
use_take_shared_method() const override
{
return take_shared_method;
}
@@ -331,12 +388,14 @@ public:
// Prevent the header files of the mocked classes to be included
#define RCLCPP__PUBLISHER_HPP_
#define RCLCPP__PUBLISHER_BASE_HPP_
#define RCLCPP__EXPERIMENTAL__BUFFERS__INTRA_PROCESS_BUFFER_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BASE_HPP_
// Force ipm to use our mock publisher class.
#define Publisher mock::Publisher
#define PublisherBase mock::PublisherBase
#define IntraProcessBufferBase mock::IntraProcessBufferBase
#define IntraProcessBuffer mock::IntraProcessBuffer
#define SubscriptionIntraProcessBase mock::SubscriptionIntraProcessBase
#define SubscriptionIntraProcessBuffer mock::SubscriptionIntraProcessBuffer
@@ -368,28 +427,36 @@ void Publisher<T, Alloc>::publish(MessageUniquePtr msg)
throw std::runtime_error("cannot publish msg which is a null pointer");
}
ipm->template do_intra_process_publish<T, T, Alloc>(
intra_process_publisher_id_,
std::move(msg),
*message_allocator_);
if (buffer) {
auto shared_msg = ipm->template do_intra_process_publish_and_return_shared<T, T, Alloc>(
intra_process_publisher_id_,
std::move(msg),
*message_allocator_);
buffer->add(shared_msg);
} else {
ipm->template do_intra_process_publish<T, T, Alloc>(
intra_process_publisher_id_,
std::move(msg),
*message_allocator_);
}
}
} // namespace mock
} // namespace rclcpp
/*
This tests how the class connects and disconnects publishers and subscriptions:
- Creates 2 publishers on different topics and a subscription to one of them.
Add everything to the intra-process manager.
- All the entities are expected to have different ids.
- Check the subscriptions count for each publisher.
- One of the publishers is expected to have 1 subscription, while the other 0.
- Check the subscription count for a non existing publisher id, should return 0.
- Add a new publisher and a new subscription both with reliable QoS.
- The subscriptions count of the previous publisher is expected to remain unchanged,
while the new publisher is expected to have 2 subscriptions (it's compatible with both QoS).
- Remove the just added subscriptions.
- The count for the last publisher is expected to decrease to 1.
* This tests how the class connects and disconnects publishers and subscriptions:
* - Creates 2 publishers on different topics and a subscription to one of them.
* Add everything to the intra-process manager.
* - All the entities are expected to have different ids.
* - Check the subscriptions count for each publisher.
* - One of the publishers is expected to have 1 subscription, while the other 0.
* - Check the subscription count for a non existing publisher id, should return 0.
* - Add a new publisher and a new subscription both with reliable QoS.
* - The subscriptions count of the previous publisher is expected to remain unchanged,
* while the new publisher is expected to have 2 subscriptions (it's compatible with both QoS).
* - Remove the just added subscriptions.
* - The count for the last publisher is expected to decrease to 1.
*/
TEST(TestIntraProcessManager, add_pub_sub) {
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
@@ -408,7 +475,7 @@ TEST(TestIntraProcessManager, add_pub_sub) {
auto p1_id = ipm->add_publisher(p1);
auto p2_id = ipm->add_publisher(p2);
auto s1_id = ipm->add_subscription(s1);
auto s1_id = ipm->template add_subscription<MessageT>(s1);
bool unique_ids = p1_id != p2_id && p2_id != s1_id;
ASSERT_TRUE(unique_ids);
@@ -424,7 +491,7 @@ TEST(TestIntraProcessManager, add_pub_sub) {
auto s2 = std::make_shared<SubscriptionIntraProcessT>(rclcpp::QoS(10).reliable());
auto s2_id = ipm->add_subscription(s2);
auto s2_id = ipm->template add_subscription<MessageT>(s2);
auto p3_id = ipm->add_publisher(p3);
p1_subs = ipm->get_subscription_count(p1_id);
@@ -444,14 +511,14 @@ TEST(TestIntraProcessManager, add_pub_sub) {
}
/*
This tests the minimal usage of the class where there is a single subscription per publisher:
- Publishes a unique_ptr message with a subscription requesting ownership.
- The received message is expected to be the same.
- Remove the first subscription from ipm and add a new one.
- Publishes a unique_ptr message with a subscription not requesting ownership.
- The received message is expected to be the same, the first subscription do not receive it.
- Publishes a shared_ptr message with a subscription not requesting ownership.
- The received message is expected to be the same.
* This tests the minimal usage of the class where there is a single subscription per publisher:
* - Publishes a unique_ptr message with a subscription requesting ownership.
* - The received message is expected to be the same.
* - Remove the first subscription from ipm and add a new one.
* - Publishes a unique_ptr message with a subscription not requesting ownership.
* - The received message is expected to be the same, the first subscription do not receive it.
* - Publishes a shared_ptr message with a subscription not requesting ownership.
* - The received message is expected to be the same.
*/
TEST(TestIntraProcessManager, single_subscription) {
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
@@ -467,7 +534,7 @@ TEST(TestIntraProcessManager, single_subscription) {
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
s1->take_shared_method = false;
auto s1_id = ipm->add_subscription(s1);
auto s1_id = ipm->template add_subscription<MessageT>(s1);
auto unique_msg = std::make_unique<MessageT>();
auto original_message_pointer = reinterpret_cast<std::uintptr_t>(unique_msg.get());
@@ -478,7 +545,7 @@ TEST(TestIntraProcessManager, single_subscription) {
ipm->remove_subscription(s1_id);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
s2->take_shared_method = true;
auto s2_id = ipm->add_subscription(s2);
auto s2_id = ipm->template add_subscription<MessageT>(s2);
(void)s2_id;
unique_msg = std::make_unique<MessageT>();
@@ -497,15 +564,15 @@ TEST(TestIntraProcessManager, single_subscription) {
}
/*
This tests the usage of the class where there are multiple subscriptions of the same type:
- Publishes a unique_ptr message with 2 subscriptions requesting ownership.
- One is expected to receive the published message, while the other will receive a copy.
- Publishes a unique_ptr message with 2 subscriptions not requesting ownership.
- Both received messages are expected to be the same as the published one.
- Publishes a shared_ptr message with 2 subscriptions requesting ownership.
- Both received messages are expected to be a copy of the published one.
- Publishes a shared_ptr message with 2 subscriptions not requesting ownership.
- Both received messages are expected to be the same as the published one.
* This tests the usage of the class where there are multiple subscriptions of the same type:
* - Publishes a unique_ptr message with 2 subscriptions requesting ownership.
* - One is expected to receive the published message, while the other will receive a copy.
* - Publishes a unique_ptr message with 2 subscriptions not requesting ownership.
* - Both received messages are expected to be the same as the published one.
* - Publishes a shared_ptr message with 2 subscriptions requesting ownership.
* - Both received messages are expected to be a copy of the published one.
* - Publishes a shared_ptr message with 2 subscriptions not requesting ownership.
* - Both received messages are expected to be the same as the published one.
*/
TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
@@ -521,11 +588,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
s1->take_shared_method = false;
auto s1_id = ipm->add_subscription(s1);
auto s1_id = ipm->template add_subscription<MessageT>(s1);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
s2->take_shared_method = false;
auto s2_id = ipm->add_subscription(s2);
auto s2_id = ipm->template add_subscription<MessageT>(s2);
auto unique_msg = std::make_unique<MessageT>();
auto original_message_pointer = reinterpret_cast<std::uintptr_t>(unique_msg.get());
@@ -541,11 +608,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
s3->take_shared_method = true;
auto s3_id = ipm->add_subscription(s3);
auto s3_id = ipm->template add_subscription<MessageT>(s3);
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
s4->take_shared_method = true;
auto s4_id = ipm->add_subscription(s4);
auto s4_id = ipm->template add_subscription<MessageT>(s4);
unique_msg = std::make_unique<MessageT>();
original_message_pointer = reinterpret_cast<std::uintptr_t>(unique_msg.get());
@@ -560,11 +627,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
s5->take_shared_method = false;
auto s5_id = ipm->add_subscription(s5);
auto s5_id = ipm->template add_subscription<MessageT>(s5);
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
s6->take_shared_method = false;
auto s6_id = ipm->add_subscription(s6);
auto s6_id = ipm->template add_subscription<MessageT>(s6);
unique_msg = std::make_unique<MessageT>();
original_message_pointer = reinterpret_cast<std::uintptr_t>(unique_msg.get());
@@ -580,12 +647,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
s7->take_shared_method = true;
auto s7_id = ipm->add_subscription(s7);
auto s7_id = ipm->template add_subscription<MessageT>(s7);
(void)s7_id;
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
s8->take_shared_method = true;
auto s8_id = ipm->add_subscription(s8);
auto s8_id = ipm->template add_subscription<MessageT>(s8);
(void)s8_id;
unique_msg = std::make_unique<MessageT>();
@@ -598,20 +665,20 @@ TEST(TestIntraProcessManager, multiple_subscriptions_same_type) {
}
/*
This tests the usage of the class where there are multiple subscriptions of different types:
- Publishes a unique_ptr message with 1 subscription requesting ownership and 1 not.
- The one requesting ownership is expected to receive the published message,
while the other is expected to receive a copy.
- Publishes a unique_ptr message with 2 subscriptions requesting ownership and 1 not.
- One of the subscriptions requesting ownership is expected to receive the published message,
while both other subscriptions are expected to receive different copies.
- Publishes a unique_ptr message with 2 subscriptions requesting ownership and 2 not.
- The 2 subscriptions not requesting ownership are expected to both receive the same copy
of the message, one of the subscription requesting ownership is expected to receive a
different copy, while the last is expected to receive the published message.
- Publishes a shared_ptr message with 1 subscription requesting ownership and 1 not.
- The subscription requesting ownership is expected to receive a copy of the message, while
the other is expected to receive the published message
* This tests the usage of the class where there are multiple subscriptions of different types:
* - Publishes a unique_ptr message with 1 subscription requesting ownership and 1 not.
* - The one requesting ownership is expected to receive the published message,
* while the other is expected to receive a copy.
* - Publishes a unique_ptr message with 2 subscriptions requesting ownership and 1 not.
* - One of the subscriptions requesting ownership is expected to receive the published message,
* while both other subscriptions are expected to receive different copies.
* - Publishes a unique_ptr message with 2 subscriptions requesting ownership and 2 not.
* - The 2 subscriptions not requesting ownership are expected to both receive the same copy
* of the message, one of the subscription requesting ownership is expected to receive a
* different copy, while the last is expected to receive the published message.
* - Publishes a shared_ptr message with 1 subscription requesting ownership and 1 not.
* - The subscription requesting ownership is expected to receive a copy of the message, while
* the other is expected to receive the published message
*/
TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
@@ -627,11 +694,11 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
auto s1 = std::make_shared<SubscriptionIntraProcessT>();
s1->take_shared_method = true;
auto s1_id = ipm->add_subscription(s1);
auto s1_id = ipm->template add_subscription<MessageT>(s1);
auto s2 = std::make_shared<SubscriptionIntraProcessT>();
s2->take_shared_method = false;
auto s2_id = ipm->add_subscription(s2);
auto s2_id = ipm->template add_subscription<MessageT>(s2);
auto unique_msg = std::make_unique<MessageT>();
auto original_message_pointer = reinterpret_cast<std::uintptr_t>(unique_msg.get());
@@ -646,15 +713,15 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
auto s3 = std::make_shared<SubscriptionIntraProcessT>();
s3->take_shared_method = false;
auto s3_id = ipm->add_subscription(s3);
auto s3_id = ipm->template add_subscription<MessageT>(s3);
auto s4 = std::make_shared<SubscriptionIntraProcessT>();
s4->take_shared_method = false;
auto s4_id = ipm->add_subscription(s4);
auto s4_id = ipm->template add_subscription<MessageT>(s4);
auto s5 = std::make_shared<SubscriptionIntraProcessT>();
s5->take_shared_method = true;
auto s5_id = ipm->add_subscription(s5);
auto s5_id = ipm->template add_subscription<MessageT>(s5);
unique_msg = std::make_unique<MessageT>();
original_message_pointer = reinterpret_cast<std::uintptr_t>(unique_msg.get());
@@ -678,19 +745,19 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
auto s6 = std::make_shared<SubscriptionIntraProcessT>();
s6->take_shared_method = true;
auto s6_id = ipm->add_subscription(s6);
auto s6_id = ipm->template add_subscription<MessageT>(s6);
auto s7 = std::make_shared<SubscriptionIntraProcessT>();
s7->take_shared_method = true;
auto s7_id = ipm->add_subscription(s7);
auto s7_id = ipm->template add_subscription<MessageT>(s7);
auto s8 = std::make_shared<SubscriptionIntraProcessT>();
s8->take_shared_method = false;
auto s8_id = ipm->add_subscription(s8);
auto s8_id = ipm->template add_subscription<MessageT>(s8);
auto s9 = std::make_shared<SubscriptionIntraProcessT>();
s9->take_shared_method = false;
auto s9_id = ipm->add_subscription(s9);
auto s9_id = ipm->template add_subscription<MessageT>(s9);
unique_msg = std::make_unique<MessageT>();
original_message_pointer = reinterpret_cast<std::uintptr_t>(unique_msg.get());
@@ -716,12 +783,12 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
auto s10 = std::make_shared<SubscriptionIntraProcessT>();
s10->take_shared_method = false;
auto s10_id = ipm->add_subscription(s10);
auto s10_id = ipm->template add_subscription<MessageT>(s10);
(void)s10_id;
auto s11 = std::make_shared<SubscriptionIntraProcessT>();
s11->take_shared_method = true;
auto s11_id = ipm->add_subscription(s11);
auto s11_id = ipm->template add_subscription<MessageT>(s11);
(void)s11_id;
unique_msg = std::make_unique<MessageT>();
@@ -734,25 +801,25 @@ TEST(TestIntraProcessManager, multiple_subscriptions_different_type) {
}
/*
This tests the method "lowest_available_capacity":
- Creates 1 publisher.
- The available buffer capacity should be at least history size.
- Add 2 subscribers.
- Add everything to the intra-process manager.
- All the entities are expected to have different ids.
- Check the subscriptions count for the publisher.
- The available buffer capacity should be the history size.
- Publish one message (without receiving it).
- The available buffer capacity should decrease by 1.
- Publish another message (without receiving it).
- The available buffer capacity should decrease by 1.
- One subscriber receives one message.
- The available buffer capacity should stay the same,
as the other subscriber still has not freed its buffer.
- The other subscriber receives one message.
- The available buffer capacity should increase by 1.
- One subscription goes out of scope.
- The available buffer capacity should not change.
* This tests the method "lowest_available_capacity":
* - Creates 1 publisher.
* - The available buffer capacity should be at least history size.
* - Add 2 subscribers.
* - Add everything to the intra-process manager.
* - All the entities are expected to have different ids.
* - Check the subscriptions count for the publisher.
* - The available buffer capacity should be the history size.
* - Publish one message (without receiving it).
* - The available buffer capacity should decrease by 1.
* - Publish another message (without receiving it).
* - The available buffer capacity should decrease by 1.
* - One subscriber receives one message.
* - The available buffer capacity should stay the same,
* as the other subscriber still has not freed its buffer.
* - The other subscriber receives one message.
* - The available buffer capacity should increase by 1.
* - One subscription goes out of scope.
* - The available buffer capacity should not change.
*/
TEST(TestIntraProcessManager, lowest_available_capacity) {
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
@@ -776,8 +843,8 @@ TEST(TestIntraProcessManager, lowest_available_capacity) {
ASSERT_LE(0u, c1);
auto s1_id = ipm->add_subscription(s1);
auto s2_id = ipm->add_subscription(s2);
auto s1_id = ipm->template add_subscription<MessageT>(s1);
auto s2_id = ipm->template add_subscription<MessageT>(s2);
bool unique_ids = s1_id != s2_id && p1_id != s1_id;
ASSERT_TRUE(unique_ids);
@@ -820,3 +887,82 @@ TEST(TestIntraProcessManager, lowest_available_capacity) {
c1 = ipm->lowest_available_capacity(p1_id);
ASSERT_EQ(history_depth - 1u, c1);
}
/*
* This tests the check inside add_publisher for transient_local
* durability publishers
* - add_publisher should throw runtime_error when no valid buffer ptr
* is passed with a transient_local publisher
*/
TEST(TestIntraProcessManager, transient_local_invalid_buffer) {
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
using MessageT = rcl_interfaces::msg::Log;
using PublisherT = rclcpp::mock::Publisher<MessageT>;
constexpr auto history_depth = 10u;
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(rclcpp::QoS(history_depth).transient_local());
ASSERT_THROW(
{
ipm->add_publisher(p1, nullptr);
}, std::runtime_error);
}
/*
* This tests publishing function for transient_local durability publihers
* - A message is published before three transient_local subscriptions are added to
* ipm. Two of the subscriptions use take_shared method. Delivery of the message is verified
* along with the contents and pointer addresses from the subscriptions.
*/
TEST(TestIntraProcessManager, transient_local) {
using IntraProcessManagerT = rclcpp::experimental::IntraProcessManager;
using MessageT = rcl_interfaces::msg::Log;
using PublisherT = rclcpp::mock::Publisher<MessageT>;
using SubscriptionIntraProcessT = rclcpp::experimental::mock::SubscriptionIntraProcess<MessageT>;
constexpr auto history_depth = 10u;
auto ipm = std::make_shared<IntraProcessManagerT>();
auto p1 = std::make_shared<PublisherT>(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());
s1->take_shared_method = false;
s2->take_shared_method = true;
s3->take_shared_method = true;
auto p1_id = ipm->add_publisher(p1, p1->buffer);
p1->set_intra_process_manager(p1_id, ipm);
auto unique_msg = std::make_unique<MessageT>();
unique_msg->msg = "Test";
p1->publish(std::move(unique_msg));
ipm->template add_subscription<MessageT>(s1);
ipm->template add_subscription<MessageT>(s2);
ipm->template add_subscription<MessageT>(s3);
auto received_message_pointer_1 = s1->pop();
auto received_message_pointer_2 = s2->pop();
auto received_message_pointer_3 = s3->pop();
ASSERT_NE(0u, received_message_pointer_1);
ASSERT_NE(0u, received_message_pointer_2);
ASSERT_NE(0u, received_message_pointer_3);
ASSERT_EQ(received_message_pointer_3, received_message_pointer_2);
ASSERT_EQ(
reinterpret_cast<MessageT *>(received_message_pointer_1)->msg,
reinterpret_cast<MessageT *>(received_message_pointer_2)->msg);
ASSERT_EQ(
reinterpret_cast<MessageT *>(received_message_pointer_1)->msg,
reinterpret_cast<MessageT *>(received_message_pointer_3)->msg);
ASSERT_EQ("Test", reinterpret_cast<MessageT *>(received_message_pointer_1)->msg);
}

View File

@@ -3311,6 +3311,9 @@ TEST_F(TestNode, get_entity_names) {
const auto service_names_and_types = node->get_service_names_and_types();
EXPECT_EQ(service_names_and_types.end(), service_names_and_types.find("service"));
EXPECT_EQ(0u, node->count_clients("service"));
EXPECT_EQ(0u, node->count_services("service"));
const auto service_names_and_types_by_node =
node->get_service_names_and_types_by_node("node", "/ns");
EXPECT_EQ(

View File

@@ -196,11 +196,7 @@ static std::vector<TestParameters> invalid_qos_profiles()
{
std::vector<TestParameters> parameters;
parameters.reserve(2);
parameters.push_back(
TestParameters(
rclcpp::QoS(rclcpp::KeepLast(10)).transient_local(),
"transient_local_qos"));
parameters.reserve(1);
parameters.push_back(
TestParameters(
rclcpp::QoS(rclcpp::KeepAll()),
@@ -482,7 +478,7 @@ public:
void publish_loaned_message(rclcpp::LoanedMessage<MessageT, AllocatorT> && loaned_msg)
{
this->do_loaned_message_publish(std::move(loaned_msg.release()));
this->do_loaned_message_publish(loaned_msg.release());
}
void call_default_incompatible_qos_callback(rclcpp::QOSOfferedIncompatibleQoSInfo & event) const
@@ -674,3 +670,96 @@ INSTANTIATE_TEST_SUITE_P(
rclcpp::QoS(1).best_effort(), rclcpp::QoS(1).best_effort()),
std::pair<rclcpp::QoS, rclcpp::QoS>(
rclcpp::QoS(1).reliable(), rclcpp::QoS(1).best_effort())));
TEST_F(TestPublisher, intra_process_transient_local) {
constexpr auto history_depth = 10u;
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
rclcpp::PublisherOptionsWithAllocator<std::allocator<void>> pub_options_ipm_disabled;
pub_options_ipm_disabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
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);
auto pub_ipm_disabled_transient_local_enabled = node->create_publisher<test_msgs::msg::Empty>(
"topic2",
rclcpp::QoS(rclcpp::KeepLast(history_depth)).transient_local(), pub_options_ipm_disabled);
auto pub_ipm_enabled_transient_local_disabled = node->create_publisher<test_msgs::msg::Empty>(
"topic3",
rclcpp::QoS(rclcpp::KeepLast(history_depth)), pub_options_ipm_enabled);
auto pub_ipm_disabled_transient_local_disabled = node->create_publisher<test_msgs::msg::Empty>(
"topic4",
rclcpp::QoS(rclcpp::KeepLast(history_depth)), pub_options_ipm_disabled);
test_msgs::msg::Empty msg;
pub_ipm_enabled_transient_local_enabled->publish(msg);
pub_ipm_disabled_transient_local_enabled->publish(msg);
pub_ipm_enabled_transient_local_disabled->publish(msg);
pub_ipm_disabled_transient_local_disabled->publish(msg);
auto do_nothing = [](std::shared_ptr<const test_msgs::msg::Empty>) {};
struct IntraProcessCallback
{
void callback_fun(size_t s)
{
(void) s;
called = true;
}
bool called = false;
};
rclcpp::SubscriptionOptions sub_options_ipm_disabled;
sub_options_ipm_disabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Disable;
rclcpp::SubscriptionOptions sub_options_ipm_enabled;
sub_options_ipm_enabled.use_intra_process_comm = rclcpp::IntraProcessSetting::Enable;
IntraProcessCallback callback1, callback2, callback3, callback4;
auto sub_ipm_enabled_transient_local_enabled = node->create_subscription<test_msgs::msg::Empty>(
"topic1",
rclcpp::QoS(rclcpp::KeepLast(history_depth)).transient_local(),
do_nothing, sub_options_ipm_enabled);
sub_ipm_enabled_transient_local_enabled->set_on_new_intra_process_message_callback(
std::bind(&IntraProcessCallback::callback_fun, &callback1, std::placeholders::_1));
auto sub_ipm_disabled_transient_local_enabled = node->create_subscription<test_msgs::msg::Empty>(
"topic2",
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(&IntraProcessCallback::callback_fun, &callback2, std::placeholders::_1));
auto sub_ipm_enabled_transient_local_disabled = node->create_subscription<test_msgs::msg::Empty>(
"topic3",
rclcpp::QoS(rclcpp::KeepLast(history_depth)),
do_nothing, sub_options_ipm_enabled);
sub_ipm_enabled_transient_local_disabled->set_on_new_intra_process_message_callback(
std::bind(&IntraProcessCallback::callback_fun, &callback3, std::placeholders::_1));
auto sub_ipm_disabled_transient_local_disabled = node->create_subscription<test_msgs::msg::Empty>(
"topic4",
rclcpp::QoS(rclcpp::KeepLast(history_depth)),
do_nothing, sub_options_ipm_disabled);
sub_ipm_disabled_transient_local_disabled->set_on_new_intra_process_message_callback(
std::bind(&IntraProcessCallback::callback_fun, &callback4, std::placeholders::_1));
EXPECT_TRUE(pub_ipm_enabled_transient_local_enabled->is_durability_transient_local());
EXPECT_TRUE(pub_ipm_disabled_transient_local_enabled->is_durability_transient_local());
EXPECT_FALSE(pub_ipm_enabled_transient_local_disabled->is_durability_transient_local());
EXPECT_FALSE(pub_ipm_disabled_transient_local_disabled->is_durability_transient_local());
EXPECT_EQ(1, pub_ipm_enabled_transient_local_enabled->get_intra_process_subscription_count());
EXPECT_EQ(0, pub_ipm_disabled_transient_local_enabled->get_intra_process_subscription_count());
EXPECT_EQ(1, pub_ipm_enabled_transient_local_disabled->get_intra_process_subscription_count());
EXPECT_EQ(0, pub_ipm_disabled_transient_local_disabled->get_intra_process_subscription_count());
EXPECT_EQ(
history_depth - 1u,
pub_ipm_enabled_transient_local_enabled->lowest_available_ipm_capacity());
EXPECT_EQ(0, pub_ipm_disabled_transient_local_enabled->lowest_available_ipm_capacity());
EXPECT_EQ(
history_depth,
pub_ipm_enabled_transient_local_disabled->lowest_available_ipm_capacity());
EXPECT_EQ(0, pub_ipm_disabled_transient_local_disabled->lowest_available_ipm_capacity());
EXPECT_TRUE(callback1.called);
EXPECT_FALSE(callback2.called);
EXPECT_FALSE(callback3.called);
EXPECT_FALSE(callback4.called);
}

View File

@@ -21,12 +21,24 @@
#include "../utils/rclcpp_gtest_macros.hpp"
/*
Basic tests for the Rate, WallRate and ROSRate classes.
*/
TEST(TestRate, rate_basics) {
rclcpp::init(0, nullptr);
class TestRate : public ::testing::Test
{
public:
void SetUp()
{
rclcpp::init(0, nullptr);
}
void TearDown()
{
rclcpp::shutdown();
}
};
/*
Basic tests for the Rate and WallRate classes.
*/
TEST_F(TestRate, rate_basics) {
auto period = std::chrono::milliseconds(1000);
auto offset = std::chrono::milliseconds(500);
auto epsilon = std::chrono::milliseconds(100);
@@ -79,13 +91,9 @@ TEST(TestRate, rate_basics) {
auto five = std::chrono::system_clock::now();
delta = five - four;
ASSERT_TRUE(epsilon > delta);
rclcpp::shutdown();
}
TEST(TestRate, wall_rate_basics) {
rclcpp::init(0, nullptr);
TEST_F(TestRate, wall_rate_basics) {
auto period = std::chrono::milliseconds(100);
auto offset = std::chrono::milliseconds(50);
auto epsilon = std::chrono::milliseconds(1);
@@ -140,77 +148,12 @@ TEST(TestRate, wall_rate_basics) {
auto five = std::chrono::steady_clock::now();
delta = five - four;
EXPECT_GT(epsilon, delta);
rclcpp::shutdown();
}
TEST(TestRate, ros_rate_basics) {
rclcpp::init(0, nullptr);
auto period = std::chrono::milliseconds(100);
auto offset = std::chrono::milliseconds(50);
auto epsilon = std::chrono::milliseconds(1);
double overrun_ratio = 1.5;
rclcpp::Clock clock(RCL_ROS_TIME);
auto start = clock.now();
rclcpp::ROSRate r(period);
EXPECT_EQ(rclcpp::Duration(period), r.period());
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
ASSERT_FALSE(r.is_steady());
// suppress deprecated warnings
#if !defined(_WIN32)
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else // !defined(_WIN32)
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
ASSERT_EQ(RCL_ROS_TIME, r.get_type());
ASSERT_TRUE(r.sleep());
auto one = clock.now();
auto delta = one - start;
EXPECT_LT(rclcpp::Duration(period), delta);
EXPECT_GT(rclcpp::Duration(period * overrun_ratio), delta);
clock.sleep_for(offset);
ASSERT_TRUE(r.sleep());
auto two = clock.now();
delta = two - start;
EXPECT_LT(rclcpp::Duration(2 * period), delta + epsilon);
EXPECT_GT(rclcpp::Duration(2 * period * overrun_ratio), delta);
clock.sleep_for(offset);
auto two_offset = clock.now();
r.reset();
ASSERT_TRUE(r.sleep());
auto three = clock.now();
delta = three - two_offset;
EXPECT_LT(rclcpp::Duration(period), delta);
EXPECT_GT(rclcpp::Duration(period * overrun_ratio), delta);
clock.sleep_for(offset + period);
auto four = clock.now();
ASSERT_FALSE(r.sleep());
auto five = clock.now();
delta = five - four;
EXPECT_GT(rclcpp::Duration(epsilon), delta);
rclcpp::shutdown();
}
/*
Basic test for the deprecated GenericRate class.
*/
TEST(TestRate, generic_rate) {
TEST_F(TestRate, generic_rate) {
auto period = std::chrono::milliseconds(100);
auto offset = std::chrono::milliseconds(50);
auto epsilon = std::chrono::milliseconds(1);
@@ -325,13 +268,13 @@ TEST(TestRate, generic_rate) {
}
}
TEST(TestRate, from_double) {
TEST_F(TestRate, from_double) {
{
rclcpp::Rate rate(1.0);
EXPECT_EQ(rclcpp::Duration(std::chrono::seconds(1)), rate.period());
}
{
rclcpp::WallRate rate(2.0);
rclcpp::Rate rate(2.0);
EXPECT_EQ(rclcpp::Duration(std::chrono::milliseconds(500)), rate.period());
}
{
@@ -339,12 +282,12 @@ TEST(TestRate, from_double) {
EXPECT_EQ(rclcpp::Duration(std::chrono::seconds(2)), rate.period());
}
{
rclcpp::ROSRate rate(4.0);
rclcpp::WallRate rate(4.0);
EXPECT_EQ(rclcpp::Duration(std::chrono::milliseconds(250)), rate.period());
}
}
TEST(TestRate, clock_types) {
TEST_F(TestRate, clock_types) {
{
rclcpp::Rate rate(1.0, std::make_shared<rclcpp::Clock>(RCL_SYSTEM_TIME));
// suppress deprecated warnings
@@ -404,7 +347,7 @@ TEST(TestRate, clock_types) {
}
}
TEST(TestRate, incorrect_constuctor) {
TEST_F(TestRate, incorrect_constuctor) {
// Constructor with 0-frequency
RCLCPP_EXPECT_THROW_EQ(
rclcpp::Rate rate(0.0),

View File

@@ -22,7 +22,7 @@
#include "rclcpp/experimental/buffers/ring_buffer_implementation.hpp"
/*
Construtctor
* Construtctor
*/
TEST(TestRingBufferImplementation, constructor) {
// Cannot create a buffer of size zero.
@@ -37,10 +37,11 @@ TEST(TestRingBufferImplementation, constructor) {
}
/*
Basic usage
- insert data and check that it has data
- extract data
- overwrite old data writing over the buffer capacity
* Basic usage
* - insert data and check that it has data
* - get all data
* - extract data
* - overwrite old data writing over the buffer capacity
*/
TEST(TestRingBufferImplementation, basic_usage) {
rclcpp::experimental::buffers::RingBufferImplementation<char> rb(2);
@@ -64,6 +65,12 @@ TEST(TestRingBufferImplementation, basic_usage) {
rb.enqueue('d');
const auto all_data_vec = rb.get_all_data();
EXPECT_EQ(2u, all_data_vec.size());
EXPECT_EQ('c', all_data_vec[0]);
EXPECT_EQ('d', all_data_vec[1]);
EXPECT_EQ(true, rb.has_data());
EXPECT_EQ(true, rb.is_full());
@@ -79,3 +86,56 @@ TEST(TestRingBufferImplementation, basic_usage) {
EXPECT_EQ(false, rb.has_data());
EXPECT_EQ(false, rb.is_full());
}
/*
* Basic usage with unique_ptr
* - insert unique_ptr data and check that it has data
* - get all data
* - extract data
* - overwrite old data writing over the buffer capacity
*/
TEST(TestRingBufferImplementation, basic_usage_unique_ptr) {
rclcpp::experimental::buffers::RingBufferImplementation<std::unique_ptr<char>> rb(2);
auto a = std::make_unique<char>('a');
auto b = std::make_unique<char>('b');
auto original_b_pointer = reinterpret_cast<std::uintptr_t>(b.get());
auto c = std::make_unique<char>('c');
auto original_c_pointer = reinterpret_cast<std::uintptr_t>(c.get());
rb.enqueue(std::move(a));
EXPECT_EQ(true, rb.has_data());
EXPECT_EQ(false, rb.is_full());
rb.enqueue(std::move(b));
rb.enqueue(std::move(c));
EXPECT_EQ(true, rb.has_data());
EXPECT_EQ(true, rb.is_full());
const auto all_data_vec = rb.get_all_data();
EXPECT_EQ(2u, all_data_vec.size());
EXPECT_EQ('b', *all_data_vec[0]);
EXPECT_EQ('c', *all_data_vec[1]);
EXPECT_NE(original_b_pointer, reinterpret_cast<std::uintptr_t>(all_data_vec[0].get()));
EXPECT_NE(original_c_pointer, reinterpret_cast<std::uintptr_t>(all_data_vec[1].get()));
EXPECT_EQ(true, rb.has_data());
EXPECT_EQ(true, rb.is_full());
auto uni_ptr = rb.dequeue();
EXPECT_EQ('b', *uni_ptr);
EXPECT_EQ(original_b_pointer, reinterpret_cast<std::uintptr_t>(uni_ptr.get()));
EXPECT_EQ(true, rb.has_data());
EXPECT_EQ(false, rb.is_full());
uni_ptr = rb.dequeue();
EXPECT_EQ('c', *uni_ptr);
EXPECT_EQ(original_c_pointer, reinterpret_cast<std::uintptr_t>(uni_ptr.get()));
EXPECT_EQ(false, rb.has_data());
EXPECT_EQ(false, rb.is_full());
}

View File

@@ -178,3 +178,13 @@ TEST_F(TestRosoutSubscription, test_rosoutsubscription_getchild_hierarchy) {
EXPECT_TRUE(future.get());
received_msg_promise = {};
}
TEST_F(TestRosoutSubscription, test_rosoutsubscription_node_rosout_disabled) {
rclcpp::NodeOptions options = rclcpp::NodeOptions().enable_rosout(false);
auto node = std::make_shared<rclcpp::Node>("my_node", options);
auto log_func = [&node] {
auto logger = node->get_logger().get_child("child");
RCLCPP_INFO(logger, "test");
};
ASSERT_NO_THROW(log_func());
}

View File

@@ -145,11 +145,6 @@ TEST(TestSerializedMessage, reserve) {
// Resize using reserve method
serialized_msg.reserve(15);
EXPECT_EQ(15u, serialized_msg.capacity());
// Use invalid value throws exception
EXPECT_THROW(
{serialized_msg.reserve(-1);},
rclcpp::exceptions::RCLBadAlloc);
}
TEST(TestSerializedMessage, serialization) {

View File

@@ -611,11 +611,7 @@ static std::vector<TestParameters> invalid_qos_profiles()
{
std::vector<TestParameters> parameters;
parameters.reserve(3);
parameters.push_back(
TestParameters(
rclcpp::QoS(rclcpp::KeepLast(10)).transient_local(),
"transient_local_qos"));
parameters.reserve(1);
parameters.push_back(
TestParameters(
rclcpp::QoS(rclcpp::KeepAll()),

View File

@@ -60,14 +60,17 @@ 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());
options.topic_stats_options.state = rclcpp::TopicStatisticsState::Enable;
options.topic_stats_options.publish_topic = "topic_statistics";
options.topic_stats_options.publish_period = 5min;
options.topic_stats_options.qos = rclcpp::BestAvailableQoS();
EXPECT_EQ(options.topic_stats_options.state, rclcpp::TopicStatisticsState::Enable);
EXPECT_EQ(options.topic_stats_options.publish_topic, "topic_statistics");
EXPECT_EQ(options.topic_stats_options.publish_period, 5min);
EXPECT_EQ(options.topic_stats_options.qos, rclcpp::BestAvailableQoS());
}
TEST_F(TestSubscriptionOptions, topic_statistics_options_node_default_mode) {

View File

@@ -363,10 +363,27 @@ TEST_F(TestTime, seconds) {
}
TEST_F(TestTime, test_max) {
const rclcpp::Time time_max = rclcpp::Time::max();
const rclcpp::Time max_time(std::numeric_limits<int32_t>::max(), 999999999);
EXPECT_DOUBLE_EQ(max_time.seconds(), time_max.seconds());
EXPECT_EQ(max_time.nanoseconds(), time_max.nanoseconds());
// Same clock types
for (rcl_clock_type_t type = RCL_ROS_TIME;
type != RCL_STEADY_TIME; type = static_cast<rcl_clock_type_t>(type + 1))
{
const rclcpp::Time time_max = rclcpp::Time::max(type);
const rclcpp::Time max_time(std::numeric_limits<int32_t>::max(), 999999999, type);
EXPECT_DOUBLE_EQ(max_time.seconds(), time_max.seconds());
EXPECT_EQ(max_time.nanoseconds(), time_max.nanoseconds());
}
// Different clock types
{
const rclcpp::Time time_max = rclcpp::Time::max(RCL_ROS_TIME);
const rclcpp::Time max_time(std::numeric_limits<int32_t>::max(), 999999999, RCL_STEADY_TIME);
EXPECT_ANY_THROW((void)(time_max == max_time));
EXPECT_ANY_THROW((void)(time_max != max_time));
EXPECT_ANY_THROW((void)(time_max <= max_time));
EXPECT_ANY_THROW((void)(time_max >= max_time));
EXPECT_ANY_THROW((void)(time_max < max_time));
EXPECT_ANY_THROW((void)(time_max > max_time));
EXPECT_ANY_THROW((void)(time_max - max_time));
}
}
TEST_F(TestTime, test_constructor_from_rcl_time_point) {

View File

@@ -15,6 +15,7 @@
#include <gtest/gtest.h>
#include <chrono>
#include <cmath>
#include <memory>
#include <utility>
@@ -65,8 +66,10 @@ TEST_F(TestTimersManager, empty_manager)
TEST_F(TestTimersManager, add_run_remove_timer)
{
size_t t_runs = 0;
std::chrono::milliseconds timer_period(10);
auto t = TimerT::make_shared(
10ms,
timer_period,
[&t_runs]() {
t_runs++;
},
@@ -79,7 +82,7 @@ TEST_F(TestTimersManager, add_run_remove_timer)
timers_manager->add_timer(t);
// Sleep for more 3 times the timer period
std::this_thread::sleep_for(30ms);
std::this_thread::sleep_for(3 * timer_period);
// The timer is executed only once, even if we slept 3 times the period
execute_all_ready_timers(timers_manager);
@@ -191,67 +194,6 @@ TEST_F(TestTimersManager, head_not_ready)
EXPECT_EQ(0u, t_runs);
}
TEST_F(TestTimersManager, timers_order)
{
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
size_t t1_runs = 0;
auto t1 = TimerT::make_shared(
10ms,
[&t1_runs]() {
t1_runs++;
},
rclcpp::contexts::get_global_default_context());
size_t t2_runs = 0;
auto t2 = TimerT::make_shared(
30ms,
[&t2_runs]() {
t2_runs++;
},
rclcpp::contexts::get_global_default_context());
size_t t3_runs = 0;
auto t3 = TimerT::make_shared(
100ms,
[&t3_runs]() {
t3_runs++;
},
rclcpp::contexts::get_global_default_context());
// Add timers in a random order
timers_manager->add_timer(t2);
timers_manager->add_timer(t3);
timers_manager->add_timer(t1);
std::this_thread::sleep_for(10ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(1u, t1_runs);
EXPECT_EQ(0u, t2_runs);
EXPECT_EQ(0u, t3_runs);
std::this_thread::sleep_for(30ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(2u, t1_runs);
EXPECT_EQ(1u, t2_runs);
EXPECT_EQ(0u, t3_runs);
std::this_thread::sleep_for(100ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(3u, t1_runs);
EXPECT_EQ(2u, t2_runs);
EXPECT_EQ(1u, t3_runs);
timers_manager->remove_timer(t1);
std::this_thread::sleep_for(30ms);
execute_all_ready_timers(timers_manager);
EXPECT_EQ(3u, t1_runs);
EXPECT_EQ(3u, t2_runs);
EXPECT_EQ(1u, t3_runs);
}
TEST_F(TestTimersManager, start_stop_timers_thread)
{
auto timers_manager = std::make_shared<TimersManager>(
@@ -274,7 +216,7 @@ TEST_F(TestTimersManager, timers_thread)
auto timers_manager = std::make_shared<TimersManager>(
rclcpp::contexts::get_global_default_context());
size_t t1_runs = 0;
int t1_runs = 0;
auto t1 = TimerT::make_shared(
1ms,
[&t1_runs]() {
@@ -282,7 +224,7 @@ TEST_F(TestTimersManager, timers_thread)
},
rclcpp::contexts::get_global_default_context());
size_t t2_runs = 0;
int t2_runs = 0;
auto t2 = TimerT::make_shared(
1ms,
[&t2_runs]() {
@@ -296,12 +238,12 @@ TEST_F(TestTimersManager, timers_thread)
// Run timers thread for a while
timers_manager->start();
std::this_thread::sleep_for(5ms);
std::this_thread::sleep_for(50ms);
timers_manager->stop();
EXPECT_LT(1u, t1_runs);
EXPECT_LT(1u, t2_runs);
EXPECT_EQ(t1_runs, t2_runs);
EXPECT_LE(std::abs(t1_runs - t2_runs), 1);
}
TEST_F(TestTimersManager, destructor)
@@ -365,13 +307,13 @@ TEST_F(TestTimersManager, add_remove_while_thread_running)
timers_manager->start();
// After a while remove t1 and add t2
std::this_thread::sleep_for(5ms);
std::this_thread::sleep_for(50ms);
timers_manager->remove_timer(t1);
size_t tmp_t1 = t1_runs;
timers_manager->add_timer(t2);
// Wait some more time and then stop
std::this_thread::sleep_for(5ms);
std::this_thread::sleep_for(50ms);
timers_manager->stop();
// t1 has stopped running

View File

@@ -50,7 +50,7 @@ TEST(TypesupportHelpersTest, returns_c_type_info_for_valid_legacy_library) {
try {
auto library = rclcpp::get_typesupport_library(
"test_msgs/BasicTypes", "rosidl_typesupport_cpp");
auto string_typesupport = rclcpp::get_typesupport_handle(
auto string_typesupport = rclcpp::get_message_typesupport_handle(
"test_msgs/BasicTypes", "rosidl_typesupport_cpp", *library);
EXPECT_THAT(
@@ -65,7 +65,7 @@ TEST(TypesupportHelpersTest, returns_c_type_info_for_valid_library) {
try {
auto library = rclcpp::get_typesupport_library(
"test_msgs/msg/BasicTypes", "rosidl_typesupport_cpp");
auto string_typesupport = rclcpp::get_typesupport_handle(
auto string_typesupport = rclcpp::get_message_typesupport_handle(
"test_msgs/msg/BasicTypes", "rosidl_typesupport_cpp", *library);
EXPECT_THAT(
@@ -75,3 +75,52 @@ TEST(TypesupportHelpersTest, returns_c_type_info_for_valid_library) {
FAIL() << e.what();
}
}
TEST(TypesupportHelpersTest, returns_service_type_info_for_valid_legacy_library) {
try {
auto library = rclcpp::get_typesupport_library(
"test_msgs/Empty", "rosidl_typesupport_cpp");
auto empty_typesupport = rclcpp::get_service_typesupport_handle(
"test_msgs/Empty", "rosidl_typesupport_cpp", *library);
EXPECT_THAT(
std::string(empty_typesupport->typesupport_identifier),
ContainsRegex("rosidl_typesupport"));
} catch (const std::runtime_error & e) {
FAIL() << e.what();
}
}
TEST(TypesupportHelpersTest, returns_service_type_info_for_valid_library) {
try {
auto library = rclcpp::get_typesupport_library(
"test_msgs/srv/Empty", "rosidl_typesupport_cpp");
auto empty_typesupport = rclcpp::get_service_typesupport_handle(
"test_msgs/srv/Empty", "rosidl_typesupport_cpp", *library);
EXPECT_THAT(
std::string(empty_typesupport->typesupport_identifier),
ContainsRegex("rosidl_typesupport"));
} catch (const std::runtime_error & e) {
FAIL() << e.what();
}
}
TEST(TypesupportHelpersTest, test_throw_exception_with_invalid_type) {
// message
std::string invalid_type = "test_msgs/msg/InvalidType";
auto library = rclcpp::get_typesupport_library(invalid_type, "rosidl_typesupport_cpp");
EXPECT_THROW(
rclcpp::get_message_typesupport_handle(invalid_type, "rosidl_typesupport_cpp", *library),
std::runtime_error);
EXPECT_THROW(
rclcpp::get_service_typesupport_handle(invalid_type, "rosidl_typesupport_cpp", *library),
std::runtime_error);
// service
invalid_type = "test_msgs/srv/InvalidType";
library = rclcpp::get_typesupport_library(invalid_type, "rosidl_typesupport_cpp");
EXPECT_THROW(
rclcpp::get_service_typesupport_handle(invalid_type, "rosidl_typesupport_cpp", *library),
std::runtime_error);
}

View File

@@ -14,7 +14,6 @@
#include <gtest/gtest.h>
#include <atomic>
#include <chrono>
#include <iostream>
#include <memory>
@@ -22,12 +21,12 @@
#include <set>
#include <stdexcept>
#include <string>
#include <utility>
#include <vector>
#include "libstatistics_collector/moving_average_statistics/types.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rclcpp/msg/message_with_header.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/rclcpp.hpp"
@@ -36,10 +35,10 @@
#include "rclcpp/topic_statistics/subscription_topic_statistics.hpp"
#include "statistics_msgs/msg/metrics_message.hpp"
#include "statistics_msgs/msg/statistic_data_point.hpp"
#include "statistics_msgs/msg/statistic_data_type.hpp"
#include "test_msgs/msg/empty.hpp"
#include "test_msgs/msg/strings.hpp"
#include "test_topic_stats_utils.hpp"
@@ -67,7 +66,6 @@ constexpr const std::chrono::seconds kUnstableMessageAgeWindowDuration{
constexpr const std::chrono::seconds kUnstableMessageAgeOffset{std::chrono::seconds{1}};
} // namespace
using rclcpp::msg::MessageWithHeader;
using test_msgs::msg::Empty;
using rclcpp::topic_statistics::SubscriptionTopicStatistics;
using statistics_msgs::msg::MetricsMessage;
@@ -76,114 +74,73 @@ using statistics_msgs::msg::StatisticDataType;
using libstatistics_collector::moving_average_statistics::StatisticData;
/**
* Wrapper class to test and expose parts of the SubscriptionTopicStatistics<T> class.
* \tparam CallbackMessageT
* Wrapper class to test and expose parts of the SubscriptionTopicStatistics class.
*/
template<typename CallbackMessageT>
class TestSubscriptionTopicStatistics : public SubscriptionTopicStatistics<CallbackMessageT>
class TestSubscriptionTopicStatistics : public SubscriptionTopicStatistics
{
public:
TestSubscriptionTopicStatistics(
const std::string & node_name,
rclcpp::Publisher<statistics_msgs::msg::MetricsMessage>::SharedPtr publisher)
: SubscriptionTopicStatistics<CallbackMessageT>(node_name, publisher)
: SubscriptionTopicStatistics(node_name, std::move(publisher))
{
}
virtual ~TestSubscriptionTopicStatistics() = default;
~TestSubscriptionTopicStatistics() override = default;
/// Exposed for testing
std::vector<StatisticData> get_current_collector_data() const
{
return SubscriptionTopicStatistics<CallbackMessageT>::get_current_collector_data();
}
using SubscriptionTopicStatistics::get_current_collector_data;
};
/**
* Empty publisher node: used to publish empty messages
* PublisherNode wrapper: used to create publisher node
*/
class EmptyPublisher : public rclcpp::Node
template<typename MessageT>
class PublisherNode : public rclcpp::Node
{
public:
EmptyPublisher(
PublisherNode(
const std::string & name, const std::string & topic,
const std::chrono::milliseconds & publish_period = std::chrono::milliseconds{100})
: Node(name)
{
publisher_ = create_publisher<Empty>(topic, 10);
publisher_ = create_publisher<MessageT>(topic, 10);
publish_timer_ = this->create_wall_timer(
publish_period, [this]() {
this->publish_message();
});
}
virtual ~EmptyPublisher() = default;
~PublisherNode() override = default;
private:
void publish_message()
{
auto msg = Empty{};
auto msg = MessageT{};
publisher_->publish(msg);
}
rclcpp::Publisher<Empty>::SharedPtr publisher_;
typename rclcpp::Publisher<MessageT>::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr publish_timer_;
};
/**
* MessageWithHeader publisher node: used to publish MessageWithHeader with `header` value set
*/
class MessageWithHeaderPublisher : public rclcpp::Node
{
public:
MessageWithHeaderPublisher(
const std::string & name, const std::string & topic,
const std::chrono::milliseconds & publish_period = std::chrono::milliseconds{100})
: Node(name)
{
publisher_ = create_publisher<MessageWithHeader>(topic, 10);
publish_timer_ = this->create_wall_timer(
publish_period, [this]() {
this->publish_message();
});
uniform_dist_ = std::uniform_int_distribution<uint32_t>{1000000, 100000000};
}
virtual ~MessageWithHeaderPublisher() = default;
private:
void publish_message()
{
std::random_device rd;
std::mt19937 gen{rd()};
uint32_t d = uniform_dist_(gen);
auto msg = MessageWithHeader{};
// Subtract ~1 second (add some noise for a non-zero standard deviation)
// so the received message age calculation is always > 0
msg.header.stamp = this->now() - rclcpp::Duration{1, d};
publisher_->publish(msg);
}
rclcpp::Publisher<MessageWithHeader>::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr publish_timer_;
std::uniform_int_distribution<uint32_t> uniform_dist_;
};
/**
* TransitionMessageStamp publisher node : used to publish MessageWithHeader with `header` value set
* TransitionMessageStamp publisher emulator node : used to emulate publishing messages by
* directly calling rclcpp::Subscription::handle_message(msg_shared_ptr, message_info).
* The message age results change during the test.
*/
class TransitionMessageStampPublisher : public rclcpp::Node
template<typename MessageT>
class TransitionMessageStampPublisherEmulator : public rclcpp::Node
{
public:
TransitionMessageStampPublisher(
const std::string & name, const std::string & topic,
TransitionMessageStampPublisherEmulator(
const std::string & name,
const std::chrono::seconds transition_duration, const std::chrono::seconds message_age_offset,
typename rclcpp::Subscription<MessageT>::SharedPtr subscription,
const std::chrono::milliseconds & publish_period = std::chrono::milliseconds{100})
: Node(name), transition_duration_(transition_duration), message_age_offset_(message_age_offset)
: Node(name), transition_duration_(transition_duration), message_age_offset_(message_age_offset),
subscription_(std::move(subscription))
{
publisher_ = create_publisher<MessageWithHeader>(topic, 10);
publish_timer_ = this->create_wall_timer(publish_period, [this]() {this->publish_message();});
start_time_ = this->now();
}
@@ -191,84 +148,66 @@ public:
private:
void publish_message()
{
auto msg = MessageWithHeader{};
std::shared_ptr<void> msg_shared_ptr = std::make_shared<MessageT>();
rmw_message_info_t rmw_message_info = rmw_get_zero_initialized_message_info();
auto now = this->now();
auto elapsed_time = now - start_time_;
if (elapsed_time < transition_duration_) {
// Apply only to the topic statistics in the first half
// Subtract offset so message_age is always >= offset.
msg.header.stamp = now - message_age_offset_;
rmw_message_info.source_timestamp = (now - message_age_offset_).nanoseconds();
} else {
msg.header.stamp = now;
rmw_message_info.source_timestamp = now.nanoseconds();
}
publisher_->publish(msg);
rclcpp::MessageInfo message_info{rmw_message_info};
subscription_->handle_message(msg_shared_ptr, message_info);
}
std::chrono::seconds transition_duration_;
std::chrono::seconds message_age_offset_;
typename rclcpp::Subscription<MessageT>::SharedPtr subscription_;
rclcpp::Time start_time_;
rclcpp::Publisher<MessageWithHeader>::SharedPtr publisher_;
rclcpp::TimerBase::SharedPtr publish_timer_;
};
/**
* Empty subscriber node: used to create subscriber topic statistics requirements
* Message subscriber node: used to create subscriber with enabled topic statistics collectors
*
*/
class EmptySubscriber : public rclcpp::Node
template<typename MessageT>
class SubscriberWithTopicStatistics : public rclcpp::Node
{
public:
EmptySubscriber(const std::string & name, const std::string & topic)
SubscriberWithTopicStatistics(
const std::string & name, const std::string & topic,
std::chrono::milliseconds publish_period = defaultStatisticsPublishPeriod)
: Node(name)
{
// manually enable topic statistics via options
// Manually enable topic statistics via options
auto options = rclcpp::SubscriptionOptions();
options.topic_stats_options.state = rclcpp::TopicStatisticsState::Enable;
options.topic_stats_options.publish_period = publish_period;
auto callback = [](Empty::UniquePtr msg) {
auto callback = [](typename MessageT::UniquePtr msg) {
(void) msg;
};
subscription_ = create_subscription<Empty,
std::function<void(Empty::UniquePtr)>>(
subscription_ = create_subscription<MessageT,
std::function<void(typename MessageT::UniquePtr)>>(
topic,
rclcpp::QoS(rclcpp::KeepAll()),
callback,
options);
}
virtual ~EmptySubscriber() = default;
~SubscriberWithTopicStatistics() override = default;
private:
rclcpp::Subscription<Empty>::SharedPtr subscription_;
};
/**
* MessageWithHeader subscriber node: used to create subscriber topic statistics requirements
*/
class MessageWithHeaderSubscriber : public rclcpp::Node
{
public:
MessageWithHeaderSubscriber(const std::string & name, const std::string & topic)
: Node(name)
typename rclcpp::Subscription<MessageT>::SharedPtr get_subscription()
{
// manually enable topic statistics via options
auto options = rclcpp::SubscriptionOptions();
options.topic_stats_options.state = rclcpp::TopicStatisticsState::Enable;
options.topic_stats_options.publish_period = defaultStatisticsPublishPeriod;
auto callback = [](MessageWithHeader::UniquePtr msg) {
(void) msg;
};
subscription_ = create_subscription<MessageWithHeader,
std::function<void(MessageWithHeader::UniquePtr)>>(
topic,
rclcpp::QoS(rclcpp::KeepAll()),
callback,
options);
return subscription_;
}
virtual ~MessageWithHeaderSubscriber() = default;
private:
rclcpp::Subscription<MessageWithHeader>::SharedPtr subscription_;
typename rclcpp::Subscription<MessageT>::SharedPtr subscription_;
};
/**
@@ -277,43 +216,17 @@ private:
class TestSubscriptionTopicStatisticsFixture : public ::testing::Test
{
protected:
void SetUp()
void SetUp() override
{
rclcpp::init(0 /* argc */, nullptr /* argv */);
}
void TearDown()
void TearDown() override
{
rclcpp::shutdown();
}
};
/**
* Check if a received statistics message is empty (no data was observed)
* \param message_to_check
*/
void check_if_statistics_message_is_empty(const MetricsMessage & message_to_check)
{
for (const auto & stats_point : message_to_check.statistics) {
const auto type = stats_point.data_type;
switch (type) {
case StatisticDataType::STATISTICS_DATA_TYPE_SAMPLE_COUNT:
EXPECT_EQ(0, stats_point.data) << "unexpected sample count" << stats_point.data;
break;
case StatisticDataType::STATISTICS_DATA_TYPE_AVERAGE:
case StatisticDataType::STATISTICS_DATA_TYPE_MINIMUM:
case StatisticDataType::STATISTICS_DATA_TYPE_MAXIMUM:
case StatisticDataType::STATISTICS_DATA_TYPE_STDDEV:
EXPECT_TRUE(std::isnan(stats_point.data)) << "unexpected value" << stats_point.data <<
" for type:" << type;
break;
default:
FAIL() << "received unknown statistics type: " << std::dec <<
static_cast<unsigned int>(type);
}
}
}
/**
* Check if a received statistics message observed data and contains some calculation
* \param message_to_check
@@ -348,28 +261,13 @@ void check_if_statistic_message_is_populated(const MetricsMessage & message_to_c
/**
* Test an invalid argument is thrown for a bad input publish period.
*/
TEST(TestSubscriptionTopicStatistics, test_invalid_publish_period)
TEST_F(TestSubscriptionTopicStatisticsFixture, test_invalid_publish_period)
{
rclcpp::init(0 /* argc */, nullptr /* argv */);
auto node = std::make_shared<rclcpp::Node>("test_period_node");
auto options = rclcpp::SubscriptionOptions();
options.topic_stats_options.state = rclcpp::TopicStatisticsState::Enable;
options.topic_stats_options.publish_period = std::chrono::milliseconds(0);
auto callback = [](Empty::UniquePtr msg) {
(void) msg;
};
ASSERT_THROW(
(node->create_subscription<Empty, std::function<void(Empty::UniquePtr)>>(
"should_throw_invalid_arg",
rclcpp::QoS(rclcpp::KeepAll()),
callback,
options)), std::invalid_argument);
rclcpp::shutdown();
SubscriberWithTopicStatistics<Empty>(
"test_period_node", "should_throw_invalid_arg", std::chrono::milliseconds(0)
),
std::invalid_argument);
}
/**
@@ -378,7 +276,7 @@ TEST(TestSubscriptionTopicStatistics, test_invalid_publish_period)
*/
TEST_F(TestSubscriptionTopicStatisticsFixture, test_manual_construction)
{
auto empty_subscriber = std::make_shared<EmptySubscriber>(
auto empty_subscriber = std::make_shared<SubscriberWithTopicStatistics<Empty>>(
kTestSubNodeName,
kTestSubStatsEmptyTopic);
@@ -389,7 +287,7 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_manual_construction)
10);
// Construct a separate instance
auto sub_topic_stats = std::make_unique<TestSubscriptionTopicStatistics<Empty>>(
auto sub_topic_stats = std::make_unique<TestSubscriptionTopicStatistics>(
empty_subscriber->get_name(),
topic_stats_publisher);
@@ -410,7 +308,7 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_manual_construction)
TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_for_message_no_header)
{
// Create an empty publisher
auto empty_publisher = std::make_shared<EmptyPublisher>(
auto empty_publisher = std::make_shared<PublisherNode<Empty>>(
kTestPubNodeName,
kTestSubStatsEmptyTopic);
// empty_subscriber has a topic statistics instance as part of its subscription
@@ -422,7 +320,7 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_for_message_no
"/statistics",
kNumExpectedMessages);
auto empty_subscriber = std::make_shared<EmptySubscriber>(
auto empty_subscriber = std::make_shared<SubscriberWithTopicStatistics<Empty>>(
kTestSubNodeName,
kTestSubStatsEmptyTopic);
@@ -432,74 +330,7 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_for_message_no
ex.add_node(empty_subscriber);
// Spin and get future
ex.spin_until_future_complete(
statistics_listener->GetFuture(),
kTestTimeout);
// Compare message counts, sample count should be the same as published and received count
EXPECT_EQ(kNumExpectedMessages, statistics_listener->GetNumberOfMessagesReceived());
// Check the received message total count
const auto received_messages = statistics_listener->GetReceivedMessages();
EXPECT_EQ(kNumExpectedMessages, received_messages.size());
// check the type of statistics that were received and their counts
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 == "message_age") {
message_age_count++;
}
if (msg.metrics_source == "message_period") {
message_period_count++;
}
}
EXPECT_EQ(kNumExpectedMessageAgeMessages, message_age_count);
EXPECT_EQ(kNumExpectedMessagePeriodMessages, message_period_count);
// Check the collected statistics for message period.
// Message age statistics will not be calculated because Empty messages
// don't have a `header` with timestamp. This means that we expect to receive a `message_age`
// and `message_period` message for each empty message published.
for (const auto & msg : received_messages) {
if (msg.metrics_source == kMessageAgeSourceLabel) {
check_if_statistics_message_is_empty(msg);
} else if (msg.metrics_source == kMessagePeriodSourceLabel) {
check_if_statistic_message_is_populated(msg);
}
}
}
TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_for_message_with_header)
{
// Create a MessageWithHeader publisher
auto msg_with_header_publisher = std::make_shared<MessageWithHeaderPublisher>(
kTestPubNodeName,
kTestSubStatsTopic);
// empty_subscriber has a topic statistics instance as part of its subscription
// this will listen to and generate statistics for the empty message
// Create a listener for topic statistics messages
auto statistics_listener = std::make_shared<rclcpp::topic_statistics::MetricsMessageSubscriber>(
"test_receive_stats_for_message_with_header",
"/statistics",
kNumExpectedMessages);
auto msg_with_header_subscriber = std::make_shared<MessageWithHeaderSubscriber>(
kTestSubNodeName,
kTestSubStatsTopic);
rclcpp::executors::SingleThreadedExecutor ex;
ex.add_node(msg_with_header_publisher);
ex.add_node(statistics_listener);
ex.add_node(msg_with_header_subscriber);
// Spin and get future
ex.spin_until_future_complete(
statistics_listener->GetFuture(),
kTestTimeout);
ex.spin_until_future_complete(statistics_listener->GetFuture(), kTestTimeout);
// Compare message counts, sample count should be the same as published and received count
EXPECT_EQ(kNumExpectedMessages, statistics_listener->GetNumberOfMessagesReceived());
@@ -524,6 +355,7 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_for_message_wi
EXPECT_EQ(kNumExpectedMessageAgeMessages, message_age_count);
EXPECT_EQ(kNumExpectedMessagePeriodMessages, message_period_count);
// Check the collected statistics for message period.
for (const auto & msg : received_messages) {
check_if_statistic_message_is_populated(msg);
}
@@ -531,23 +363,27 @@ TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_for_message_wi
TEST_F(TestSubscriptionTopicStatisticsFixture, test_receive_stats_include_window_reset)
{
// Create a MessageWithHeader publisher
auto msg_with_header_publisher = std::make_shared<TransitionMessageStampPublisher>(
kTestPubNodeName, kTestSubStatsTopic, kUnstableMessageAgeWindowDuration,
kUnstableMessageAgeOffset);
// msg_with_header_subscriber has a topic statistics instance as part of its
// msg_subscriber_with_topic_statistics has a topic statistics instance as part of its
// subscription this will listen to and generate statistics
auto msg_with_header_subscriber =
std::make_shared<MessageWithHeaderSubscriber>(kTestSubNodeName, kTestSubStatsTopic);
auto msg_subscriber_with_topic_statistics =
std::make_shared<SubscriberWithTopicStatistics<test_msgs::msg::Strings>>(
kTestSubNodeName,
kTestSubStatsTopic);
// Create a message publisher
auto msg_publisher =
std::make_shared<TransitionMessageStampPublisherEmulator<test_msgs::msg::Strings>>(
kTestPubNodeName, kUnstableMessageAgeWindowDuration,
kUnstableMessageAgeOffset, msg_subscriber_with_topic_statistics->get_subscription());
// Create a listener for topic statistics messages
auto statistics_listener = std::make_shared<rclcpp::topic_statistics::MetricsMessageSubscriber>(
"test_receive_stats_include_window_reset", "/statistics", kNumExpectedMessages);
rclcpp::executors::SingleThreadedExecutor ex;
ex.add_node(msg_with_header_publisher);
ex.add_node(msg_publisher);
ex.add_node(statistics_listener);
ex.add_node(msg_with_header_subscriber);
ex.add_node(msg_subscriber_with_topic_statistics);
// Spin and get future
ex.spin_until_future_complete(statistics_listener->GetFuture(), kTestTimeout);

View File

@@ -3,6 +3,29 @@ Changelog for package rclcpp_action
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26.0.0 (2024-01-24)
-------------------
25.0.0 (2023-12-26)
-------------------
* Switch to target_link_libraries. (`#2374 <https://github.com/ros2/rclcpp/issues/2374>`_)
* Contributors: Chris Lalancette
24.0.0 (2023-11-06)
-------------------
23.2.0 (2023-10-09)
-------------------
23.1.0 (2023-10-04)
-------------------
23.0.0 (2023-09-08)
-------------------
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
* fix(ClientGoalHandle): Made mutex recursive to prevent deadlocks (`#2267 <https://github.com/ros2/rclcpp/issues/2267>`_)
* Contributors: Christophe Bedard, jmachowinski
22.2.0 (2023-09-07)
-------------------
* Correct the position of a comment. (`#2290 <https://github.com/ros2/rclcpp/issues/2290>`_)

View File

@@ -5,6 +5,7 @@ project(rclcpp_action)
find_package(ament_cmake_ros REQUIRED)
find_package(action_msgs REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rcl REQUIRED)
find_package(rcl_action REQUIRED)
find_package(rcpputils REQUIRED)
find_package(rosidl_runtime_c REQUIRED)
@@ -21,28 +22,26 @@ if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
)
endif()
set(${PROJECT_NAME}_SRCS
add_library(${PROJECT_NAME}
src/client.cpp
src/qos.cpp
src/server.cpp
src/server_goal_handle.cpp
src/types.cpp
)
add_library(${PROJECT_NAME}
${${PROJECT_NAME}_SRCS})
target_include_directories(${PROJECT_NAME}
PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>")
ament_target_dependencies(${PROJECT_NAME}
"action_msgs"
"rcl_action"
"rclcpp"
"rcpputils"
"rosidl_runtime_c"
target_link_libraries(${PROJECT_NAME} PUBLIC
${action_msgs_TARGETS}
rcl::rcl
rcl_action::rcl_action
rclcpp::rclcpp
rosidl_runtime_c::rosidl_runtime_c
)
target_link_libraries(${PROJECT_NAME} PRIVATE
rcpputils::rcpputils
)
# Causes the visibility macros to use dllexport rather than dllimport,
@@ -69,12 +68,7 @@ ament_export_libraries(${PROJECT_NAME})
# Export modern CMake targets
ament_export_targets(${PROJECT_NAME})
# specific order: dependents before dependencies
ament_export_dependencies(ament_cmake)
ament_export_dependencies(action_msgs)
ament_export_dependencies(rclcpp)
ament_export_dependencies(rcl_action)
ament_export_dependencies(rosidl_runtime_c)
ament_export_dependencies(action_msgs rcl_action rclcpp rosidl_runtime_c)
if(BUILD_TESTING)
find_package(ament_cmake_gtest REQUIRED)
@@ -87,58 +81,52 @@ if(BUILD_TESTING)
ament_add_gtest(test_client test/test_client.cpp TIMEOUT 180)
if(TARGET test_client)
ament_target_dependencies(test_client
"rcutils"
"test_msgs"
)
target_link_libraries(test_client
${PROJECT_NAME}
mimick
rcl::rcl
rcl_action::rcl_action
rclcpp::rclcpp
rcutils::rcutils
${test_msgs_TARGETS}
)
endif()
ament_add_gtest(test_server test/test_server.cpp TIMEOUT 180)
if(TARGET test_server)
ament_target_dependencies(test_server
"rcpputils"
"rcutils"
"test_msgs"
)
target_link_libraries(test_server
${PROJECT_NAME}
mimick
rcl_action::rcl_action
rclcpp::rclcpp
${test_msgs_TARGETS}
)
endif()
ament_add_gtest(test_server_goal_handle test/test_server_goal_handle.cpp)
if(TARGET test_server_goal_handle)
ament_target_dependencies(test_server_goal_handle
"rcutils"
"test_msgs"
)
target_link_libraries(test_server_goal_handle
${PROJECT_NAME}
${action_msgs_TARGETS}
mimick
rclcpp::rclcpp
${test_msgs_TARGETS}
)
endif()
ament_add_gtest(test_traits test/test_traits.cpp)
if(TARGET test_traits)
ament_target_dependencies(test_traits
"test_msgs"
)
target_link_libraries(test_traits
${PROJECT_NAME}
${test_msgs_TARGETS}
)
endif()
ament_add_gtest(test_types test/test_types.cpp)
if(TARGET test_types)
ament_target_dependencies(test_types
"test_msgs"
)
target_link_libraries(test_types
${PROJECT_NAME}
${test_msgs_TARGETS}
)
endif()
endif()

View File

@@ -2,7 +2,8 @@
Adds action APIs for C++.
Visit the [rclcpp_action API documentation](http://docs.ros2.org/latest/api/rclcpp_action/) for a complete list of its main components and features. For more information about Actions in ROS 2, see the [design document](http://design.ros2.org/articles/actions.html).
The link to the latest rclcpp_action API documentation, which includes a complete list of its main components and features, can be found on the rclcpp_action package info page, at the [ROS Index](https://index.ros.org/p/rclcpp_action/).
For more information about Actions in ROS 2, see the [design document](http://design.ros2.org/articles/actions.html).
## Quality Declaration

View File

@@ -163,7 +163,7 @@ private:
ResultCallback result_callback_{nullptr};
int8_t status_{GoalStatus::STATUS_ACCEPTED};
std::mutex handle_mutex_;
std::recursive_mutex handle_mutex_;
};
} // namespace rclcpp_action

View File

@@ -59,7 +59,7 @@ template<typename ActionT>
std::shared_future<typename ClientGoalHandle<ActionT>::WrappedResult>
ClientGoalHandle<ActionT>::async_get_result()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
if (!is_result_aware_) {
throw exceptions::UnawareGoalHandleError();
}
@@ -70,7 +70,7 @@ template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_result(const WrappedResult & wrapped_result)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
status_ = static_cast<int8_t>(wrapped_result.code);
result_promise_.set_value(wrapped_result);
if (result_callback_) {
@@ -82,7 +82,7 @@ template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_feedback_callback(FeedbackCallback callback)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
feedback_callback_ = callback;
}
@@ -90,7 +90,7 @@ template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_result_callback(ResultCallback callback)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
result_callback_ = callback;
}
@@ -98,7 +98,7 @@ template<typename ActionT>
int8_t
ClientGoalHandle<ActionT>::get_status()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
return status_;
}
@@ -106,7 +106,7 @@ template<typename ActionT>
void
ClientGoalHandle<ActionT>::set_status(int8_t status)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
status_ = status;
}
@@ -114,7 +114,7 @@ template<typename ActionT>
bool
ClientGoalHandle<ActionT>::is_feedback_aware()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
return feedback_callback_ != nullptr;
}
@@ -122,7 +122,7 @@ template<typename ActionT>
bool
ClientGoalHandle<ActionT>::is_result_aware()
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
return is_result_aware_;
}
@@ -130,7 +130,7 @@ template<typename ActionT>
bool
ClientGoalHandle<ActionT>::set_result_awareness(bool awareness)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
bool previous = is_result_aware_;
is_result_aware_ = awareness;
return previous;
@@ -140,7 +140,7 @@ template<typename ActionT>
void
ClientGoalHandle<ActionT>::invalidate(const exceptions::UnawareGoalHandleError & ex)
{
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
// Guard against multiple calls
if (is_invalidated()) {
return;
@@ -168,7 +168,7 @@ ClientGoalHandle<ActionT>::call_feedback_callback(
RCLCPP_ERROR(rclcpp::get_logger("rclcpp_action"), "Sent feedback to wrong goal handle.");
return;
}
std::lock_guard<std::mutex> guard(handle_mutex_);
std::lock_guard<std::recursive_mutex> guard(handle_mutex_);
if (nullptr == feedback_callback_) {
// Normal, some feedback messages may arrive after the goal result.
RCLCPP_DEBUG(rclcpp::get_logger("rclcpp_action"), "Received feedback but goal ignores it.");

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_action</name>
<version>22.2.0</version>
<version>26.0.0</version>
<description>Adds action APIs for C++.</description>
<maintainer email="ivanpauno@ekumenlabs.com">Ivan Paunovic</maintainer>
@@ -23,6 +23,7 @@
<depend>action_msgs</depend>
<depend>rclcpp</depend>
<depend>rcl_action</depend>
<depend>rcl</depend>
<depend>rcpputils</depend>
<depend>ament_cmake</depend>

View File

@@ -9,8 +9,7 @@ add_performance_test(
benchmark_action_client.cpp
TIMEOUT 240)
if(TARGET benchmark_action_client)
target_link_libraries(benchmark_action_client ${PROJECT_NAME})
ament_target_dependencies(benchmark_action_client rclcpp test_msgs)
target_link_libraries(benchmark_action_client ${PROJECT_NAME} rclcpp::rclcpp ${test_msgs_TARGETS})
endif()
add_performance_test(
@@ -18,6 +17,5 @@ add_performance_test(
benchmark_action_server.cpp
TIMEOUT 120)
if(TARGET benchmark_action_server)
target_link_libraries(benchmark_action_server ${PROJECT_NAME})
ament_target_dependencies(benchmark_action_server rclcpp test_msgs)
target_link_libraries(benchmark_action_server ${PROJECT_NAME} rclcpp::rclcpp ${test_msgs_TARGETS})
endif()

View File

@@ -852,6 +852,86 @@ TEST_F(TestClientAgainstServer, async_cancel_some_goals_with_callback)
EXPECT_EQ(rclcpp_action::GoalStatus::STATUS_CANCELED, goal_handle0->get_status());
}
TEST_F(TestClientAgainstServer, deadlock_in_callbacks)
{
std::atomic<bool> feedback_callback_called = false;
std::atomic<bool> response_callback_called = false;
std::atomic<bool> result_callback_called = false;
std::atomic<bool> no_deadlock = false;
std::thread tr = std::thread(
[&]() {
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);
ASSERT_TRUE(action_client->wait_for_action_server(WAIT_FOR_SERVER_TIMEOUT));
ActionGoal goal;
using GoalHandle = rclcpp_action::ClientGoalHandle<ActionType>;
rclcpp_action::Client<ActionType>::SendGoalOptions ops;
ops.feedback_callback =
[&feedback_callback_called](const GoalHandle::SharedPtr handle,
ActionType::Feedback::ConstSharedPtr) {
// call functions on the handle that acquire the lock
handle->get_status();
handle->is_feedback_aware();
handle->is_result_aware();
feedback_callback_called = true;
};
ops.goal_response_callback = [&response_callback_called](
const GoalHandle::SharedPtr & handle) {
// call functions on the handle that acquire the lock
handle->get_status();
handle->is_feedback_aware();
handle->is_result_aware();
response_callback_called = true;
};
ops.result_callback = [&result_callback_called](
const GoalHandle::WrappedResult &) {
result_callback_called = true;
};
goal.order = 6;
auto future_goal_handle = action_client->async_send_goal(goal, ops);
dual_spin_until_future_complete(future_goal_handle);
auto goal_handle = future_goal_handle.get();
ASSERT_TRUE(goal_handle);
ASSERT_EQ(RCL_RET_OK, rcl_set_ros_time_override(clock.get_clock_handle(), RCL_S_TO_NS(2)));
auto result_future = action_client->async_get_result(goal_handle);
dual_spin_until_future_complete(result_future);
EXPECT_TRUE(result_future.valid());
auto result = result_future.get();
no_deadlock = true;
});
auto start_time = std::chrono::system_clock::now();
while (std::chrono::system_clock::now() - start_time < std::chrono::milliseconds(2000) &&
!no_deadlock)
{
std::this_thread::sleep_for(std::chrono::milliseconds(10));
}
if (no_deadlock) {
tr.join();
} else {
// In case of a failure, the thread is assumed to be in a deadlock.
// We detach the thread so we don't block further tests.
tr.detach();
}
EXPECT_TRUE(no_deadlock);
EXPECT_TRUE(response_callback_called);
EXPECT_TRUE(result_callback_called);
EXPECT_TRUE(feedback_callback_called);
}
TEST_F(TestClientAgainstServer, send_rcl_errors)
{
auto action_client = rclcpp_action::create_client<ActionType>(client_node, action_name);

View File

@@ -2,6 +2,32 @@
Changelog for package rclcpp_components
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
26.0.0 (2024-01-24)
-------------------
25.0.0 (2023-12-26)
-------------------
* Switch to target_link_libraries. (`#2374 <https://github.com/ros2/rclcpp/issues/2374>`_)
* feat(rclcpp_components): support events executor in node main template (`#2366 <https://github.com/ros2/rclcpp/issues/2366>`_)
* fix(rclcpp_components): increase the service queue sizes in component_container (`#2363 <https://github.com/ros2/rclcpp/issues/2363>`_)
* Contributors: Chris Lalancette, Daisuke Nishimatsu, M. Fatih Cırıt
24.0.0 (2023-11-06)
-------------------
23.2.0 (2023-10-09)
-------------------
23.1.0 (2023-10-04)
-------------------
* Add missing header required by the rclcpp::NodeOptions type (`#2324 <https://github.com/ros2/rclcpp/issues/2324>`_)
* Contributors: Ignacio Vizzo
23.0.0 (2023-09-08)
-------------------
* Update API docs links in package READMEs (`#2302 <https://github.com/ros2/rclcpp/issues/2302>`_)
* Contributors: Christophe Bedard
22.2.0 (2023-09-07)
-------------------

View File

@@ -21,7 +21,7 @@ find_package(composition_interfaces REQUIRED)
find_package(rclcpp REQUIRED)
find_package(rcpputils REQUIRED)
# Add an interface library that can be dependend upon by libraries who register components
# Add an interface library that can be depended upon by libraries who register components
add_library(component INTERFACE)
target_include_directories(component INTERFACE
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
@@ -38,12 +38,14 @@ add_library(
target_include_directories(component_manager PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>")
ament_target_dependencies(component_manager
"ament_index_cpp"
"class_loader"
"composition_interfaces"
"rclcpp"
"rcpputils"
target_link_libraries(component_manager PUBLIC
${composition_interfaces_TARGETS}
rclcpp::rclcpp
)
target_link_libraries(component_manager PRIVATE
ament_index_cpp::ament_index_cpp
class_loader::class_loader
rcpputils::rcpputils
)
target_compile_definitions(component_manager
PRIVATE "RCLCPP_COMPONENTS_BUILDING_LIBRARY")
@@ -52,10 +54,7 @@ add_executable(
component_container
src/component_container.cpp
)
target_link_libraries(component_container component_manager)
ament_target_dependencies(component_container
"rclcpp"
)
target_link_libraries(component_container component_manager rclcpp::rclcpp)
set(node_main_template_install_dir "share/${PROJECT_NAME}")
install(FILES
@@ -66,20 +65,13 @@ add_executable(
component_container_mt
src/component_container_mt.cpp
)
target_link_libraries(component_container_mt component_manager)
ament_target_dependencies(component_container_mt
"rclcpp"
)
target_link_libraries(component_container_mt component_manager rclcpp::rclcpp)
add_executable(
component_container_isolated
src/component_container_isolated.cpp
)
target_link_libraries(component_container_isolated component_manager)
ament_target_dependencies(component_container_isolated
"rclcpp"
)
target_link_libraries(component_container_isolated component_manager rclcpp::rclcpp)
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
target_link_libraries(component_container "stdc++fs")

View File

@@ -2,7 +2,7 @@
Package containing tools for dynamically loadable components.
Visit the [rclcpp_components API documentation](http://docs.ros2.org/latest/api/rclcpp_components/) for a complete list of its main components and features.
The link to the latest rclcpp_components API documentation, which includes a complete list of its main components and features, can be found on the rclcpp_components package info page, at the [ROS Index](https://index.ros.org/p/rclcpp_components/).
## Quality Declaration

View File

@@ -70,10 +70,11 @@ macro(rclcpp_components_register_node target)
file(GENERATE OUTPUT ${PROJECT_BINARY_DIR}/rclcpp_components/node_main_${node}.cpp
INPUT ${PROJECT_BINARY_DIR}/rclcpp_components/node_main_configured_${node}.cpp.in)
add_executable(${node} ${PROJECT_BINARY_DIR}/rclcpp_components/node_main_${node}.cpp)
ament_target_dependencies(${node}
"rclcpp"
"class_loader"
"rclcpp_components")
target_link_libraries(${node}
class_loader::class_loader
rclcpp::rclcpp
rclcpp_components::component
)
install(TARGETS
${node}
DESTINATION lib/${PROJECT_NAME})

View File

@@ -15,6 +15,7 @@
#ifndef RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
#define RCLCPP_COMPONENTS__NODE_FACTORY_HPP__
#include "rclcpp/node_options.hpp"
#include "rclcpp_components/node_instance_wrapper.hpp"
namespace rclcpp_components

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