Compare commits

...

129 Commits

Author SHA1 Message Date
Michel Hidalgo
a7a3a38c7a Ensure rclcpp::TimerBase thread-safety.
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2020-02-19 16:53:07 -03:00
Shane Loretz
9dff67fc88 Allow node clock use in logging macros (#969) (#970) (#981)
Capturing a cached reference allows a clock object that is not a local
(e.g. the one returned by Node::get_clock()) to be passed to the throttle
logging macro.

Signed-off-by: Matt Schickler <mschickler@gmail.com>
Co-Authored-By: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

Co-authored-by: mschickler <mschickler@users.noreply.github.com>
Co-authored-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-02-06 08:23:08 -08:00
Jacob Perron
ef788db75a Complete published event message when declaring a parameter (#928) (#966)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>

Co-authored-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2020-01-30 17:15:48 -08:00
Jacob Perron
34dae0a8c9 0.8.4 2020-01-17 16:57:25 -08:00
Jacob Perron
7ff1b2991f Intra-process subscriber should use RMW actual qos. (ros2#913) (#914) (#965)
* Intra-process subscriber should use RMW actual qos. (ros2#913)

Signed-off-by: Todd Malsbary <todd.malsbary@intel.com>

Co-authored-by: Todd Malsbary <todd.malsbary@intel.com>
2020-01-17 17:18:21 -05:00
Jacob Perron
c0ad535249 Remove absolute path from installed CMake code (#948) (#950)
Otherwise, rclcpp_components_register_node() fails if used from a fat archive.

Related to https://github.com/ros2/ros2/issues/606.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2020-01-17 16:38:20 -05:00
Michael Carroll
2014385671 0.8.3
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2019-11-19 23:16:21 -06:00
astere-cpr
ecc39cace6 issue-919 Fixed "memory leak" in action clients (#920)
Whenever a call is made to `rclcpp_action::Client::wait_for_action_server`
a weak pointer to an Event object gets added to the graph_event_ vector
of the NodeGraph interface. This vector will be cleared on a node graph
change event, but if no such event occurs the weak pointer will be stuck
in the vector.  Furthermore, if client code issues repeated calls to
`wait_for_action_server` the vector will keep growing.

The fix moves the Event object creation right after the early return from
`wait_for_action_server` so that the Event object is not created in the
case that the server is known to be present and therefore there is no
need to wait for a node graph change event to occur.

Signed-off-by: Adrian Stere <astere@clearpath.ai>
2019-11-19 16:09:41 -03:00
William Woodall
a512d58a4f 0.8.2 2019-11-18 16:56:29 -08:00
William Woodall
64319062cd changelogs
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-11-18 16:56:14 -08:00
Christophe Bedard
1f79bdb3f7 Put subscription callback tracepoints in intraprocess subscription too (#918)
* Put subscription callback tracepoints in intraprocess subscription too

Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>

* Add missing tracetools header

Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>

* Move Subscription tracepoints back to outside of intra-process condition

Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>

* Revert API change by adding new tracepoint as an intermediate

Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2019-11-19 08:53:02 +08:00
alexfneves
5254e6abc3 Bug fix on shutdown_on_sigint (not set on InitOptions constructors) (#850)
Signed-off-by: Alex Fernandes Neves <alexfneves@gmail.com>
2019-11-19 08:45:39 +08:00
Dirk Thomas
6963c2d05a add support for STREAM logging macros (#926)
* refactor template logic around feature combinations

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>

* add support for STREAM logging macros

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
2019-11-18 16:07:57 -06:00
Michel Hidalgo
b92db52bb1 [rclcpp_action] Bump test_client timeout. (#917)
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-11-12 19:15:38 -03:00
Anas Abou Allaban
2ed4456474 Relax multithreaded test constraint (#907)
Signed-off-by: Anas Abou Allaban <aabouallaban@pm.me>
2019-10-29 20:50:17 -07:00
Shane Loretz
8cc2a9ba83 0.8.1
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2019-10-23 17:25:23 -07:00
Michael Carroll
2716d3e81e De-flake tests for rmw_connext (#899)
* Remove duplicate tests

Signed-off-by: Michael Carroll <michael@openrobotics.org>

* Expand test timeout to 240 seconds for Connext

Signed-off-by: Michael Carroll <michael@openrobotics.org>
2019-10-23 10:14:47 -05:00
Karsten Knese
a6e3412bb0 rename return functions for loaned messages (#896)
* fix up documentation for zero copy api

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* rename loan_message to borrow_loaned_message

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* use return_loaned_message_from

Signed-off-by: Karsten Knese <karsten@openrobotics.org>
2019-10-21 22:47:42 -05:00
Brian Marchi
3de5337376 Enable throttling logs (#879)
* Enable throttling logs

Signed-off-by: Brian Ezequiel Marchi <brian.marchi65@gmail.com>

* Change time source macro parameter generation

Signed-off-by: Brian Ezequiel Marchi <brian.marchi65@gmail.com>

* Modify only throttle parameters for logging

Signed-off-by: Brian Ezequiel Marchi <brian.marchi65@gmail.com>
2019-10-21 18:06:27 -03:00
Alberto Soragna
8525ee2eb5 New Intra-Process Communication (#778)
* basic ipc implementation from alsora/new_ipc_proposal

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

better use of node_topic create subscription

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

added intra process manager test

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

fixed ring buffer and added test

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

added intra process buffer test

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

added intra process buffer test

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

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

removed intra-process methods from subscription base

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

using lock_guard instead of unique_lock, renamed var without camel case

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

using unordered set and references in intra process manager

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

subscription intra-process does not depend anymore on subscription, but has a copy of the callback

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

changed buffer API to use rvo

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

avoid copying shared_ptr

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

revert not needed changes to create_subscription

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

updated tests according to new buffer APIs

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

updated types in ring buffer implementation avoid using uint32_t

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

using unique ptr for buffers in subscription_intra_process

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

added missing std::move in subscription_intra_process constructor

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

use consisting names for ring_buffer_implementation members

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

addressing typos, one-liners and similar from ivanpauno review

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

moved subscription_intra_process_base to its own files and moved non templated method from derived class

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

removed forward declarations, fixed include subscription_intra_process_base

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

removed member variable from do_intra_process_publish signature

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

declare public before private in intra_process_manager_impl

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

made matches_any_intra_process_publishers const

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

using const reference in get_all_matching_publishers

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

added deleter and alloc templates in intra_process_buffer

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

added RCLCPP_WARN to intra_process_manager_impl

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

passing context from node to subscription_intra_process

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

using allocators in intra_process_manager

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

use size_t instead of int in ring buffer indices

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

creating buffer inside subscription_intra_process constructor

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

fix lint errors

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

throw error if trying to dequeue when buffer empty; remove duplicated methods in intra_process_buffer

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

added todo for creating an rmw function for checking qos compatibility

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

test fixes

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

refactored intra_process_manager, removed ipm impl

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

added mutex in intra_process_manager add_* methods

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

added allocator to intra_process_buffer

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

added invalid intra_process qos test for subscription

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

throw error if history size is 0 with keep last and ipc

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

using allocator when creating unique_ptr from shared_ptr

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

adding deleter template argument to intra_process buffer

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

fix linter

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

throw error with callbackT different from messageT

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

updated deleter template argument in subscription factory

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

Fix typo in test fixture tear down method name (#787)

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

Add free function for creating service clients (#788)

Equivalent to the free function for creating a service.
Resolves #768

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

Cmake infrastructure for creating components (#784)

*cmake macro to create components for libraries with multiple nodes

Signed-off-by: Siddharth Kucheria <kucheria@usc.edu>

Allow registering multiple on_parameters_set_callback (#772)

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

fix for multiple nodes not being recognized (#790)

Signed-off-by: Siddharth Kucheria <kucheria@usc.edu>

Remove non-package from ament_target_dependencies() (#793)

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

fix linter issue (#795)

Signed-off-by: Siddharth Kucheria <kucheria@usc.edu>

Make TimeSource ignore use_sim_time events coming from other nodes. (#799)

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

passing deleter template parameter

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

small fixes for failing tests

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

fixed imports in test_intra_process_manager

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

using RCLCPP_SMART_PTR_ALIASES_ONLY and RCLCPP_PUBLIC macros

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

added RCLCPP_PUBLIC macros and virtual destructor to sub intra_process base

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

added unique_ptr alias to macros

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

updated test_intra_process_manager.cpp

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

remove mock msgs from rclcpp (#800)

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

Add line break after first open paren in multiline function call (#785)

* Add line break after first open paren in multiline function call

as per developer guide:
https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#open-versus-cuddled-braces
see https://github.com/ament/ament_lint/pull/148

Signed-off-by: Dan Rose <dan@digilabs.io>

Fix dedent when first function argument starts with a brace

Signed-off-by: Dan Rose <dan@digilabs.io>

Line break with multiline if condition
Remove line breaks where allowed.

Signed-off-by: Dan Rose <dan@digilabs.io>

Fixup after rebase

Signed-off-by: Dan Rose <dan@digilabs.io>

Fixup again after reverting indent_paren_open_brace

Signed-off-by: Dan Rose <dan@digilabs.io>

* Revert comment spacing change, condense some lines

Signed-off-by: Dan Rose <dan@digilabs.io>

Adapt to '--ros-args ... [--]'-based ROS args extraction (#816)

* Use --ros-args to deal with node arguments in rclcpp.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Document implicit --ros-args flag in NodeOptions::arguments().

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Add missing size_t to int cast.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Only add implicit --ros-args flag if not present already.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Add some rclcpp::NodeOptions test coverage.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Address peer review comments.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Please cpplint and uncrustify.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

Guard against making multiple result requests for a goal handle (#808)

This fixes a runtime error caused by a race condition when making consecutive requests for the
result.
Specifically, this happens if the user provides a result callback when sending a goal and then
calls async_get_result shortly after.

Resolves #783

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

Explain return value of spin_until_future_complete (#792)

Signed-off-by: Dan Rose <dan@digilabs.io>

Allow passing logger by const ref (#820)

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

Delete unnecessary call for get_node_by_group (#823)

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

Fix get_node_interfaces functions taking a pointer (#821)

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

add callback group as member variable and constructor arg (#811)

Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>

remove callback group as member variable

Wrap documentation examples in code blocks (#830)

This makes the code examples easier to read in the generated documentation.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

Crash in callback group pointer vector iterator (#814)

Signed-off-by: Guillaume Autran <gautran@clearpath.ai>

add mutex in add/remove_node and wait_for_work to protect concurrent use/change of memory_strategy_ (#837)

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>

Fix hang with timers in MultiThreadedExecutor (#835) (#836)

Signed-off-by: Todd Malsbary <todd.malsbary@intel.com>

Use of -r/--remap flags where appropriate. (#834)

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

Force explicit --ros-args in NodeOptions::arguments(). (#845)

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

Fail on invalid and unknown ROS specific arguments (#842)

* Fail on invalid and unknown ROS specific arguments.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Revert changes to utilities.hpp in rclcpp

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Fully revert change to utilities.hpp

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

Fix typo in deprecated warning. (#848)

"it's" instead of its

Signed-off-by: Luca Della Vedova <luca@openrobotics.org>

Add throwing parameter name if parameter is not set (#833)

* added throwing parameter name if parameter is not set

Signed-off-by: Alex <cvbn127@gmail.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

check valid timer handler 1st to reduce the time window for scan. (#841)

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

remove features and related code which were deprecated in dashing (#852)

Signed-off-by: William Woodall <william@osrfoundation.org>

reset error message before setting a new one, embed the original one (#854)

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>

restored virtual destructor in publisher_base

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

* fixup a few things after rebase

Signed-off-by: William Woodall <william@osrfoundation.org>

* refactor some API's and get code compiling again

Signed-off-by: William Woodall <william@osrfoundation.org>

* docs and style changes (whitespace)

Signed-off-by: William Woodall <william@osrfoundation.org>

* move new intra process internals into experimental namespace

Signed-off-by: William Woodall <william@osrfoundation.org>

* uncrustify

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix issues with LoanedMessages after rebase

Signed-off-by: William Woodall <william@osrfoundation.org>

* more fixups

Signed-off-by: William Woodall <william@osrfoundation.org>

* readd logic for avoiding in compatible QoS

Signed-off-by: William Woodall <william@osrfoundation.org>

* avoid an error when intra process is disabled

Signed-off-by: William Woodall <william@osrfoundation.org>

* change intra process to preserve pointer in cyclic_pipeline

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix issue matching topics in intra process

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix some issues with the tests after latest behavior change

Signed-off-by: William Woodall <william@osrfoundation.org>

* address review feedback

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix the initialization order

Signed-off-by: William Woodall <william@osrfoundation.org>

* avoid possible loss of data warning

Signed-off-by: William Woodall <william@osrfoundation.org>

* more fixes related to initialization

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix use of custom allocators

Signed-off-by: William Woodall <william@osrfoundation.org>
2019-10-21 16:46:38 -04:00
Ingo Lütkebohle
8fd9a0a00c Instrumentation update (#789)
* Add initial instrumentation
* Rename function registration method and elaborate on object copy issue
* Rely on get_symbol overload instead of using enable_if

Signed-off-by: Christophe Bedard <fixed-term.christophe.bourquebedard@de.bosch.com>
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
Signed-off-by: Ingo Luetkebohle <ingo.luetkebohle@de.bosch.com>
2019-10-18 19:28:04 -03:00
Karsten Knese
ed0bd16e31 Zero copy api (#864)
* loaned message

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

wip

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

loaned message

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* use publisher allocator

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* use unique_ptr inside LoanedMessage

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* can_loan_messages for subscription_base

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* use correct rcl_publish function

* default move constructor not allowed in gcc

Signed-off-by: Knese Karsten (CR/RTC-HMI4) <karsten.knese@us.bosch.com>

*  remove get_instance and make LoanedMessage constructor public

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* some more api doc

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* rebase ontop of master

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* warn when not being able to loan message

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* first draft loaned_message_sequence

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* rmw_take_loaned_message

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* address review comments

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* introduce rmw_publish_loaned_message

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* const correct publish

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* placement new

Signed-off-by: Knese Karsten <karsten@openrobotics.org>

* disable deleter for callback

Signed-off-by: Knese Karsten <karsten@openrobotics.org>

* print info once

Signed-off-by: Knese Karsten <karsten@openrobotics.org>

* uncrustify

Signed-off-by: Karsten Knese <karsten@openrobotics.org>
2019-10-18 14:51:24 -07:00
Michel Hidalgo
658f207dd8 Drop rclcpp remove_ros_arguments_null test case. (#894)
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-10-17 16:22:57 -03:00
Steven Macenski
d83a947c26 template node type for rclcpp action server and clients (#892)
Signed-off-by: stevemacenski <stevenmacenski@gmail.com>
2019-10-16 18:31:36 -07:00
Michael Carroll
231b991098 Trait tests for generated actions (#853)
* Trait tests for generated actions

Signed-off-by: Michael Carroll <michael@openrobotics.org>

* Address reviewer feedback

Signed-off-by: Michael Carroll <michael@openrobotics.org>
2019-10-15 13:51:57 -05:00
Jacob Perron
8e69b7d505 [rclcpp_action] Do not throw exception in action client if take fails (#888)
As documented in rcl_action, a return code of RCL_RET_ACTION_CLIENT_TAKE_FAILED does not mean that
an error occurred.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-10-14 09:03:59 -07:00
Michel Hidalgo
07cb443a18 [rclcpp_components] Enable intra-process comm via LoadNode request. (#871)
Look for a 'use_intra_process_comms' boolean in extra_arguments.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-10-10 13:14:24 -03:00
William Woodall
b8f7237087 add mechanism to pass rmw impl specific payloads during pub/sub creation (#882)
* add mechanism to pass rmw impl specific payloads during pub/sub creation

Signed-off-by: William Woodall <william@osrfoundation.org>

* use class instead of struct

Signed-off-by: William Woodall <william@osrfoundation.org>

* narrow API of rmw payload to just use rmw_*_options_t's

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup after recent change

Signed-off-by: William Woodall <william@osrfoundation.org>
2019-10-08 15:40:27 -07:00
William Woodall
27a97e868c make get_actual_qos return a rclcpp::QoS (#883)
* make get_actual_qos return a rclcpp::QoS

Signed-off-by: William Woodall <william@osrfoundation.org>

* make simpler following suggestion from review

Signed-off-by: William Woodall <william@osrfoundation.org>
2019-10-08 15:15:08 -07:00
Hunter L. Allen
9b4f049277 Fix Compiler Warning (#881)
Signed-off-by: Hunter L. Allen <hunterlallen@protonmail.com>
2019-10-04 16:07:26 -07:00
Brian Marchi
9723576821 Add callback handler for use_sim_time parameter #802 (#875)
* Add callback handler for use_sim_time parameter #802

Signed-off-by: Brian Ezequiel Marchi <brian.marchi65@gmail.com>
2019-10-01 10:14:55 -03:00
Michel Hidalgo
4afd1cd5ad Aggregate all component manager API tests. (#876)
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-09-26 18:16:04 -03:00
Michael Carroll
b178c47134 0.8.0
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2019-09-26 14:11:36 -05:00
William Woodall
81049c42c0 clean up publisher and subscription creation logic (#867)
* streamline creation of publishers after removing deprecated API

Signed-off-by: William Woodall <william@osrfoundation.org>

* use deduced template arguments to cleanup create_subscription

Signed-off-by: William Woodall <william@osrfoundation.org>

* add missing file

Signed-off-by: William Woodall <william@osrfoundation.org>

* streamline creation of subscriptions after removing deprecated API

Signed-off-by: William Woodall <william@osrfoundation.org>

* small subscription code cleanup to match publisher's style

Signed-off-by: William Woodall <william@osrfoundation.org>

* some fixes to rclcpp_lifecycle to match rclcpp

Signed-off-by: William Woodall <william@osrfoundation.org>

* add README to the rclcpp/detail include directory

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup SubscriptionBase's use of visibility macros

Signed-off-by: William Woodall <william@osrfoundation.org>

* reapply function to create default options, as it is still needed on Windows

Signed-off-by: William Woodall <william@osrfoundation.org>

* address review comments

Signed-off-by: William Woodall <william@osrfoundation.org>

* workaround cppcheck 1.89 syntax error

Signed-off-by: William Woodall <william@osrfoundation.org>
2019-09-25 16:22:06 -07:00
Michel Hidalgo
7728d8a38c Take parameter overrides provided through the CLI. (#865)
* Take parameter overrides provided through the CLI.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Address peer review comments.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Do not 'find_package' rcpputils twice.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-09-25 14:57:49 -03:00
Dirk Thomas
b6d18ccc81 add more context to exception message (#858)
* add more context to exception message

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>

* fix linter warnings

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
2019-09-19 13:15:19 -07:00
Chris Lalancette
9b47f36080 Fix UnknownGoalHandle error string. (#856)
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2019-09-18 17:52:49 -04:00
Dirk Thomas
cf838d1eb7 reset error message before setting a new one, embed the original one (#854)
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
2019-09-17 08:54:39 -07:00
William Woodall
89119c6422 remove features and related code which were deprecated in dashing (#852)
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-09-16 15:37:19 -07:00
fujitatomoya
dfb144d3cb check valid timer handler 1st to reduce the time window for scan. (#841)
Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>
2019-09-12 08:26:19 -07:00
ivanpauno
64c0f86f14 Add throwing parameter name if parameter is not set (#833)
* added throwing parameter name if parameter is not set

Signed-off-by: Alex <cvbn127@gmail.com>
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-09-10 10:21:25 -07:00
Luca Della Vedova
925460dcfb Fix typo in deprecated warning. (#848)
"it's" instead of its

Signed-off-by: Luca Della Vedova <luca@openrobotics.org>
2019-09-09 20:30:42 -07:00
Michel Hidalgo
458967bb56 Fail on invalid and unknown ROS specific arguments (#842)
* Fail on invalid and unknown ROS specific arguments.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Revert changes to utilities.hpp in rclcpp

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Fully revert change to utilities.hpp

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-09-06 11:35:36 -07:00
Michel Hidalgo
1fff8cbac1 Force explicit --ros-args in NodeOptions::arguments(). (#845)
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-09-05 13:20:47 -03:00
Michel Hidalgo
a6e80fcaea Use of -r/--remap flags where appropriate. (#834)
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-09-03 14:34:54 -03:00
Todd Malsbary
f153cf7173 Fix hang with timers in MultiThreadedExecutor (#835) (#836)
Signed-off-by: Todd Malsbary <todd.malsbary@intel.com>
2019-08-30 14:38:51 -03:00
Dirk Thomas
d5301c1c7c add mutex in add/remove_node and wait_for_work to protect concurrent use/change of memory_strategy_ (#837)
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
2019-08-29 15:09:39 -07:00
Guillaume Autran
4feecc5945 Crash in callback group pointer vector iterator (#814)
Signed-off-by: Guillaume Autran <gautran@clearpath.ai>
2019-08-28 18:11:11 -03:00
Jacob Perron
17841d6b7c Wrap documentation examples in code blocks (#830)
This makes the code examples easier to read in the generated documentation.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-08-23 08:58:08 -07:00
bpwilcox
ccd5b49186 add callback group as member variable and constructor arg (#811)
Signed-off-by: bpwilcox <bpwilcox@eng.ucsd.edu>

remove callback group as member variable
2019-08-21 17:02:37 -07:00
ivanpauno
4dbc7192d2 Fix get_node_interfaces functions taking a pointer (#821)
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-08-21 09:17:25 -03:00
fujitatomoya
65188b021d Delete unnecessary call for get_node_by_group (#823)
Signed-off-by: Tomoya.Fujita <Tomoya.Fujita@sony.com>
2019-08-20 09:24:59 -03:00
Karsten Knese
25f196989c Allow passing logger by const ref (#820)
Signed-off-by: Karsten Knese <karsten@openrobotics.org>
2019-08-09 09:56:38 -07:00
Dan Rose
78ab3731c9 Explain return value of spin_until_future_complete (#792)
Signed-off-by: Dan Rose <dan@digilabs.io>
2019-08-08 17:29:10 -03:00
Jacob Perron
41a99b64d3 Guard against making multiple result requests for a goal handle (#808)
This fixes a runtime error caused by a race condition when making consecutive requests for the
result.
Specifically, this happens if the user provides a result callback when sending a goal and then
calls async_get_result shortly after.

Resolves #783

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-08-07 09:26:19 -07:00
Michel Hidalgo
871c375da7 Adapt to '--ros-args ... [--]'-based ROS args extraction (#816)
* Use --ros-args to deal with node arguments in rclcpp.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Document implicit --ros-args flag in NodeOptions::arguments().

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Add missing size_t to int cast.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Only add implicit --ros-args flag if not present already.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Add some rclcpp::NodeOptions test coverage.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Address peer review comments.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>

* Please cpplint and uncrustify.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-08-07 12:58:25 -03:00
Dan Rose
4a5eed968c Add line break after first open paren in multiline function call (#785)
* Add line break after first open paren in multiline function call

as per developer guide:
https://index.ros.org/doc/ros2/Contributing/Developer-Guide/#open-versus-cuddled-braces
see https://github.com/ament/ament_lint/pull/148

Signed-off-by: Dan Rose <dan@digilabs.io>

Fix dedent when first function argument starts with a brace

Signed-off-by: Dan Rose <dan@digilabs.io>

Line break with multiline if condition
Remove line breaks where allowed.

Signed-off-by: Dan Rose <dan@digilabs.io>

Fixup after rebase

Signed-off-by: Dan Rose <dan@digilabs.io>

Fixup again after reverting indent_paren_open_brace

Signed-off-by: Dan Rose <dan@digilabs.io>

* Revert comment spacing change, condense some lines

Signed-off-by: Dan Rose <dan@digilabs.io>
2019-08-07 08:33:06 -07:00
Karsten Knese
dc3c36c7f0 remove mock msgs from rclcpp (#800)
Signed-off-by: Karsten Knese <karsten@openrobotics.org>
2019-08-05 22:43:30 -07:00
Michel Hidalgo
9be3e08cd4 Make TimeSource ignore use_sim_time events coming from other nodes. (#799)
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-07-29 16:57:51 -03:00
Siddharth Kucheria
9aacc6d895 fix linter issue (#795)
Signed-off-by: Siddharth Kucheria <kucheria@usc.edu>
2019-07-25 12:44:57 -07:00
Shane Loretz
c1d7c6b7be Remove non-package from ament_target_dependencies() (#793)
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2019-07-25 09:09:49 -07:00
Siddharth Kucheria
d31ea14985 fix for multiple nodes not being recognized (#790)
Signed-off-by: Siddharth Kucheria <kucheria@usc.edu>
2019-07-24 10:18:04 -07:00
ivanpauno
94ea26bffb Allow registering multiple on_parameters_set_callback (#772)
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-07-24 14:01:23 -03:00
Siddharth Kucheria
b214324bf2 Cmake infrastructure for creating components (#784)
*cmake macro to create components for libraries with multiple nodes

Signed-off-by: Siddharth Kucheria <kucheria@usc.edu>
2019-07-24 09:15:37 -07:00
Jacob Perron
cd063575ff Add free function for creating service clients (#788)
Equivalent to the free function for creating a service.
Resolves #768

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-07-22 10:25:48 -07:00
Jacob Perron
c7d01d7bf3 Fix typo in test fixture tear down method name (#787)
Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-07-18 14:49:42 -07:00
Michel Hidalgo
8c48dbede7 Include missing rcl headers in use. (#782)
And stop relying on transitive dependencies.

Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2019-07-12 11:01:21 -03:00
Chris Lalancette
f8d609496e Switch the NodeParameters lock to recursive. (#781)
* Switch the NodeParameters lock to recursive.

This is so that the on_set_parameter_callback can successfully
call other parameter methods (like get_parameter) without
deadlocking.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>

* Make sure that modifications can't happen within a callback.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>

* Review fixes.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>

* Set parameter_modification_enabled_ in calls that make modifications.

This will prevent any modification from within modification,
which is a good thing.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>

* Fix windows errors.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>

* Switch to an RAII-style recursion guard.

Also update the documentation.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2019-07-11 14:56:04 -04:00
Yathartha Tuladhar
207bcc5de3 Fixe error messages not printing to terminal (#777)
* Uncommented catch exception code to print error

Signed-off-by: Yathartha Tuladhar <yathartha3@gmail.com>

* put back the e

Signed-off-by: Yathartha Tuladhar <yathartha3@gmail.com>

* uncommented error printing in exception handling

Signed-off-by: Yathartha Tuladhar <yathartha3@gmail.com>

* Removed "/n" characters.
Update lifecycle_node_interface_impl.hpp

Signed-off-by: Yathartha Tuladhar <yathartha3@gmail.com>

* Fix CI issue and remove TODO

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-07-11 11:25:21 -07:00
Esteve Fernandez
378657865e Add default value to options in LifecycleNode construnctor. Update API documentation. (#775)
Signed-off-by: Esteve Fernandez <esteve@apache.org>
2019-07-02 10:51:47 -03:00
Alberto Soragna
61312b0576 changed on_parameter_event qos profile to rmw_qos_profile_parameter_events (#774)
Signed-off-by: alberto <alberto.soragna@gmail.com>
2019-07-01 12:44:34 -03:00
Carl Delsey
0ccac1e3bd Adding a factory method to create a Duration from seconds (#567)
* Adding a factory method to create a Duration from seconds

There are many places in the ROS codebase where a time duration is
specified as a floating point number of seconds. A factory function
to create a Duration object from these values makes the code a
bit simpler in many cases.

Signed-off-by: Carl Delsey <carl.r.delsey@intel.com>

* Adding some comments to clarify which constructors get matched.

Signed-off-by: Carl Delsey <carl.r.delsey@intel.com>
2019-06-28 12:43:09 -04:00
Scott K Logan
5a5a1fe530 Fix a comparison with a sign mismatch (#771)
Signed-off-by: Scott K Logan <logans@cottsay.net>
2019-06-26 10:26:07 -07:00
Karsten Knese
890b724e6f delete superfluous spaces (#770)
not exactly sure if this was on purpose, but it looks to me like a typo
2019-06-25 01:34:02 +02:00
Scott K Logan
dff36c2f67 Use params from node '/**' from parameter YAML file (#762)
The short-term goal of this change is to enable the creation of a
parameter YAML file which is applied to each node, regardless of node
name or namespace.

Future work is to support all wildcard syntax in node names in
parameter YAML files.

Signed-off-by: Scott K Logan <logans@cottsay.net>
2019-06-24 15:55:15 -07:00
ivanpauno
2069594cc2 Add ignore override argument to declare parameter (#767)
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-06-24 14:09:36 -03:00
Karsten Knese
e7c463dc74 use default parameter descriptor in parameters interface (#765)
* use default parameter descriptor in parameters interface

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* use default parameter for value

Signed-off-by: Karsten Knese <karsten@openrobotics.org>
2019-06-20 10:43:44 -07:00
Esteve Fernandez
2bee266adf Added support for const member functions (#763)
* Added support for const member functions

Signed-off-by: Esteve Fernandez <esteve@apache.org>

* Added signature for Windows

Signed-off-by: Esteve Fernandez <esteve@apache.org>

* Fix style

Signed-off-by: Esteve Fernandez <esteve@apache.org>

* Attempt at fixing function_traits for macOS

Signed-off-by: Esteve Fernandez <esteve@apache.org>
2019-06-14 13:09:47 -07:00
M. M
0ae3da49e7 add get_actual_qos() feature to subscriptions (#754)
Signed-off-by: Miaofei <miaofei@amazon.com>
2019-06-12 12:51:29 -03:00
ivanpauno
91198ef917 Ignore parameters overrides in set parameter methods when allowing undeclared parameters (#756)
* Ignore parameters overrides in set parameter methods when allowing undeclared parameters

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Address reviewer comment

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-06-11 12:04:38 -07:00
Shane Loretz
411e748632 Add rclcpp::create_timer() (#757)
* Add rclcpp::create_timer()

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* Friendly overload with node-like object

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* forward<CallbackT>

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* Make sure test with NodeWrapper compiles

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2019-06-10 16:37:55 -07:00
Alberto Soragna
4532d9cf78 checking origin of intra-process msg before taking them (#753)
Signed-off-by: alberto <alberto.soragna@gmail.com>
2019-06-06 17:56:50 -03:00
Jacob Perron
24080a458d 0.7.5 2019-05-30 17:31:20 -07:00
ivanpauno
8ff51833ad Avoid 'Intra process message no longer being stored when trying to ha… (#749)
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-05-30 15:54:02 -03:00
Michael Carroll
347f8d0b1d 0.7.4
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2019-05-29 18:53:14 -05:00
William Woodall
ecc95009f1 Rename parameter options (#745)
* rename initial_parameters in NodeOptions to parameter_overrides

Signed-off-by: William Woodall <william@osrfoundation.org>

* rename automatically_declare_initial_parameters to automatically_declare_parameters_from_overrides

Signed-off-by: William Woodall <william@osrfoundation.org>

* some additional renames I missed

Signed-off-by: William Woodall <william@osrfoundation.org>

* add test for setting after declaring with parameter overrides

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup NodeOptions docs

Signed-off-by: William Woodall <william+github@osrfoundation.org>

Co-Authored-By: chapulina <louise@openrobotics.org>

* clarify relationship between allow_undeclared_parameters and parameter_overrides

Signed-off-by: William Woodall <william@osrfoundation.org>
2019-05-29 12:12:42 -07:00
roderick-koehle
d2b2f9124e Bionic use of strerror_r (#742)
Since API 23 Android Bionic uses the GNU convention for strerror_r.
Following bionic/libc line 96,
  https://android.googlesource.com/platform/bionic.git/+/refs/heads/master/libc/include/string.h

Signed-off-by: roderick-koehle <50633232+roderick-koehle@users.noreply.github.com>
2019-05-29 11:15:50 -07:00
ivanpauno
ca01555936 Enforce parameter ranges (#735)
Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-05-29 12:03:06 -03:00
Alberto Soragna
0723a0a6fc removed not used parameter client (#740)
* removed not used parameter client

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

* moved parameter include directives to time source cpp file

Signed-off-by: alberto <alberto.soragna@gmail.com>
2019-05-28 14:52:32 -07:00
Dirk Thomas
8553fbea7f ensure removal of guard conditions of expired nodes from memory strategy (#741)
* change memory strategy API from vector of nodes to list of nodes

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>

* store guard_condition of node in executor and ensure that it is removed from the memory strategy

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>

* add unit test

Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
2019-05-24 15:31:31 -07:00
Jacob Perron
131a11bba5 Guard against calling null goal response callback (#738)
Also make sure to invoke the goal response callback before the result callback.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-05-23 21:00:58 -07:00
Jacob Perron
29308dc8bc Fix typo in log warning message (#737)
Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-05-23 16:10:36 -07:00
Dirk Thomas
06275105fc don't use global arguments for components loaded into the manager (#736)
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
2019-05-23 13:06:23 -07:00
ivanpauno
30df5cdf36 Throw nice errors when creating a publisher with intraprocess communication and incompatible qos policy (#729)
* Throw nice errors when creating a publisher with intraprocess communication and history keep all or history depth 0.

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-05-22 15:38:10 -03:00
William Woodall
1a662d46fb 0.7.3 2019-05-20 16:12:29 -07:00
William Woodall
4467ce9913 changelogs
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-05-20 16:12:05 -07:00
Emerson Knapp
005131dba8 I realize why the original was spelled wrong, volatile is a c++ keyword. Modify to not require learning a misspelling, and note why it can't have that name (#725)
Signed-off-by: Emerson Knapp <eknapp@amazon.com>
2019-05-17 18:18:58 -07:00
Emerson Knapp
05c19028f4 volitile -> volatile (#724)
Signed-off-by: Emerson Knapp <eknapp@amazon.com>
2019-05-17 15:37:38 -07:00
Dirk Thomas
a8f4d391f2 fix clang warning (#723)
Signed-off-by: Dirk Thomas <dirk-thomas@users.noreply.github.com>
2019-05-17 09:26:16 -07:00
Alberto Soragna
0682ceb3e1 Created on_parameter_event static function (#688)
* created static functions

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

* updated on_parameter_event to new subscriber api

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

* Update parameter_client.hpp

Reorderd typenames in template

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

* updated API also for Synchronous client and fixed linter errors

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

* added empty line at the end of files

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

* fixed linter error

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

* added parameter client tests

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

* added missing includes in unit test

Signed-off-by: alsora <alberto.soragna@gmail.com>
2019-05-16 13:45:10 -07:00
Jacob Perron
2152e0574b Guard against ParameterNotDeclaredException in parameter service callbacks (#718)
Fixes #705.

If the set_parameters() call fails, it's nice to be able to return a partial result.
Since there is no convenient method to obtain a partial result, we call set_parameters()
once for each parameter.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-05-15 16:10:53 -07:00
Michael Jeronimo
1081e75079 Add missing template functionality to lifecycle_node. (#707)
* Add missing template functionality to lifecycle_node.

Recent changes to the node_parameters interface was accompanied by additions to the
node.hpp header and implementation files. However, these additions were not also made
to the corresponding lifecycle node files. This PR includes the changes required for
the lifecycle node.

Going forward, I suggest that any code like this that supplements the basic node interfaces
should either be factored out into a separate header that both node and lifecycle_node
include, or that the supplemental code simply be included in the appropriate node_interface
file directly, if possible. That way we can avoid code duplication and its symptoms which
is node and lifecycle_node getting out of sync (which they have several times).

Signed-off-by: Michael Jeronimo <michael.jeronimo@intel.com>

* consolidate documentation to just be in rclcpp/node.hpp

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix visibility macros

Signed-off-by: William Woodall <william@osrfoundation.org>

* deprecation methods that were also deprecated in rclcpp::Node

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup variable name

Signed-off-by: William Woodall <william@osrfoundation.org>

* add missing template method implementations

Signed-off-by: William Woodall <william@osrfoundation.org>

* add more methods that were not ported to lifecycle node originally

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix cpplint

Signed-off-by: William Woodall <william@osrfoundation.org>
2019-05-15 15:15:40 -07:00
Prajakta Gokhale
b17bbf31b3 Fix heap-use-after-free and memory leaks reported from test_node.cpp (#719)
Fix AddressSanitizer errors reported by test_node.cpp unit test.

Signed-off-by: Prajakta Gokhale <prajaktg@amazon.com>
2019-05-14 21:08:33 -07:00
William Woodall
ef41059a75 0.7.2 2019-05-08 17:27:05 -07:00
William Woodall
cfeb6a6360 changelogs
Signed-off-by: William Woodall <william@osrfoundation.org>
2019-05-08 17:25:59 -07:00
William Woodall
c769b1b030 change API to encourage users to specify history depth always (#713)
* improve interoperability with rclcpp::Duration and std::chrono

Signed-off-by: William Woodall <william@osrfoundation.org>

* add to_rmw_time to Duration

Signed-off-by: William Woodall <william@osrfoundation.org>

* add new QoS class to rclcpp

Signed-off-by: William Woodall <william@osrfoundation.org>

* changes to NodeBase, NodeTopics, etc in preparation for changes to pub/sub

Signed-off-by: William Woodall <william@osrfoundation.org>

* refactor publisher creation to use new QoS class

Signed-off-by: William Woodall <william@osrfoundation.org>

* refactor subscription creation to use new QoS class

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixing fallout from changes to pub/sub creation

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixed Windows error: no appropriate default constructor available

why? who knows

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixed Windows error: could not deduce template argument for 'PublisherT'

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix missing vftable linker error on Windows

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix more cases of no suitable default constructor errors...

Signed-off-by: William Woodall <william@osrfoundation.org>

* prevent msvc from trying to interpret some cases as functions

Signed-off-by: William Woodall <william@osrfoundation.org>

* uncrustify

Signed-off-by: William Woodall <william@osrfoundation.org>

* cpplint

Signed-off-by: William Woodall <william@osrfoundation.org>

* add C++ version of default action qos

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixing lifecycle subscription signatures

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix allocators (we actually use this already in the pub/sub factory)

Signed-off-by: William Woodall <william@osrfoundation.org>

* suppress cppcheck on false positive syntax error

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix more cppcheck syntax error false positives

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix case where sub-type of QoS is used

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup get_node_topics_interface.hpp according to reviews and tests

Signed-off-by: William Woodall <william@osrfoundation.org>

* additional fixes based on local testing and CI

Signed-off-by: William Woodall <william@osrfoundation.org>

* another trick to avoid 'no appropriate default constructor available'

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix compiler error with clang on macOS

Signed-off-by: William Woodall <william@osrfoundation.org>

* disable build failure tests until we can get Jenkins to ignore their output

Signed-off-by: William Woodall <william@osrfoundation.org>

* suppress more cppcheck false positives

Signed-off-by: William Woodall <william@osrfoundation.org>

* add missing visibility macros to default QoS profile classes

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix another case of 'no appropriate default constructor available'

Signed-off-by: William Woodall <william@osrfoundation.org>

* unfortunately this actaully fixes a build error on Windows...

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix typos

Signed-off-by: William Woodall <william@osrfoundation.org>
2019-05-08 14:24:40 -07:00
ivanpauno
385cccc2cc Deprecate shared ptr publish (#709)
* Deprecate publish methods with shared_ptr signature

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Corrected with PR comments. Deprecated similar methods in lifecycle publisher

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Removed reference in unique_ptr publish call

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Corrected with PR comments. Corrected warning problem in lifecycle_publisher

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Deprecate publish call taking a raw ptr. Stop deprecating publish methods in LifecyclePublisher.

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Pleased linter

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Corrected mac warning

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Corrected serialized publish methods

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Avoid windows warning

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Not deprecate on windows

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-05-06 13:32:50 -07:00
M. M
d399fef9c6 Implement callbacks for liveliness and deadline QoS events (#695)
* implement deadline and liveliness qos callbacks

Signed-off-by: Miaofei <miaofei@amazon.com>

* fix windows build

Signed-off-by: Miaofei <miaofei@amazon.com>

* address feedback from pull request

Signed-off-by: Miaofei <miaofei@amazon.com>

* update formatting to be compatible with ros2 coding style and ament_uncrustify

Signed-off-by: Miaofei <miaofei@amazon.com>

* make QOSEventHandlerBase::add_to_wait_set() throw

Signed-off-by: Miaofei <miaofei@amazon.com>

* mark throw_from_rcl_error as [[noreturn]]

Signed-off-by: Miaofei <miaofei@amazon.com>

* fix windows compilation error

Signed-off-by: Miaofei <miaofei@amazon.com>

* Ignore uncrustify for single [[noreturn]] syntax instance

Signed-off-by: Emerson Knapp <eknapp@amazon.com>
2019-05-03 10:16:39 -07:00
Jacob Perron
ecf35114b6 Add return code to CancelGoal service response (#710)
* Populate return code of CancelGoal service response

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Throw if there is an error processing a cancel goal request

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Make cancel callback signature consistent across cancel methods and add tests

Refactored the callback signature for canceling one goal. Now it is the same as the other cancel methods.
This makes it easier to communicate the error code to the user.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>

* Address review

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-05-02 15:25:15 -07:00
Alberto Soragna
7ed130cf7a check for nullptr before publishing event (#714)
Signed-off-by: alberto <asoragna@irobot.com>
2019-05-02 10:24:44 -07:00
Michael Carroll
59d59b0c18 API updates for rmw preallocation work (#711)
* API updates for rmw preallocation work.

Signed-off-by: Michael Carroll <michael@openrobotics.org>

* Adjust for allocation in serialized message method.

Signed-off-by: Michael Carroll <michael@openrobotics.org>

* Fix extra take call.

Signed-off-by: Michael Carroll <michael@openrobotics.org>
2019-05-02 11:32:35 -05:00
jhdcs
a8a0788f81 [WIP / Re-Opened] Add functions to return formatted Node Name-Namespace strings (#698)
* Add functions to return formatted Node Name-Namespace strings

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Changed get_node_names to return fully qualified names, removed namespace method
Signed-off-by: Oswin So <oswinso@gmail.com>

* Removed unnecessary capture-by-reference

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Added first draft of tests

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Fixed bug creating phantom empty name/namespaces

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Re-ordered includes

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Swap checks to see if name is in set

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Fixed style errors from uncrustify

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Swapped to unordered_set

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Re-ordered includes again

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* nitpick: minimize vertical whitespace

see: https://google.github.io/styleguide/cppguide.html#Vertical_Whitespace

Signed-off-by: William Woodall <william@osrfoundation.org>

* Add API documentation for added get_node_names function

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Revert to last known semi-working point

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Modified expected test results

A fully-qualified name is "namespace"/"name". If namespace is set to be "/" (as they are in these tests), we would expect a qualified name of "//name"
Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Have get_node_names determine if central slash needed or not

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Corrected tests to not accept double slashes

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Undo changes to .gitignore

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Change qualified string construction to better handle invalid slashes

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Removed debugging statements

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Simplified slash-checking logic

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>
2019-05-01 17:56:05 -07:00
Tully Foote
e9101b49cd Add rcl_node_get_fully_qualified_name (ros2/rcl#255) (#712)
Signed-off-by: RARvolt <rarvolt@gmail.com>
2019-04-30 18:13:16 -07:00
Dima Dorezyuk
078d5ff662 Fixup clock (#696)
* Fix uninitialized bool in clock.cpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Fixup includes of clock.hpp/cpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Add documentation for exceptions to clock.hpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Adjust function signature of getters of clock.hpp/cpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Remove raw pointers Clock::create_jump_callback

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Remove unnecessary rclcpp namespace reference from clock.cpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Change exception to bad_alloc on JumpHandler allocation failure

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Fix missing nullptr check in Clock::on_time_jump

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Add JumpHandler::callback types

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Add warning for lifetime of Clock and JumpHandler

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Incorporate review

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Incorporate review

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>
2019-04-30 17:22:26 -07:00
Michael Jeronimo
dc05a2e755 Add assignment of parameter-related fields in node options constructor (#708)
* Add assignment of missing parameter-related fields in node options copy constructor.

The allow_undeclared_parameters and automatically_declare_initial_parameters fields of
the node options class were not assigned in the assignment operator, resulting in
an incorrect copy of the node options object, which also indirectly affects the
copy constructor.

Signed-off-by: Michael Jeronimo <michael.jeronimo@intel.com>

* Run linters
2019-04-30 13:47:20 -07:00
ivanpauno
98f610c114 New IntraProcessManager capable of storing shared_ptr<const T> (#690)
* Changed mapped_ring_buffer class to store both shared_ptr or unique_ptr

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Changed the IPM store and take methods

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Changed publish methods to take advantage of the new IPM

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Change how subscriptions handle intraprocess messages

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Modified publish method signatures

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Renamed 'publisher.cpp' and 'subscription.cpp' to 'publisher_base.cpp' and 'subscription_base.cpp'

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>

* Updated lifecycle_publisher publish methods

Signed-off-by: ivanpauno <ivanpauno@ekumenlabs.com>
2019-04-30 16:05:53 -03:00
Steven! Ragnarök
d34fa607a2 0.7.1 2019-04-26 11:37:10 -07:00
Jacob Perron
02050c3901 Add optional cancel callback to asynchronous cancel goal methods
Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-04-26 09:21:24 -07:00
Jacob Perron
1a0f8e3f28 Add optional result callback to async_get_result
Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-04-26 09:21:24 -07:00
Jacob Perron
0da966b981 Use options struct for passing callbacks to async_send_goal
Now supports callbacks for the goal response and result.
This also makes it easier to incorporate action clients in composable nodes since we don't have to rely on waiting on futures.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-04-26 09:21:24 -07:00
Shane Loretz
6b10841477 Read only parameters (#495)
* in progress broken test_time_source

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* style

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* test undeclared params

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* Only get parameter if it is set

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* doc fixup

Signed-off-by: William Woodall <william@osrfoundation.org>

* use override rather than virtual in places

Signed-off-by: William Woodall <william@osrfoundation.org>

* rename ParameterInfo_t to ParameterInfo and just use struct, no typedef

Signed-off-by: William Woodall <william@osrfoundation.org>

* add method to access ParameterValue within a Parameter

Signed-off-by: William Woodall <william@osrfoundation.org>

* enable get<Parameter> and get<ParameterValue> on Parameter class

Signed-off-by: William Woodall <william@osrfoundation.org>

* avoid const pass by value

Signed-off-by: William Woodall <william@osrfoundation.org>

* match type of enum in C++ to type used in message definition

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup after rebase

Signed-off-by: William Woodall <william@osrfoundation.org>

* more fixup after rebase

Signed-off-by: William Woodall <william@osrfoundation.org>

* replace create_parameter with declare_parameter

Signed-off-by: William Woodall <william@osrfoundation.org>

* provide implementation for templated declare_parameter method

Signed-off-by: William Woodall <william@osrfoundation.org>

* style

Signed-off-by: William Woodall <william@osrfoundation.org>

* do not use const reference when it's a primitive (like bool)

Signed-off-by: William Woodall <william@osrfoundation.org>

* typo

Signed-off-by: William Woodall <william@osrfoundation.org>

* follow to bool change that wasn't staged

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup tests

Signed-off-by: William Woodall <william@osrfoundation.org>

* added lots of docs, alternative API signatures, and some of the tests

Signed-off-by: William Woodall <william@osrfoundation.org>

* more tests and associated fixes

Signed-off-by: William Woodall <william@osrfoundation.org>

* address documentation feedback

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup previously added tests

Signed-off-by: William Woodall <william@osrfoundation.org>

* add tests and fixes for describe_parameter(s) and get_parameter_types

Signed-off-by: William Woodall <william@osrfoundation.org>

* remove old parameter tests

Signed-off-by: William Woodall <william@osrfoundation.org>

* use const reference where possible

Signed-off-by: William Woodall <william@osrfoundation.org>

* address comments

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix tests for deprecated methods

Signed-off-by: William Woodall <william@osrfoundation.org>

* address feedback

Signed-off-by: William Woodall <william@osrfoundation.org>

* significantly improve the reliability of the time_source tests

Signed-off-by: William Woodall <william@osrfoundation.org>

* uncrustify, cpplint, and cppcheck fixes

Signed-off-by: William Woodall <william@osrfoundation.org>

* Revert "significantly improve the reliability of the time_source tests"

This reverts commit 3ef385d841.

Signed-off-by: William Woodall <william@osrfoundation.org>

* only declare use_sim_time parameter if not already declared

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup rclcpp_lifecycle

Signed-off-by: William Woodall <william@osrfoundation.org>

* fixup tests

Signed-off-by: William Woodall <william@osrfoundation.org>

* add missing namespace scope which fails on Windows

Signed-off-by: William Woodall <william@osrfoundation.org>

* extend deprecation warning suppression to support Windows too

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix compiler warnings and missing visibility macro

Signed-off-by: William Woodall <william@osrfoundation.org>

* remove commented left over tests

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix compiler warning on Windows

Signed-off-by: William Woodall <william@osrfoundation.org>

* suppress deprecation warning on include of file in Windows

Signed-off-by: William Woodall <william@osrfoundation.org>

* avoid potential loss of data warning converting int64_t to int

Signed-off-by: William Woodall <william@osrfoundation.org>

* trying to fix more loss of data warnings

Signed-off-by: William Woodall <william@osrfoundation.org>

* fix test_node

Signed-off-by: William Woodall <william@osrfoundation.org>

* add option to automatically declare parameters from initial parameters (yaml file)

Signed-off-by: William Woodall <william@osrfoundation.org>

* remove redundant conditional

Signed-off-by: William Woodall <william@osrfoundation.org>
2019-04-23 10:44:55 -07:00
Guillaume Autran
97ed34a042 Fix a concurrency problem in the multithreaded executor (#703)
Both, the `Executor::execute_any_executable` and the destructor for the `AnyExecutable` object used by the multithreaded executor, reset the `can_be_taken_from_` flag on a MutuallyExclusive group. This cause the variable to get out of sync and threads to process executables out of sequence.

This fix clears the callback group variable of the `AnyExecutable` instance effectively preventing its destructor from modifying the variable at the wrong time.

Issue: #702
Signed-off-by: Guillaume Autran <gautran@clearpath.ai>
2019-04-22 11:39:32 -07:00
Dima Dorezyuk
ddf4d345b3 Fixup utilities (#692)
* Fixed dependencies in the utility.hpp/cpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Add missing documentation for exceptions in utility.hpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Add rclcpp namespace to the utility.cpp

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Add check for a non-negative nonros_argc value

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Simplify syntax for the return_arguments

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>

* Incorporate Review

Signed-off-by: Dmitrij Dorezyuk <dmitrij.dorezyuk@hotmail.de>
2019-04-19 16:19:17 -07:00
Devin Bonnie
ddcc1ec553 Add method to read timer cancellation (#697)
* Add method to read timer cancellation

Signed-off-by: Devin Bonnie <dbbonnie@amazon.com>

* Add improved documentation
Add improved and more unit tests

Signed-off-by: Devin Bonnie <dbbonnie@amazon.com>

* Add missing include
Add override for inherited methods

Signed-off-by: Devin Bonnie <dbbonnie@amazon.com>

* Addressed review comments

Signed-off-by: Devin Bonnie <dbbonnie@amazon.com>
2019-04-18 14:29:38 -07:00
Karsten Knese
60996d1e59 overload for node interfaces (#700)
* overload for node interfaces

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* remove new line

Signed-off-by: Karsten Knese <karsten@openrobotics.org>

* overload client for node iterfaces

Signed-off-by: Karsten Knese <karsten@openrobotics.org>
2019-04-17 17:01:26 -05:00
jhdcs
713dd0c184 [WIP] Exception Generator function for implementing "from_rcl_error" (#678)
* Created function to generate exception objects

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Created function to generate exception objects

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Fixed typo

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Fixed typo

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Throw exceptions not created by ret

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Throw exceptions not created by ret

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* convert throw_from_rcl_error to use from_rcl_error

Mostly just a convenience function
Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Updated .gitignore

Please ignore
Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Re-ordered functions to allow compilation

Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* Revert "Updated .gitignore"

This reverts commit bee0ee13ce687bc56bdc7ad1e8382506d9aef428.
Signed-off-by: Jacob Hassold <jhassold@dcscorp.com>

* restore .gitignore to original state

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

* oops, actually restore .gitignore

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2019-04-16 11:25:25 -07:00
Jacob Perron
68d0ac1e61 Rename action state transitions (#677)
* Rename action state transitions

Now using active verbs as described in the design doc:

http://design.ros2.org/articles/actions.html#goal-states

Connects to ros2/rcl#399.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2019-04-16 04:46:40 -07:00
M. M
70f48d68b9 correct initialization of rmw_qos_profile_t struct instances (#691)
Signed-off-by: Miaofei <miaofei@amazon.com>
2019-04-15 17:44:44 -07:00
Víctor Mayoral Vilches
fcfe94e404 logging, remove_const before comparison (#680)
* logging, remove_const before comparison

This change removes the const value from the logger before
comparing with std::is_same.

Signed-off-by: Víctor Mayoral Vilches <v.mayoralv@gmail.com>

* logging template, replace remove_const by remove_cv

Signed-off-by: Víctor Mayoral Vilches <v.mayoralv@gmail.com>

* Append typename

Located after compiling rclcpp_action from source

Signed-off-by: Víctor Mayoral Vilches <v.mayoralv@gmail.com>
2019-04-15 11:42:07 -07:00
194 changed files with 14494 additions and 5437 deletions

View File

@@ -2,6 +2,143 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
0.8.4 (2020-01-17)
------------------
* Intra-process subscriber should use RMW actual qos (ros2`#913 <https://github.com/ros2/rclcpp/issues/913>`_) (`#914 <https://github.com/ros2/rclcpp/issues/914>`_) (`#965 <https://github.com/ros2/rclcpp/issues/965>`_)
* Contributors: Todd Malsbary
0.8.3 (2019-11-19)
------------------
0.8.2 (2019-11-18)
------------------
* Updated tracing logic to match changes in rclcpp's intra-process system (`#918 <https://github.com/ros2/rclcpp/issues/918>`_)
* Fixed a bug that prevented the ``shutdown_on_sigint`` option to not work correctly (`#850 <https://github.com/ros2/rclcpp/issues/850>`_)
* Added support for STREAM logging macros (`#926 <https://github.com/ros2/rclcpp/issues/926>`_)
* Relaxed multithreaded test constraint (`#907 <https://github.com/ros2/rclcpp/issues/907>`_)
* Contributors: Anas Abou Allaban, Christophe Bedard, Dirk Thomas, alexfneves
0.8.1 (2019-10-23)
------------------
* De-flake tests for rmw_connext (`#899 <https://github.com/ros2/rclcpp/issues/899>`_)
* rename return functions for loaned messages (`#896 <https://github.com/ros2/rclcpp/issues/896>`_)
* Enable throttling logs (`#879 <https://github.com/ros2/rclcpp/issues/879>`_)
* New Intra-Process Communication (`#778 <https://github.com/ros2/rclcpp/issues/778>`_)
* Instrumentation update (`#789 <https://github.com/ros2/rclcpp/issues/789>`_)
* Zero copy api (`#864 <https://github.com/ros2/rclcpp/issues/864>`_)
* Drop rclcpp remove_ros_arguments_null test case. (`#894 <https://github.com/ros2/rclcpp/issues/894>`_)
* add mechanism to pass rmw impl specific payloads during pub/sub creation (`#882 <https://github.com/ros2/rclcpp/issues/882>`_)
* make get_actual_qos return a rclcpp::QoS (`#883 <https://github.com/ros2/rclcpp/issues/883>`_)
* Fix Compiler Warning (`#881 <https://github.com/ros2/rclcpp/issues/881>`_)
* Add callback handler for use_sim_time parameter `#802 <https://github.com/ros2/rclcpp/issues/802>`_ (`#875 <https://github.com/ros2/rclcpp/issues/875>`_)
* Contributors: Alberto Soragna, Brian Marchi, Hunter L. Allen, Ingo Lütkebohle, Karsten Knese, Michael Carroll, Michel Hidalgo, William Woodall
0.8.0 (2019-09-26)
------------------
* clean up publisher and subscription creation logic (`#867 <https://github.com/ros2/rclcpp/issues/867>`_)
* Take parameter overrides provided through the CLI. (`#865 <https://github.com/ros2/rclcpp/issues/865>`_)
* add more context to exception message (`#858 <https://github.com/ros2/rclcpp/issues/858>`_)
* remove features and related code which were deprecated in dashing (`#852 <https://github.com/ros2/rclcpp/issues/852>`_)
* check valid timer handler 1st to reduce the time window for scan. (`#841 <https://github.com/ros2/rclcpp/issues/841>`_)
* Add throwing parameter name if parameter is not set (`#833 <https://github.com/ros2/rclcpp/issues/833>`_)
* Fix typo in deprecated warning. (`#848 <https://github.com/ros2/rclcpp/issues/848>`_)
* Fail on invalid and unknown ROS specific arguments (`#842 <https://github.com/ros2/rclcpp/issues/842>`_)
* Force explicit --ros-args in NodeOptions::arguments(). (`#845 <https://github.com/ros2/rclcpp/issues/845>`_)
* Use of -r/--remap flags where appropriate. (`#834 <https://github.com/ros2/rclcpp/issues/834>`_)
* Fix hang with timers in MultiThreadedExecutor (`#835 <https://github.com/ros2/rclcpp/issues/835>`_) (`#836 <https://github.com/ros2/rclcpp/issues/836>`_)
* add mutex in add/remove_node and wait_for_work to protect concurrent use/change of memory_strategy\_ (`#837 <https://github.com/ros2/rclcpp/issues/837>`_)
* Crash in callback group pointer vector iterator (`#814 <https://github.com/ros2/rclcpp/issues/814>`_)
* Wrap documentation examples in code blocks (`#830 <https://github.com/ros2/rclcpp/issues/830>`_)
* add callback group as member variable and constructor arg (`#811 <https://github.com/ros2/rclcpp/issues/811>`_)
* Fix get_node_interfaces functions taking a pointer (`#821 <https://github.com/ros2/rclcpp/issues/821>`_)
* Delete unnecessary call for get_node_by_group (`#823 <https://github.com/ros2/rclcpp/issues/823>`_)
* Allow passing logger by const ref (`#820 <https://github.com/ros2/rclcpp/issues/820>`_)
* Explain return value of spin_until_future_complete (`#792 <https://github.com/ros2/rclcpp/issues/792>`_)
* Adapt to '--ros-args ... [--]'-based ROS args extraction (`#816 <https://github.com/ros2/rclcpp/issues/816>`_)
* Add line break after first open paren in multiline function call (`#785 <https://github.com/ros2/rclcpp/issues/785>`_)
* remove mock msgs from rclcpp (`#800 <https://github.com/ros2/rclcpp/issues/800>`_)
* Make TimeSource ignore use_sim_time events coming from other nodes. (`#799 <https://github.com/ros2/rclcpp/issues/799>`_)
* Allow registering multiple on_parameters_set_callback (`#772 <https://github.com/ros2/rclcpp/issues/772>`_)
* Add free function for creating service clients (`#788 <https://github.com/ros2/rclcpp/issues/788>`_)
* Include missing rcl headers in use. (`#782 <https://github.com/ros2/rclcpp/issues/782>`_)
* Switch the NodeParameters lock to recursive. (`#781 <https://github.com/ros2/rclcpp/issues/781>`_)
* changed on_parameter_event qos profile to rmw_qos_profile_parameter_events (`#774 <https://github.com/ros2/rclcpp/issues/774>`_)
* Adding a factory method to create a Duration from seconds (`#567 <https://github.com/ros2/rclcpp/issues/567>`_)
* Fix a comparison with a sign mismatch (`#771 <https://github.com/ros2/rclcpp/issues/771>`_)
* delete superfluous spaces (`#770 <https://github.com/ros2/rclcpp/issues/770>`_)
* Use params from node '/\*\*' from parameter YAML file (`#762 <https://github.com/ros2/rclcpp/issues/762>`_)
* Add ignore override argument to declare parameter (`#767 <https://github.com/ros2/rclcpp/issues/767>`_)
* use default parameter descriptor in parameters interface (`#765 <https://github.com/ros2/rclcpp/issues/765>`_)
* Added support for const member functions (`#763 <https://github.com/ros2/rclcpp/issues/763>`_)
* add get_actual_qos() feature to subscriptions (`#754 <https://github.com/ros2/rclcpp/issues/754>`_)
* Ignore parameters overrides in set parameter methods when allowing undeclared parameters (`#756 <https://github.com/ros2/rclcpp/issues/756>`_)
* Add rclcpp::create_timer() (`#757 <https://github.com/ros2/rclcpp/issues/757>`_)
* checking origin of intra-process msg before taking them (`#753 <https://github.com/ros2/rclcpp/issues/753>`_)
* Contributors: Alberto Soragna, Carl Delsey, Chris Lalancette, Dan Rose, Dirk Thomas, Esteve Fernandez, Guillaume Autran, Jacob Perron, Karsten Knese, Luca Della Vedova, M. M, Michel Hidalgo, Scott K Logan, Shane Loretz, Todd Malsbary, William Woodall, bpwilcox, fujitatomoya, ivanpauno
0.7.5 (2019-05-30)
------------------
* Avoid 'Intra process message no longer being stored when trying to handle it' warning (`#749 <https://github.com/ros2/rclcpp/issues/749>`_)
* Contributors: ivanpauno
0.7.4 (2019-05-29)
------------------
* Rename parameter options (`#745 <https://github.com/ros2/rclcpp/issues/745>`_)
* Bionic use of strerror_r (`#742 <https://github.com/ros2/rclcpp/issues/742>`_)
* Enforce parameter ranges (`#735 <https://github.com/ros2/rclcpp/issues/735>`_)
* removed not used parameter client (`#740 <https://github.com/ros2/rclcpp/issues/740>`_)
* ensure removal of guard conditions of expired nodes from memory strategy (`#741 <https://github.com/ros2/rclcpp/issues/741>`_)
* Fix typo in log warning message (`#737 <https://github.com/ros2/rclcpp/issues/737>`_)
* Throw nice errors when creating a publisher with intraprocess communication and incompatible qos policy (`#729 <https://github.com/ros2/rclcpp/issues/729>`_)
* Contributors: Alberto Soragna, Dirk Thomas, Jacob Perron, William Woodall, ivanpauno, roderick-koehle
0.7.3 (2019-05-20)
------------------
* Fixed misspelling, volitile -> volatile (`#724 <https://github.com/ros2/rclcpp/issues/724>`_), and then fixed that since it is a C++ keyword to be ``durability_volatile`` (`#725 <https://github.com/ros2/rclcpp/issues/725>`_)
* Fixed a clang warning (`#723 <https://github.com/ros2/rclcpp/issues/723>`_)
* Added ``on_parameter_event`` static method to the ``AsyncParametersClient`` (`#688 <https://github.com/ros2/rclcpp/issues/688>`_)
* Added a guard against ``ParameterNotDeclaredException`` throwing from within the parameter service callbacks. (`#718 <https://github.com/ros2/rclcpp/issues/718>`_)
* Added missing template functionality to lifecycle_node. (`#707 <https://github.com/ros2/rclcpp/issues/707>`_)
* Fixed heap-use-after-free and memory leaks reported from ``test_node.cpp`` (`#719 <https://github.com/ros2/rclcpp/issues/719>`_)
* Contributors: Alberto Soragna, Dirk Thomas, Emerson Knapp, Jacob Perron, Michael Jeronimo, Prajakta Gokhale
0.7.2 (2019-05-08)
------------------
* Added new way to specify QoS settings for publishers and subscriptions. (`#713 <https://github.com/ros2/rclcpp/issues/713>`_)
* The new way requires that you specify a history depth when creating a publisher or subscription.
* In the past it was possible to create one without specifying any history depth, but these signatures have been deprecated.
* Deprecated ``shared_ptr`` and raw pointer versions of ``Publisher<T>::publish()``. (`#709 <https://github.com/ros2/rclcpp/issues/709>`_)
* Implemented API to set callbacks for liveliness and deadline QoS events for publishers and subscriptions. (`#695 <https://github.com/ros2/rclcpp/issues/695>`_)
* Fixed a segmentation fault when publishing a parameter event when they ought to be disabled. (`#714 <https://github.com/ros2/rclcpp/issues/714>`_)
* Changes required for upcoming pre-allocation API. (`#711 <https://github.com/ros2/rclcpp/issues/711>`_)
* Changed ``Node::get_node_names()`` to return the full node names rather than just the base name. (`#698 <https://github.com/ros2/rclcpp/issues/698>`_)
* Remove logic made redundant by the `ros2/rcl#255 <https://github.com/ros2/rcl/issues/255>`_ pull request. (`#712 <https://github.com/ros2/rclcpp/issues/712>`_)
* Various improvements for ``rclcpp::Clock``. (`#696 <https://github.com/ros2/rclcpp/issues/696>`_)
* Fixed uninitialized bool in ``clock.cpp``.
* Fixed up includes of ``clock.hpp/cpp``.
* Added documentation for exceptions to ``clock.hpp``.
* Adjusted function signature of getters of ``clock.hpp/cpp``.
* Removed raw pointers to ``Clock::create_jump_callback``.
* Removed unnecessary ``rclcpp`` namespace reference from ``clock.cpp``.
* Changed exception to ``bad_alloc`` on ``JumpHandler`` allocation failure.
* Fixed missing ``nullptr`` check in ``Clock::on_time_jump``.
* Added ``JumpHandler::callback`` types.
* Added warning for lifetime of Clock and JumpHandler
* Fixed bug left over from the `pull request #495 <https://github.com/ros2/rclcpp/pull/495>`_. (`#708 <https://github.com/ros2/rclcpp/issues/708>`_)
* Changed the ``IntraProcessManager`` to be capable of storing ``shared_ptr<const T>`` in addition to ``unique_ptr<T>``. (`#690 <https://github.com/ros2/rclcpp/issues/690>`_)
* Contributors: Alberto Soragna, Dima Dorezyuk, M. M, Michael Carroll, Michael Jeronimo, Tully Foote, William Woodall, ivanpauno, jhdcs
0.7.1 (2019-04-26)
------------------
* Added read only parameters. (`#495 <https://github.com/ros2/rclcpp/issues/495>`_)
* Fixed a concurrency problem in the multithreaded executor. (`#703 <https://github.com/ros2/rclcpp/issues/703>`_)
* Fixup utilities. (`#692 <https://github.com/ros2/rclcpp/issues/692>`_)
* Added method to read timer cancellation. (`#697 <https://github.com/ros2/rclcpp/issues/697>`_)
* Added Exception Generator function for implementing "from_rcl_error". (`#678 <https://github.com/ros2/rclcpp/issues/678>`_)
* Updated initialization of rmw_qos_profile_t struct instances. (`#691 <https://github.com/ros2/rclcpp/issues/691>`_)
* Removed the const value from the logger before comparison. (`#680 <https://github.com/ros2/rclcpp/issues/680>`_)
* Contributors: Devin Bonnie, Dima Dorezyuk, Guillaume Autran, M. M, Shane Loretz, Víctor Mayoral Vilches, William Woodall, jhdcs
0.7.0 (2019-04-14)
------------------
* Added Options-struct interfaces for creating publishers/subscribers (pre-QoS, standalone). (`#673 <https://github.com/ros2/rclcpp/issues/673>`_)

View File

@@ -7,12 +7,14 @@ find_package(builtin_interfaces REQUIRED)
find_package(rcl REQUIRED)
find_package(rcl_interfaces REQUIRED)
find_package(rcl_yaml_param_parser REQUIRED)
find_package(rcpputils REQUIRED)
find_package(rmw REQUIRED)
find_package(rmw_implementation REQUIRED)
find_package(rosgraph_msgs REQUIRED)
find_package(rosidl_generator_cpp REQUIRED)
find_package(rosidl_typesupport_c REQUIRED)
find_package(rosidl_typesupport_cpp REQUIRED)
find_package(tracetools REQUIRED)
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
@@ -31,6 +33,9 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/clock.cpp
src/rclcpp/context.cpp
src/rclcpp/contexts/default_context.cpp
src/rclcpp/detail/rmw_implementation_specific_payload.cpp
src/rclcpp/detail/rmw_implementation_specific_publisher_payload.cpp
src/rclcpp/detail/rmw_implementation_specific_subscription_payload.cpp
src/rclcpp/duration.cpp
src/rclcpp/event.cpp
src/rclcpp/exceptions.cpp
@@ -42,7 +47,6 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/graph_listener.cpp
src/rclcpp/init_options.cpp
src/rclcpp/intra_process_manager.cpp
src/rclcpp/intra_process_manager_impl.cpp
src/rclcpp/logger.cpp
src/rclcpp/memory_strategies.cpp
src/rclcpp/memory_strategy.cpp
@@ -64,10 +68,13 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/parameter_events_filter.cpp
src/rclcpp/parameter_map.cpp
src/rclcpp/parameter_service.cpp
src/rclcpp/publisher.cpp
src/rclcpp/publisher_base.cpp
src/rclcpp/qos.cpp
src/rclcpp/qos_event.cpp
src/rclcpp/service.cpp
src/rclcpp/signal_handler.cpp
src/rclcpp/subscription.cpp
src/rclcpp/subscription_base.cpp
src/rclcpp/subscription_intra_process_base.cpp
src/rclcpp/time.cpp
src/rclcpp/time_source.cpp
src/rclcpp/timer.cpp
@@ -104,10 +111,13 @@ add_library(${PROJECT_NAME}
ament_target_dependencies(${PROJECT_NAME}
"rcl"
"rcl_yaml_param_parser"
"rcpputils"
"builtin_interfaces"
"rosgraph_msgs"
"rosidl_typesupport_cpp"
"rosidl_generator_cpp")
"rosidl_generator_cpp"
"tracetools"
)
# Causes the visibility macros to use dllexport rather than dllimport,
# which is appropriate when building the dll but not consuming it.
@@ -127,12 +137,14 @@ ament_export_libraries(${PROJECT_NAME})
ament_export_dependencies(ament_cmake)
ament_export_dependencies(rcl)
ament_export_dependencies(rcpputils)
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_generator_cpp)
ament_export_dependencies(rcl_yaml_param_parser)
ament_export_dependencies(tracetools)
if(BUILD_TESTING)
find_package(ament_cmake_gtest REQUIRED)
@@ -141,6 +153,12 @@ if(BUILD_TESTING)
find_package(rmw_implementation_cmake REQUIRED)
find_package(test_msgs REQUIRED)
include(cmake/rclcpp_add_build_failure_test.cmake)
add_definitions(-DTEST_RESOURCES_DIRECTORY="${CMAKE_CURRENT_BINARY_DIR}/test/resources")
ament_add_gtest(test_client test/test_client.cpp)
if(TARGET test_client)
ament_target_dependencies(test_client
@@ -151,6 +169,18 @@ if(BUILD_TESTING)
)
target_link_libraries(test_client ${PROJECT_NAME})
endif()
ament_add_gtest(test_create_timer test/test_create_timer.cpp)
if(TARGET test_create_timer)
ament_target_dependencies(test_create_timer
"rcl_interfaces"
"rmw"
"rcl"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_create_timer ${PROJECT_NAME})
target_include_directories(test_create_timer PRIVATE test/)
endif()
ament_add_gtest(test_expand_topic_or_service_name test/test_expand_topic_or_service_name.cpp)
if(TARGET test_expand_topic_or_service_name)
ament_target_dependencies(test_expand_topic_or_service_name
@@ -170,17 +200,7 @@ if(BUILD_TESTING)
"rosidl_typesupport_cpp"
)
endif()
ament_add_gtest(test_mapped_ring_buffer test/test_mapped_ring_buffer.cpp)
if(TARGET test_mapped_ring_buffer)
ament_target_dependencies(test_mapped_ring_buffer
"rcl"
"rcl_interfaces"
"rmw"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp"
)
endif()
ament_add_gtest(test_intra_process_manager test/test_intra_process_manager.cpp)
ament_add_gmock(test_intra_process_manager test/test_intra_process_manager.cpp)
if(TARGET test_intra_process_manager)
ament_target_dependencies(test_intra_process_manager
"rcl"
@@ -189,17 +209,74 @@ if(BUILD_TESTING)
"rosidl_generator_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_intra_process_manager ${PROJECT_NAME})
endif()
ament_add_gtest(test_node test/test_node.cpp)
ament_add_gtest(test_ring_buffer_implementation test/test_ring_buffer_implementation.cpp)
if(TARGET test_ring_buffer_implementation)
ament_target_dependencies(test_ring_buffer_implementation
"rcl_interfaces"
"rmw"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_ring_buffer_implementation ${PROJECT_NAME})
endif()
ament_add_gtest(test_intra_process_buffer test/test_intra_process_buffer.cpp)
if(TARGET test_intra_process_buffer)
ament_target_dependencies(test_intra_process_buffer
"rcl_interfaces"
"rmw"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_intra_process_buffer ${PROJECT_NAME})
endif()
ament_add_gtest(test_loaned_message test/test_loaned_message.cpp)
ament_target_dependencies(test_loaned_message
"test_msgs"
)
target_link_libraries(test_loaned_message ${PROJECT_NAME})
ament_add_gtest(test_node test/test_node.cpp TIMEOUT 240)
if(TARGET test_node)
ament_target_dependencies(test_node
"rcl_interfaces"
"rcpputils"
"rmw"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_node ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_interfaces__get_node_interfaces
test/node_interfaces/test_get_node_interfaces.cpp)
if(TARGET test_node_interfaces__get_node_interfaces)
target_link_libraries(test_node_interfaces__get_node_interfaces ${PROJECT_NAME})
endif()
# TODO(wjwwood): reenable these build failure tests when I can get Jenkins to ignore their output
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ref_rclcpp_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ptr_rclcpp_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ref_wrapped_node
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ref_wrapped_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ref_rclcpp_node
# ${PROJECT_NAME})
# rclcpp_add_build_failure_test(build_failure__get_node_topics_interface_const_ptr_wrapped_node
# test/node_interfaces/test_does_not_compile/get_node_topics_interface_const_ptr_wrapped_node.cpp)
# target_link_libraries(build_failure__get_node_topics_interface_const_ptr_rclcpp_node
# ${PROJECT_NAME})
ament_add_gtest(test_node_global_args test/test_node_global_args.cpp)
if(TARGET test_node_global_args)
ament_target_dependencies(test_node_global_args
@@ -210,9 +287,17 @@ if(BUILD_TESTING)
)
target_link_libraries(test_node_global_args ${PROJECT_NAME})
endif()
ament_add_gtest(test_node_initial_parameters test/test_node_initial_parameters.cpp)
if(TARGET test_node_initial_parameters)
target_link_libraries(test_node_initial_parameters ${PROJECT_NAME})
ament_add_gtest(test_node_options test/test_node_options.cpp)
if(TARGET test_node_options)
ament_target_dependencies(test_node_options "rcl")
target_link_libraries(test_node_options ${PROJECT_NAME})
endif()
ament_add_gtest(test_parameter_client test/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})
endif()
ament_add_gtest(test_parameter_events_filter test/test_parameter_events_filter.cpp)
if(TARGET test_parameter_events_filter)
@@ -248,15 +333,6 @@ if(BUILD_TESTING)
)
target_link_libraries(test_publisher ${PROJECT_NAME})
endif()
ament_add_gtest(test_pub_sub_option_interface test/test_pub_sub_option_interface.cpp)
if(TARGET test_pub_sub_option_interface)
ament_target_dependencies(test_pub_sub_option_interface
test_msgs
)
target_link_libraries(test_pub_sub_option_interface
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_publisher_subscription_count_api test/test_publisher_subscription_count_api.cpp)
if(TARGET test_publisher_subscription_count_api)
ament_target_dependencies(test_publisher_subscription_count_api
@@ -318,7 +394,6 @@ if(BUILD_TESTING)
)
target_link_libraries(test_subscription_publisher_count_api ${PROJECT_NAME})
endif()
find_package(test_msgs REQUIRED)
ament_add_gtest(test_subscription_traits test/test_subscription_traits.cpp)
if(TARGET test_subscription_traits)
ament_target_dependencies(test_subscription_traits
@@ -334,38 +409,17 @@ if(BUILD_TESTING)
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
endif()
get_default_rmw_implementation(default_rmw)
find_package(${default_rmw} REQUIRED)
get_rmw_typesupport(typesupport_impls_cpp "${default_rmw}" LANGUAGE "cpp")
get_rmw_typesupport(typesupport_impls_c "${default_rmw}" LANGUAGE "c")
set(mock_msg_files
"test/mock_msgs/srv/Mock.srv")
rosidl_generate_interfaces(mock_msgs
${mock_msg_files}
LIBRARY_NAME "rclcpp"
SKIP_INSTALL)
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
if(WIN32)
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
endif()
ament_add_gtest(test_externally_defined_services test/test_externally_defined_services.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_externally_defined_services)
ament_target_dependencies(test_externally_defined_services
"rcl"
)
target_link_libraries(test_externally_defined_services ${PROJECT_NAME})
foreach(typesupport_impl_cpp ${typesupport_impls_cpp})
rosidl_target_interfaces(test_externally_defined_services
mock_msgs ${typesupport_impl_cpp})
endforeach()
foreach(typesupport_impl_c ${typesupport_impls_c})
rosidl_target_interfaces(test_externally_defined_services
mock_msgs ${typesupport_impl_c})
endforeach()
endif()
ament_add_gtest(test_externally_defined_services test/test_externally_defined_services.cpp)
ament_target_dependencies(test_externally_defined_services
"rcl"
"test_msgs"
)
target_link_libraries(test_externally_defined_services ${PROJECT_NAME})
ament_add_gtest(test_duration test/test_duration.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
@@ -397,6 +451,14 @@ if(BUILD_TESTING)
target_link_libraries(test_time ${PROJECT_NAME})
endif()
ament_add_gtest(test_timer test/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})
endif()
ament_add_gtest(test_time_source test/test_time_source.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_time_source)
@@ -429,16 +491,10 @@ if(BUILD_TESTING)
target_link_libraries(test_multi_threaded_executor ${PROJECT_NAME})
endif()
ament_add_gtest(test_local_parameters test/test_local_parameters.cpp)
if(TARGET test_local_parameters)
ament_target_dependencies(test_local_parameters
"rcl_interfaces"
"rmw"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp"
)
target_link_libraries(test_local_parameters ${PROJECT_NAME})
endif()
# Install test resources
install(
DIRECTORY test/resources
DESTINATION ${CMAKE_CURRENT_BINARY_DIR}/test)
endif()
ament_package()

View File

@@ -0,0 +1,56 @@
# Copyright 2019 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.
#
# Register a test which tries to compile a file and expects it to fail to build.
#
# This will create two targets, one by the given target name and a test target
# which has the same name prefixed with `test_`.
# For example, if target is `should_not_compile__use_const_argument` then there
# will be an executable target called `should_not_compile__use_const_argument`
# and a test target called `test_should_not_compile__use_const_argument`.
#
# :param target: the name of the target to be created
# :type target: string
# :param ARGN: the list of source files to be used to create the test executable
# :type ARGN: list of strings
#
macro(rclcpp_add_build_failure_test target)
if(${ARGC} EQUAL 0)
message(
FATAL_ERROR
"rclcpp_add_build_failure_test() requires a target name and "
"at least one source file")
endif()
add_executable(${target} ${ARGN})
set_target_properties(${target}
PROPERTIES
EXCLUDE_FROM_ALL TRUE
EXCLUDE_FROM_DEFAULT_BUILD TRUE)
add_test(
NAME test_${target}
COMMAND
${CMAKE_COMMAND}
--build .
--target ${target}
--config $<CONFIGURATION>
WORKING_DIRECTORY ${CMAKE_BINARY_DIR})
set_tests_properties(test_${target}
PROPERTIES
WILL_FAIL TRUE
LABELS "build_failure"
)
endmacro()

View File

@@ -65,7 +65,8 @@ void * retyped_reallocate(void * untyped_pointer, size_t size, void * untyped_al
// Convert a std::allocator_traits-formatted Allocator into an rcl allocator
template<
typename T, typename Alloc,
typename T,
typename Alloc,
typename std::enable_if<!std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
{
@@ -83,7 +84,8 @@ rcl_allocator_t get_rcl_allocator(Alloc & allocator)
// TODO(jacquelinekay) Workaround for an incomplete implementation of std::allocator<void>
template<
typename T, typename Alloc,
typename T,
typename Alloc,
typename std::enable_if<std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
{

View File

@@ -42,7 +42,6 @@ struct AnyExecutable
// Only one of the following pointers will be set.
rclcpp::SubscriptionBase::SharedPtr subscription;
rclcpp::SubscriptionBase::SharedPtr subscription_intra_process;
rclcpp::TimerBase::SharedPtr timer;
rclcpp::ServiceBase::SharedPtr service;
rclcpp::ClientBase::SharedPtr client;

View File

@@ -23,6 +23,7 @@
#include "rclcpp/function_traits.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/types.h"
#include "tracetools/tracetools.h"
namespace rclcpp
{
@@ -86,6 +87,7 @@ public:
std::shared_ptr<typename ServiceT::Request> request,
std::shared_ptr<typename ServiceT::Response> response)
{
TRACEPOINT(callback_start, (const void *)this, false);
if (shared_ptr_callback_ != nullptr) {
(void)request_header;
shared_ptr_callback_(request, response);
@@ -94,6 +96,7 @@ public:
} else {
throw std::runtime_error("unexpected request without any callback set");
}
TRACEPOINT(callback_end, (const void *)this);
}
};

View File

@@ -26,6 +26,8 @@
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/visibility_control.hpp"
#include "tracetools/tracetools.h"
#include "tracetools/utils.hpp"
namespace rclcpp
{
@@ -36,6 +38,7 @@ class AnySubscriptionCallback
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
using SharedPtrCallback = std::function<void (const std::shared_ptr<MessageT>)>;
@@ -154,7 +157,7 @@ public:
void dispatch(
std::shared_ptr<MessageT> message, const rmw_message_info_t & message_info)
{
(void)message_info;
TRACEPOINT(callback_start, (const void *)this, false);
if (shared_ptr_callback_) {
shared_ptr_callback_(message);
} else if (shared_ptr_with_info_callback_) {
@@ -174,31 +177,84 @@ public:
} else {
throw std::runtime_error("unexpected message without any callback set");
}
TRACEPOINT(callback_end, (const void *)this);
}
void dispatch_intra_process(
MessageUniquePtr & message, const rmw_message_info_t & message_info)
ConstMessageSharedPtr message, const rmw_message_info_t & message_info)
{
(void)message_info;
TRACEPOINT(callback_start, (const void *)this, true);
if (const_shared_ptr_callback_) {
const_shared_ptr_callback_(message);
} else if (const_shared_ptr_with_info_callback_) {
const_shared_ptr_with_info_callback_(message, message_info);
} else {
if (
unique_ptr_callback_ || unique_ptr_with_info_callback_ ||
shared_ptr_callback_ || shared_ptr_with_info_callback_)
{
throw std::runtime_error(
"unexpected dispatch_intra_process const shared "
"message call with no const shared_ptr callback");
} else {
throw std::runtime_error("unexpected message without any callback set");
}
}
TRACEPOINT(callback_end, (const void *)this);
}
void dispatch_intra_process(
MessageUniquePtr message, const rmw_message_info_t & message_info)
{
TRACEPOINT(callback_start, (const void *)this, true);
if (shared_ptr_callback_) {
typename std::shared_ptr<MessageT> shared_message = std::move(message);
shared_ptr_callback_(shared_message);
} else if (shared_ptr_with_info_callback_) {
typename std::shared_ptr<MessageT> shared_message = std::move(message);
shared_ptr_with_info_callback_(shared_message, message_info);
} else if (const_shared_ptr_callback_) {
typename std::shared_ptr<MessageT const> const_shared_message = std::move(message);
const_shared_ptr_callback_(const_shared_message);
} else if (const_shared_ptr_with_info_callback_) {
typename std::shared_ptr<MessageT const> const_shared_message = std::move(message);
const_shared_ptr_with_info_callback_(const_shared_message, message_info);
} else if (unique_ptr_callback_) {
unique_ptr_callback_(std::move(message));
} else if (unique_ptr_with_info_callback_) {
unique_ptr_with_info_callback_(std::move(message), message_info);
} else if (const_shared_ptr_callback_ || const_shared_ptr_with_info_callback_) {
throw std::runtime_error(
"unexpected dispatch_intra_process unique message call"
" with const shared_ptr callback");
} else {
throw std::runtime_error("unexpected message without any callback set");
}
TRACEPOINT(callback_end, (const void *)this);
}
bool use_take_shared_method() const
{
return const_shared_ptr_callback_ || const_shared_ptr_with_info_callback_;
}
void register_callback_for_tracing()
{
if (shared_ptr_callback_) {
TRACEPOINT(
rclcpp_callback_register,
(const void *)this,
get_symbol(shared_ptr_callback_));
} else if (shared_ptr_with_info_callback_) {
TRACEPOINT(
rclcpp_callback_register,
(const void *)this,
get_symbol(shared_ptr_with_info_callback_));
} else if (unique_ptr_callback_) {
TRACEPOINT(
rclcpp_callback_register,
(const void *)this,
get_symbol(unique_ptr_callback_));
} else if (unique_ptr_with_info_callback_) {
TRACEPOINT(
rclcpp_callback_register,
(const void *)this,
get_symbol(unique_ptr_with_info_callback_));
}
}
private:

View File

@@ -21,8 +21,9 @@
#include <vector>
#include "rclcpp/client.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/subscription_base.hpp"
#include "rclcpp/timer.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/waitable.hpp"
@@ -61,25 +62,40 @@ public:
RCLCPP_PUBLIC
explicit CallbackGroup(CallbackGroupType group_type);
RCLCPP_PUBLIC
const std::vector<rclcpp::SubscriptionBase::WeakPtr> &
get_subscription_ptrs() const;
template<typename Function>
rclcpp::SubscriptionBase::SharedPtr
find_subscription_ptrs_if(Function func) const
{
return _find_ptrs_if_impl<rclcpp::SubscriptionBase, Function>(func, subscription_ptrs_);
}
RCLCPP_PUBLIC
const std::vector<rclcpp::TimerBase::WeakPtr> &
get_timer_ptrs() const;
template<typename Function>
rclcpp::TimerBase::SharedPtr
find_timer_ptrs_if(Function func) const
{
return _find_ptrs_if_impl<rclcpp::TimerBase, Function>(func, timer_ptrs_);
}
RCLCPP_PUBLIC
const std::vector<rclcpp::ServiceBase::WeakPtr> &
get_service_ptrs() const;
template<typename Function>
rclcpp::ServiceBase::SharedPtr
find_service_ptrs_if(Function func) const
{
return _find_ptrs_if_impl<rclcpp::ServiceBase, Function>(func, service_ptrs_);
}
RCLCPP_PUBLIC
const std::vector<rclcpp::ClientBase::WeakPtr> &
get_client_ptrs() const;
template<typename Function>
rclcpp::ClientBase::SharedPtr
find_client_ptrs_if(Function func) const
{
return _find_ptrs_if_impl<rclcpp::ClientBase, Function>(func, client_ptrs_);
}
RCLCPP_PUBLIC
const std::vector<rclcpp::Waitable::WeakPtr> &
get_waitable_ptrs() const;
template<typename Function>
rclcpp::Waitable::SharedPtr
find_waitable_ptrs_if(Function func) const
{
return _find_ptrs_if_impl<rclcpp::Waitable, Function>(func, waitable_ptrs_);
}
RCLCPP_PUBLIC
std::atomic_bool &
@@ -92,6 +108,10 @@ public:
protected:
RCLCPP_DISABLE_COPY(CallbackGroup)
RCLCPP_PUBLIC
void
add_publisher(const rclcpp::PublisherBase::SharedPtr publisher_ptr);
RCLCPP_PUBLIC
void
add_subscription(const rclcpp::SubscriptionBase::SharedPtr subscription_ptr);
@@ -125,6 +145,21 @@ protected:
std::vector<rclcpp::ClientBase::WeakPtr> client_ptrs_;
std::vector<rclcpp::Waitable::WeakPtr> waitable_ptrs_;
std::atomic_bool can_be_taken_from_;
private:
template<typename TypeT, typename Function>
typename TypeT::SharedPtr _find_ptrs_if_impl(
Function func, const std::vector<typename TypeT::WeakPtr> & vect_ptrs) const
{
std::lock_guard<std::mutex> lock(mutex_);
for (auto & weak_ptr : vect_ptrs) {
auto ref_ptr = weak_ptr.lock();
if (ref_ptr && func(ref_ptr)) {
return ref_ptr;
}
}
return typename TypeT::SharedPtr();
}
};
} // namespace callback_group

View File

@@ -16,15 +16,16 @@
#define RCLCPP__CLOCK_HPP_
#include <functional>
#include <memory>
#include <thread>
#include <mutex>
#include <vector>
#include "rclcpp/macros.hpp"
#include "rclcpp/time.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcl/time.h"
#include "rcutils/time.h"
#include "rcutils/types/rcutils_ret.h"
namespace rclcpp
{
@@ -35,13 +36,17 @@ class JumpHandler
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(JumpHandler)
using pre_callback_t = std::function<void ()>;
using post_callback_t = std::function<void (const rcl_time_jump_t &)>;
JumpHandler(
std::function<void()> pre_callback,
std::function<void(const rcl_time_jump_t &)> post_callback,
pre_callback_t pre_callback,
post_callback_t post_callback,
const rcl_jump_threshold_t & threshold);
std::function<void()> pre_callback;
std::function<void(const rcl_time_jump_t &)> post_callback;
pre_callback_t pre_callback;
post_callback_t post_callback;
rcl_jump_threshold_t notice_threshold;
};
@@ -50,38 +55,80 @@ class Clock
public:
RCLCPP_SMART_PTR_DEFINITIONS(Clock)
/// Default c'tor
/**
* Initializes the clock instance with the given clock_type.
*
* \param clock_type type of the clock.
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
*/
RCLCPP_PUBLIC
explicit Clock(rcl_clock_type_t clock_type = RCL_SYSTEM_TIME);
RCLCPP_PUBLIC
~Clock();
/**
* Returns current time from the time source specified by clock_type.
*
* \return current time.
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
*/
RCLCPP_PUBLIC
Time
now();
/**
* Returns the clock of the type `RCL_ROS_TIME` is active.
*
* \return true if the clock is active
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw if
* the current clock does not have the clock_type `RCL_ROS_TIME`.
*/
RCLCPP_PUBLIC
bool
ros_time_is_active();
RCLCPP_PUBLIC
rcl_clock_t *
get_clock_handle();
get_clock_handle() noexcept;
RCLCPP_PUBLIC
rcl_clock_type_t
get_clock_type();
get_clock_type() const noexcept;
RCLCPP_PUBLIC
std::mutex &
get_clock_mutex() noexcept {
return clock_mutex_;
}
// Add a callback to invoke if the jump threshold is exceeded.
/**
* These callback functions must remain valid as long as the
* returned shared pointer is valid.
*
* Function will register callbacks to the callback queue. On time jump all
* callbacks will be executed whose threshold is greater then the time jump;
* The logic will first call selected pre_callbacks and then all selected
* post_callbacks.
*
* Function is only applicable if the clock_type is `RCL_ROS_TIME`
*
* \param pre_callback. Must be non-throwing
* \param post_callback. Must be non-throwing.
* \param threshold. Callbacks will be triggered if the time jump is greater
* then the threshold.
* \throws anything rclcpp::exceptions::throw_from_rcl_error can throw.
* \throws std::bad_alloc if the allocation of the JumpHandler fails.
* \warning the instance of the clock must remain valid as long as any created
* JumpHandler.
*/
RCLCPP_PUBLIC
JumpHandler::SharedPtr
create_jump_callback(
std::function<void()> pre_callback,
std::function<void(const rcl_time_jump_t &)> post_callback,
JumpHandler::pre_callback_t pre_callback,
JumpHandler::post_callback_t post_callback,
const rcl_jump_threshold_t & threshold);
private:
@@ -97,6 +144,7 @@ private:
rcl_clock_t rcl_clock_;
friend TimeSource; /// Allow TimeSource to access the rcl_clock_ datatype.
rcl_allocator_t allocator_;
std::mutex clock_mutex_;
};
} // namespace rclcpp

View File

@@ -0,0 +1,56 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__CREATE_CLIENT_HPP_
#define RCLCPP__CREATE_CLIENT_HPP_
#include <memory>
#include <string>
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rmw/rmw.h"
namespace rclcpp
{
/// Create a service client with a given type.
/// \internal
template<typename ServiceT>
typename rclcpp::Client<ServiceT>::SharedPtr
create_client(
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
std::shared_ptr<node_interfaces::NodeGraphInterface> node_graph,
std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
const std::string & service_name,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
rcl_client_options_t options = rcl_client_get_default_options();
options.qos = qos_profile;
auto cli = rclcpp::Client<ServiceT>::make_shared(
node_base.get(),
node_graph,
service_name,
options);
auto cli_base_ptr = std::dynamic_pointer_cast<rclcpp::ClientBase>(cli);
node_services->add_client(cli_base_ptr, group);
return cli;
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_CLIENT_HPP_

View File

@@ -18,31 +18,51 @@
#include <memory>
#include <string>
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/publisher_factory.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/qos.hpp"
#include "rmw/qos_profiles.h"
namespace rclcpp
{
template<typename MessageT, typename AllocatorT, typename PublisherT>
/// Create and return a publisher of the given MessageT type.
/**
* The NodeT type only needs to have a method called get_node_topics_interface()
* which returns a shared_ptr to a NodeTopicsInterface.
*/
template<
typename MessageT,
typename AllocatorT = std::allocator<void>,
typename PublisherT = rclcpp::Publisher<MessageT, AllocatorT>,
typename NodeT>
std::shared_ptr<PublisherT>
create_publisher(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
NodeT & node,
const std::string & topic_name,
const rmw_qos_profile_t & qos_profile,
bool use_intra_process_comms,
std::shared_ptr<AllocatorT> allocator)
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (
rclcpp::PublisherOptionsWithAllocator<AllocatorT>()
)
)
{
auto publisher_options = rcl_publisher_get_default_options();
publisher_options.qos = qos_profile;
// Extract the NodeTopicsInterface from the NodeT.
using rclcpp::node_interfaces::get_node_topics_interface;
auto node_topics = get_node_topics_interface(node);
// Create the publisher.
auto pub = node_topics->create_publisher(
topic_name,
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(allocator),
publisher_options,
use_intra_process_comms);
node_topics->add_publisher(pub);
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(options),
qos
);
// Add the publisher to the node topics interface.
node_topics->add_publisher(pub, options.callback_group);
return std::dynamic_pointer_cast<PublisherT>(pub);
}

View File

@@ -19,47 +19,60 @@
#include <string>
#include <utility>
#include "rclcpp/node_interfaces/get_node_topics_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/subscription_factory.hpp"
#include "rclcpp/subscription_options.hpp"
#include "rclcpp/qos.hpp"
#include "rmw/qos_profiles.h"
namespace rclcpp
{
/// Create and return a subscription of the given MessageT type.
/**
* The NodeT type only needs to have a method called get_node_topics_interface()
* which returns a shared_ptr to a NodeTopicsInterface, or be a
* NodeTopicsInterface pointer itself.
*/
template<
typename MessageT,
typename CallbackT,
typename AllocatorT,
typename CallbackMessageT,
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>>
typename AllocatorT = std::allocator<void>,
typename CallbackMessageT =
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT,
AllocatorT
>,
typename NodeT>
typename std::shared_ptr<SubscriptionT>
create_subscription(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
NodeT && node,
const std::string & topic_name,
const rclcpp::QoS & qos,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
bool use_intra_process_comms,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT, AllocatorT>::SharedPtr
msg_mem_strat,
typename std::shared_ptr<AllocatorT> allocator)
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
),
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat = (
MessageMemoryStrategyT::create_default()
)
)
{
auto subscription_options = rcl_subscription_get_default_options();
subscription_options.qos = qos_profile;
subscription_options.ignore_local_publications = ignore_local_publications;
using rclcpp::node_interfaces::get_node_topics_interface;
auto node_topics = get_node_topics_interface(std::forward<NodeT>(node));
auto factory = rclcpp::create_subscription_factory
<MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT>(
std::forward<CallbackT>(callback), msg_mem_strat, allocator);
auto factory = rclcpp::create_subscription_factory<MessageT>(
std::forward<CallbackT>(callback),
options,
msg_mem_strat
);
auto sub = node_topics->create_subscription(topic_name, factory, qos);
node_topics->add_subscription(sub, options.callback_group);
auto sub = node_topics->create_subscription(
topic_name,
factory,
subscription_options,
use_intra_process_comms);
node_topics->add_subscription(sub, group);
return std::dynamic_pointer_cast<SubscriptionT>(sub);
}

View File

@@ -0,0 +1,73 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__CREATE_TIMER_HPP_
#define RCLCPP__CREATE_TIMER_HPP_
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/duration.hpp"
#include "rclcpp/node_interfaces/get_node_base_interface.hpp"
#include "rclcpp/node_interfaces/get_node_timers_interface.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
namespace rclcpp
{
/// Create a timer with a given clock
/// \internal
template<typename CallbackT>
typename rclcpp::TimerBase::SharedPtr
create_timer(
node_interfaces::NodeBaseInterface * node_base,
node_interfaces::NodeTimersInterface * node_timers,
rclcpp::Clock::SharedPtr clock,
rclcpp::Duration period,
CallbackT && callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr)
{
auto timer = rclcpp::GenericTimer<CallbackT>::make_shared(
clock,
period.to_chrono<std::chrono::nanoseconds>(),
std::forward<CallbackT>(callback),
node_base->get_context());
node_timers->add_timer(timer, group);
return timer;
}
/// Create a timer with a given clock
template<typename NodeT, typename CallbackT>
typename rclcpp::TimerBase::SharedPtr
create_timer(
NodeT node,
rclcpp::Clock::SharedPtr clock,
rclcpp::Duration period,
CallbackT && callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr)
{
return create_timer(
rclcpp::node_interfaces::get_node_base_interface(node),
rclcpp::node_interfaces::get_node_timers_interface(node),
clock,
period,
std::forward<CallbackT>(callback),
group);
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_TIMER_HPP_

View File

@@ -0,0 +1,3 @@
Notice that headers in this folder should only provide symbols in the rclcpp::detail namespace.
Also that these headers are not considered part of the public API and are subject to change without notice.

View File

@@ -0,0 +1,54 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DETAIL__RESOLVE_INTRA_PROCESS_BUFFER_TYPE_HPP_
#define RCLCPP__DETAIL__RESOLVE_INTRA_PROCESS_BUFFER_TYPE_HPP_
#include <stdexcept>
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/intra_process_buffer_type.hpp"
namespace rclcpp
{
namespace detail
{
/// Return the buffer type, resolving the "CallbackDefault" type to an actual type if needed.
template<typename CallbackMessageT, typename AllocatorT>
rclcpp::IntraProcessBufferType
resolve_intra_process_buffer_type(
const rclcpp::IntraProcessBufferType buffer_type,
const rclcpp::AnySubscriptionCallback<CallbackMessageT, AllocatorT> & any_subscription_callback)
{
rclcpp::IntraProcessBufferType resolved_buffer_type = buffer_type;
// If the user has not specified a type for the intra-process buffer, use the callback's type.
if (resolved_buffer_type == IntraProcessBufferType::CallbackDefault) {
if (any_subscription_callback.use_take_shared_method()) {
resolved_buffer_type = IntraProcessBufferType::SharedPtr;
} else {
resolved_buffer_type = IntraProcessBufferType::UniquePtr;
}
}
return resolved_buffer_type;
}
} // namespace detail
} // namespace rclcpp
#endif // RCLCPP__DETAIL__RESOLVE_INTRA_PROCESS_BUFFER_TYPE_HPP_

View File

@@ -0,0 +1,56 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DETAIL__RESOLVE_USE_INTRA_PROCESS_HPP_
#define RCLCPP__DETAIL__RESOLVE_USE_INTRA_PROCESS_HPP_
#include <stdexcept>
#include "rclcpp/intra_process_setting.hpp"
namespace rclcpp
{
namespace detail
{
/// Return whether or not intra process is enabled, resolving "NodeDefault" if needed.
template<typename OptionsT, typename NodeBaseT>
bool
resolve_use_intra_process(const OptionsT & options, const NodeBaseT & node_base)
{
bool use_intra_process;
switch (options.use_intra_process_comm) {
case IntraProcessSetting::Enable:
use_intra_process = true;
break;
case IntraProcessSetting::Disable:
use_intra_process = false;
break;
case IntraProcessSetting::NodeDefault:
use_intra_process = node_base.get_use_intra_process_default();
break;
default:
throw std::runtime_error("Unrecognized IntraProcessSetting value");
break;
}
return use_intra_process;
}
} // namespace detail
} // namespace rclcpp
#endif // RCLCPP__DETAIL__RESOLVE_USE_INTRA_PROCESS_HPP_

View File

@@ -0,0 +1,51 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DETAIL__RMW_IMPLEMENTATION_SPECIFIC_PAYLOAD_HPP_
#define RCLCPP__DETAIL__RMW_IMPLEMENTATION_SPECIFIC_PAYLOAD_HPP_
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace detail
{
/// Mechanism for passing rmw implementation specific settings through the ROS interfaces.
class RCLCPP_PUBLIC RMWImplementationSpecificPayload
{
public:
virtual
~RMWImplementationSpecificPayload() = default;
/// Return false if this class has not been customized, otherwise true.
/**
* It does this based on the value of the rmw implementation identifier that
* this class reports, and so it is important for a specialization of this
* class to override the get_rmw_implementation_identifier() method to return
* something other than nullptr.
*/
bool
has_been_customized() const;
/// Derrived classes should override this and return the identifier of its rmw implementation.
virtual
const char *
get_implementation_identifier() const;
};
} // namespace detail
} // namespace rclcpp
#endif // RCLCPP__DETAIL__RMW_IMPLEMENTATION_SPECIFIC_PAYLOAD_HPP_

View File

@@ -0,0 +1,52 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DETAIL__RMW_IMPLEMENTATION_SPECIFIC_PUBLISHER_PAYLOAD_HPP_
#define RCLCPP__DETAIL__RMW_IMPLEMENTATION_SPECIFIC_PUBLISHER_PAYLOAD_HPP_
#include "rcl/publisher.h"
#include "rclcpp/detail/rmw_implementation_specific_payload.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace detail
{
class RCLCPP_PUBLIC RMWImplementationSpecificPublisherPayload
: public RMWImplementationSpecificPayload
{
public:
~RMWImplementationSpecificPublisherPayload() override = default;
/// Opportunity for a derived class to inject information into the rcl options.
/**
* This is called after the rcl_publisher_options_t has been prepared by
* rclcpp, but before rcl_publisher_init() is called.
*
* The passed option is the rmw_publisher_options field of the
* rcl_publisher_options_t that will be passed to rcl_publisher_init().
*
* By default the options are unmodified.
*/
virtual
void
modify_rmw_publisher_options(rmw_publisher_options_t & rmw_publisher_options) const;
};
} // namespace detail
} // namespace rclcpp
#endif // RCLCPP__DETAIL__RMW_IMPLEMENTATION_SPECIFIC_PUBLISHER_PAYLOAD_HPP_

View File

@@ -0,0 +1,53 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DETAIL__RMW_IMPLEMENTATION_SPECIFIC_SUBSCRIPTION_PAYLOAD_HPP_
#define RCLCPP__DETAIL__RMW_IMPLEMENTATION_SPECIFIC_SUBSCRIPTION_PAYLOAD_HPP_
#include "rcl/subscription.h"
#include "rclcpp/detail/rmw_implementation_specific_payload.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace detail
{
/// Subscription payload that may be rmw implementation specific.
class RCLCPP_PUBLIC RMWImplementationSpecificSubscriptionPayload
: public RMWImplementationSpecificPayload
{
public:
~RMWImplementationSpecificSubscriptionPayload() override = default;
/// Opportunity for a derived class to inject information into the rcl options.
/**
* This is called after the rcl_subscription_options_t has been prepared by
* rclcpp, but before rcl_subscription_init() is called.
*
* The passed option is the rmw_subscription_options field of the
* rcl_subscription_options_t that will be passed to rcl_subscription_init().
*
* By default the options are unmodified.
*/
virtual
void
modify_rmw_subscription_options(rmw_subscription_options_t & rmw_subscription_options) const;
};
} // namespace detail
} // namespace rclcpp
#endif // RCLCPP__DETAIL__RMW_IMPLEMENTATION_SPECIFIC_SUBSCRIPTION_PAYLOAD_HPP_

View File

@@ -23,91 +23,93 @@
namespace rclcpp
{
class Duration
class RCLCPP_PUBLIC Duration
{
public:
RCLCPP_PUBLIC
Duration(int32_t seconds, uint32_t nanoseconds);
RCLCPP_PUBLIC
explicit Duration(
rcl_duration_value_t nanoseconds);
// This constructor matches any numeric value - ints or floats
explicit Duration(rcl_duration_value_t nanoseconds);
RCLCPP_PUBLIC
explicit Duration(
std::chrono::nanoseconds nanoseconds);
explicit Duration(std::chrono::nanoseconds nanoseconds);
RCLCPP_PUBLIC
Duration(
const builtin_interfaces::msg::Duration & duration_msg);
// This constructor matches any std::chrono value other than nanoseconds
// intentionally not using explicit to create a conversion constructor
template<class Rep, class Period>
// cppcheck-suppress noExplicitConstructor
Duration(const std::chrono::duration<Rep, Period> & duration) // NOLINT(runtime/explicit)
: Duration(std::chrono::duration_cast<std::chrono::nanoseconds>(duration))
{}
// cppcheck-suppress noExplicitConstructor
Duration(const builtin_interfaces::msg::Duration & duration_msg); // NOLINT(runtime/explicit)
RCLCPP_PUBLIC
explicit Duration(const rcl_duration_t & duration);
RCLCPP_PUBLIC
Duration(const Duration & rhs);
RCLCPP_PUBLIC
virtual ~Duration();
virtual ~Duration() = default;
RCLCPP_PUBLIC
operator builtin_interfaces::msg::Duration() const;
RCLCPP_PUBLIC
// cppcheck-suppress operatorEq // this is a false positive from cppcheck
Duration &
operator=(const Duration & rhs);
RCLCPP_PUBLIC
Duration &
operator=(const builtin_interfaces::msg::Duration & Duration_msg);
RCLCPP_PUBLIC
bool
operator==(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
bool
operator<(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
bool
operator<=(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
bool
operator>=(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
bool
operator>(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
Duration
operator+(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
Duration
operator-(const rclcpp::Duration & rhs) const;
RCLCPP_PUBLIC
static Duration
static
Duration
max();
RCLCPP_PUBLIC
Duration
operator*(double scale) const;
RCLCPP_PUBLIC
rcl_duration_value_t
nanoseconds() const;
/// \return the duration in seconds as a floating point number.
/// \warning Depending on sizeof(double) there could be significant precision loss.
/// When an exact time is required use nanoseconds() instead.
RCLCPP_PUBLIC
double
seconds() const;
// Create a duration object from a floating point number representing seconds
static Duration
from_seconds(double seconds);
template<class DurationT>
DurationT
to_chrono() const
{
return std::chrono::duration_cast<DurationT>(std::chrono::nanoseconds(this->nanoseconds()));
}
rmw_time_t
to_rmw_time() const;
private:
rcl_duration_t rcl_duration_;
};

View File

@@ -17,11 +17,14 @@
#include <stdexcept>
#include <string>
#include <vector>
#include "rcl/error_handling.h"
#include "rcl/types.h"
#include "rclcpp/visibility_control.hpp"
#include "rcpputils/join.hpp"
namespace rclcpp
{
namespace exceptions
@@ -110,13 +113,15 @@ public:
* \throws std::runtime_error if the rcl_get_error_state returns 0
* \throws RCLErrorBase some child class exception based on ret
*/
/* *INDENT-OFF* */ // Uncrustify cannot yet understand [[noreturn]] properly
RCLCPP_PUBLIC
void
throw_from_rcl_error(
throw_from_rcl_error [[noreturn]] (
rcl_ret_t ret,
const std::string & prefix = "",
const rcl_error_state_t * error_state = nullptr,
void (* reset_error)() = rcl_reset_error);
/* *INDENT-ON* */
class RCLErrorBase
{
@@ -165,6 +170,31 @@ public:
RCLInvalidArgument(const RCLErrorBase & base_exc, const std::string & prefix);
};
/// Created when the ret is RCL_RET_INVALID_ROS_ARGS.
class RCLInvalidROSArgsError : public RCLErrorBase, public std::runtime_error
{
public:
RCLCPP_PUBLIC
RCLInvalidROSArgsError(
rcl_ret_t ret, const rcl_error_state_t * error_state, const std::string & prefix);
RCLCPP_PUBLIC
RCLInvalidROSArgsError(const RCLErrorBase & base_exc, const std::string & prefix);
};
/// Thrown when unparsed ROS specific arguments are found.
class UnknownROSArgsError : public std::runtime_error
{
public:
explicit UnknownROSArgsError(std::vector<std::string> && unknown_ros_args_in)
: std::runtime_error(
"found unknown ROS arguments: '" + rcpputils::join(unknown_ros_args_in, "', '") + "'"),
unknown_ros_args(unknown_ros_args_in)
{
}
const std::vector<std::string> unknown_ros_args;
};
/// Thrown when an invalid rclcpp::Event object or SharedPtr is encountered.
class InvalidEventError : public std::runtime_error
{
@@ -185,14 +215,42 @@ public:
class InvalidParametersException : public std::runtime_error
{
public:
// Inherit constructors from runtime_error;
// Inherit constructors from runtime_error.
using std::runtime_error::runtime_error;
};
/// Throwing if passed parameter value is invalid.
/// Thrown if passed parameter value is invalid.
class InvalidParameterValueException : public std::runtime_error
{
// Inherit constructors from runtime_error;
// Inherit constructors from runtime_error.
using std::runtime_error::runtime_error;
};
/// Thrown if parameter is already declared.
class ParameterAlreadyDeclaredException : public std::runtime_error
{
// Inherit constructors from runtime_error.
using std::runtime_error::runtime_error;
};
/// Thrown if parameter is not declared, e.g. either set or get was called without first declaring.
class ParameterNotDeclaredException : public std::runtime_error
{
// Inherit constructors from runtime_error.
using std::runtime_error::runtime_error;
};
/// Thrown if parameter is immutable and therefore cannot be undeclared.
class ParameterImmutableException : public std::runtime_error
{
// Inherit constructors from runtime_error.
using std::runtime_error::runtime_error;
};
/// Thrown if parameter is modified while in a set callback.
class ParameterModifiedInCallbackException : public std::runtime_error
{
// Inherit constructors from runtime_error.
using std::runtime_error::runtime_error;
};

View File

@@ -22,6 +22,7 @@
#include <iostream>
#include <list>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
@@ -47,6 +48,7 @@ namespace executor
/// Return codes to be used with spin_until_future_complete.
/**
* SUCCESS: The future is complete and can be accessed with "get" without blocking.
* This does not indicate that the operation succeeded; "get" may still throw an exception.
* INTERRUPTED: The future is not complete, spinning was interrupted by Ctrl-C or another error.
* TIMEOUT: Spinning timed out.
*/
@@ -210,8 +212,8 @@ public:
/// Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
/**
* \param[in] future The future to wait on. If SUCCESS, the future is safe to access after this
* function.
* \param[in] future The future to wait on. If this function returns SUCCESS, the future can be
* accessed without blocking (though it may still throw an exception).
* \param[in] timeout Optional timeout parameter, which gets passed to Executor::spin_node_once.
* `-1` is block forever, `0` is non-blocking.
* If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
@@ -303,11 +305,6 @@ protected:
execute_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription);
RCLCPP_PUBLIC
static void
execute_intra_process_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription);
RCLCPP_PUBLIC
static void
execute_timer(rclcpp::TimerBase::SharedPtr timer);
@@ -332,10 +329,6 @@ protected:
rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_timer(rclcpp::TimerBase::SharedPtr timer);
RCLCPP_PUBLIC
void
get_next_timer(AnyExecutable & any_exec);
RCLCPP_PUBLIC
bool
get_next_ready_executable(AnyExecutable & any_executable);
@@ -355,6 +348,9 @@ protected:
/// Wait set for managing entities that the rmw layer waits on.
rcl_wait_set_t wait_set_ = rcl_get_zero_initialized_wait_set();
// Mutex to protect the subsequent memory_strategy_.
std::mutex memory_strategy_mutex_;
/// The memory strategy: an interface for handling user-defined memory allocation strategies.
memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;
@@ -364,7 +360,8 @@ protected:
private:
RCLCPP_DISABLE_COPY(Executor)
std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
std::list<const rcl_guard_condition_t *> guard_conditions_;
};
} // namespace executor

View File

@@ -15,6 +15,7 @@
#ifndef RCLCPP__EXECUTORS__MULTI_THREADED_EXECUTOR_HPP_
#define RCLCPP__EXECUTORS__MULTI_THREADED_EXECUTOR_HPP_
#include <chrono>
#include <memory>
#include <mutex>
#include <set>
@@ -53,7 +54,8 @@ public:
MultiThreadedExecutor(
const executor::ExecutorArgs & args = executor::ExecutorArgs(),
size_t number_of_threads = 0,
bool yield_before_execute = false);
bool yield_before_execute = false,
std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
RCLCPP_PUBLIC
virtual ~MultiThreadedExecutor();
@@ -77,6 +79,7 @@ private:
std::mutex wait_mutex_;
size_t number_of_threads_;
bool yield_before_execute_;
std::chrono::nanoseconds next_exec_timeout_;
std::set<TimerBase::SharedPtr> scheduled_timers_;
};

View File

@@ -0,0 +1,4 @@
Notice that headers in this folder should only provide symbols in the rclcpp::experimental namespace.
Also notice that these headers are not considered part of the public API as they have not yet been stabilized.
And therefore they are subject to change without notice.

View File

@@ -0,0 +1,42 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__BUFFERS__BUFFER_IMPLEMENTATION_BASE_HPP_
#define RCLCPP__EXPERIMENTAL__BUFFERS__BUFFER_IMPLEMENTATION_BASE_HPP_
namespace rclcpp
{
namespace experimental
{
namespace buffers
{
template<typename BufferT>
class BufferImplementationBase
{
public:
virtual ~BufferImplementationBase() {}
virtual BufferT dequeue() = 0;
virtual void enqueue(BufferT request) = 0;
virtual void clear() = 0;
virtual bool has_data() const = 0;
};
} // namespace buffers
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__BUFFERS__BUFFER_IMPLEMENTATION_BASE_HPP_

View File

@@ -0,0 +1,241 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__BUFFERS__INTRA_PROCESS_BUFFER_HPP_
#define RCLCPP__EXPERIMENTAL__BUFFERS__INTRA_PROCESS_BUFFER_HPP_
#include <memory>
#include <type_traits>
#include <utility>
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/experimental/buffers/buffer_implementation_base.hpp"
#include "rclcpp/macros.hpp"
namespace rclcpp
{
namespace experimental
{
namespace buffers
{
class IntraProcessBufferBase
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(IntraProcessBufferBase)
virtual ~IntraProcessBufferBase() {}
virtual void clear() = 0;
virtual bool has_data() const = 0;
virtual bool use_take_shared_method() const = 0;
};
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename MessageDeleter = std::default_delete<MessageT>>
class IntraProcessBuffer : public IntraProcessBufferBase
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(IntraProcessBuffer)
virtual ~IntraProcessBuffer() {}
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
using MessageSharedPtr = std::shared_ptr<const MessageT>;
virtual void add_shared(MessageSharedPtr msg) = 0;
virtual void add_unique(MessageUniquePtr msg) = 0;
virtual MessageSharedPtr consume_shared() = 0;
virtual MessageUniquePtr consume_unique() = 0;
};
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename MessageDeleter = std::default_delete<MessageT>,
typename BufferT = std::unique_ptr<MessageT>>
class TypedIntraProcessBuffer : public IntraProcessBuffer<MessageT, Alloc, MessageDeleter>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(TypedIntraProcessBuffer)
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
using MessageSharedPtr = std::shared_ptr<const MessageT>;
explicit
TypedIntraProcessBuffer(
std::unique_ptr<BufferImplementationBase<BufferT>> buffer_impl,
std::shared_ptr<Alloc> allocator = nullptr)
{
bool valid_type = (std::is_same<BufferT, MessageSharedPtr>::value ||
std::is_same<BufferT, MessageUniquePtr>::value);
if (!valid_type) {
throw std::runtime_error("Creating TypedIntraProcessBuffer with not valid BufferT");
}
buffer_ = std::move(buffer_impl);
if (!allocator) {
message_allocator_ = std::make_shared<MessageAlloc>();
} else {
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
}
}
virtual ~TypedIntraProcessBuffer() {}
void add_shared(MessageSharedPtr msg) override
{
add_shared_impl<BufferT>(std::move(msg));
}
void add_unique(MessageUniquePtr msg) override
{
buffer_->enqueue(std::move(msg));
}
MessageSharedPtr consume_shared() override
{
return consume_shared_impl<BufferT>();
}
MessageUniquePtr consume_unique() override
{
return consume_unique_impl<BufferT>();
}
bool has_data() const override
{
return buffer_->has_data();
}
void clear() override
{
buffer_->clear();
}
bool use_take_shared_method() const override
{
return std::is_same<BufferT, MessageSharedPtr>::value;
}
private:
std::unique_ptr<BufferImplementationBase<BufferT>> buffer_;
std::shared_ptr<MessageAlloc> message_allocator_;
// MessageSharedPtr to MessageSharedPtr
template<typename DestinationT>
typename std::enable_if<
std::is_same<DestinationT, MessageSharedPtr>::value
>::type
add_shared_impl(MessageSharedPtr shared_msg)
{
buffer_->enqueue(std::move(shared_msg));
}
// MessageSharedPtr to MessageUniquePtr
template<typename DestinationT>
typename std::enable_if<
std::is_same<DestinationT, MessageUniquePtr>::value
>::type
add_shared_impl(MessageSharedPtr shared_msg)
{
// This should not happen: here a copy is unconditionally made, while the intra-process manager
// can decide whether a copy is needed depending on the number and the type of buffers
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);
}
buffer_->enqueue(std::move(unique_msg));
}
// MessageSharedPtr to MessageSharedPtr
template<typename OriginT>
typename std::enable_if<
std::is_same<OriginT, MessageSharedPtr>::value,
MessageSharedPtr
>::type
consume_shared_impl()
{
return buffer_->dequeue();
}
// MessageUniquePtr to MessageSharedPtr
template<typename OriginT>
typename std::enable_if<
(std::is_same<OriginT, MessageUniquePtr>::value),
MessageSharedPtr
>::type
consume_shared_impl()
{
// automatic cast from unique ptr to shared ptr
return buffer_->dequeue();
}
// MessageSharedPtr to MessageUniquePtr
template<typename OriginT>
typename std::enable_if<
(std::is_same<OriginT, MessageSharedPtr>::value),
MessageUniquePtr
>::type
consume_unique_impl()
{
MessageSharedPtr buffer_msg = buffer_->dequeue();
MessageUniquePtr unique_msg;
MessageDeleter * deleter = std::get_deleter<MessageDeleter, const MessageT>(buffer_msg);
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
MessageAllocTraits::construct(*message_allocator_.get(), ptr, *buffer_msg);
if (deleter) {
unique_msg = MessageUniquePtr(ptr, *deleter);
} else {
unique_msg = MessageUniquePtr(ptr);
}
return unique_msg;
}
// MessageUniquePtr to MessageUniquePtr
template<typename OriginT>
typename std::enable_if<
(std::is_same<OriginT, MessageUniquePtr>::value),
MessageUniquePtr
>::type
consume_unique_impl()
{
return buffer_->dequeue();
}
};
} // namespace buffers
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__BUFFERS__INTRA_PROCESS_BUFFER_HPP_

View File

@@ -0,0 +1,122 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__BUFFERS__RING_BUFFER_IMPLEMENTATION_HPP_
#define RCLCPP__EXPERIMENTAL__BUFFERS__RING_BUFFER_IMPLEMENTATION_HPP_
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <utility>
#include <vector>
#include "rclcpp/experimental/buffers/buffer_implementation_base.hpp"
#include "rclcpp/logger.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace experimental
{
namespace buffers
{
template<typename BufferT>
class RingBufferImplementation : public BufferImplementationBase<BufferT>
{
public:
explicit RingBufferImplementation(size_t capacity)
: capacity_(capacity),
ring_buffer_(capacity),
write_index_(capacity_ - 1),
read_index_(0),
size_(0)
{
if (capacity == 0) {
throw std::invalid_argument("capacity must be a positive, non-zero value");
}
}
virtual ~RingBufferImplementation() {}
void enqueue(BufferT request)
{
std::lock_guard<std::mutex> lock(mutex_);
write_index_ = next(write_index_);
ring_buffer_[write_index_] = std::move(request);
if (is_full()) {
read_index_ = next(read_index_);
} else {
size_++;
}
}
BufferT dequeue()
{
std::lock_guard<std::mutex> lock(mutex_);
if (!has_data()) {
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "Calling dequeue on empty intra-process buffer");
throw std::runtime_error("Calling dequeue on empty intra-process buffer");
}
auto request = std::move(ring_buffer_[read_index_]);
read_index_ = next(read_index_);
size_--;
return request;
}
inline size_t next(size_t val)
{
return (val + 1) % capacity_;
}
inline bool has_data() const
{
return size_ != 0;
}
inline bool is_full()
{
return size_ == capacity_;
}
void clear() {}
private:
size_t capacity_;
std::vector<BufferT> ring_buffer_;
size_t write_index_;
size_t read_index_;
size_t size_;
std::mutex mutex_;
};
} // namespace buffers
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__BUFFERS__RING_BUFFER_IMPLEMENTATION_HPP_

View File

@@ -0,0 +1,99 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__CREATE_INTRA_PROCESS_BUFFER_HPP_
#define RCLCPP__EXPERIMENTAL__CREATE_INTRA_PROCESS_BUFFER_HPP_
#include <memory>
#include <type_traits>
#include <utility>
#include "rcl/subscription.h"
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
#include "rclcpp/experimental/buffers/ring_buffer_implementation.hpp"
#include "rclcpp/intra_process_buffer_type.hpp"
namespace rclcpp
{
namespace experimental
{
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
typename rclcpp::experimental::buffers::IntraProcessBuffer<MessageT, Alloc, Deleter>::UniquePtr
create_intra_process_buffer(
IntraProcessBufferType buffer_type,
rmw_qos_profile_t qos,
std::shared_ptr<Alloc> allocator)
{
using MessageSharedPtr = std::shared_ptr<const MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
size_t buffer_size = qos.depth;
using rclcpp::experimental::buffers::IntraProcessBuffer;
typename IntraProcessBuffer<MessageT, Alloc, Deleter>::UniquePtr buffer;
switch (buffer_type) {
case IntraProcessBufferType::SharedPtr:
{
using BufferT = MessageSharedPtr;
auto buffer_implementation =
std::make_unique<rclcpp::experimental::buffers::RingBufferImplementation<BufferT>>(
buffer_size);
// Construct the intra_process_buffer
buffer =
std::make_unique<rclcpp::experimental::buffers::TypedIntraProcessBuffer<MessageT, Alloc,
Deleter, BufferT>>(
std::move(buffer_implementation),
allocator);
break;
}
case IntraProcessBufferType::UniquePtr:
{
using BufferT = MessageUniquePtr;
auto buffer_implementation =
std::make_unique<rclcpp::experimental::buffers::RingBufferImplementation<BufferT>>(
buffer_size);
// Construct the intra_process_buffer
buffer =
std::make_unique<rclcpp::experimental::buffers::TypedIntraProcessBuffer<MessageT, Alloc,
Deleter, BufferT>>(
std::move(buffer_implementation),
allocator);
break;
}
default:
{
throw std::runtime_error("Unrecognized IntraProcessBufferType value");
break;
}
}
return buffer;
}
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__CREATE_INTRA_PROCESS_BUFFER_HPP_

View File

@@ -0,0 +1,426 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__INTRA_PROCESS_MANAGER_HPP_
#define RCLCPP__EXPERIMENTAL__INTRA_PROCESS_MANAGER_HPP_
#include <rmw/types.h>
#include <shared_mutex>
#include <algorithm>
#include <atomic>
#include <cstdint>
#include <exception>
#include <map>
#include <memory>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/experimental/subscription_intra_process.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/logger.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace experimental
{
/// This class performs intra process communication between nodes.
/**
* This class is used in the creation of publishers and subscriptions.
* A singleton instance of this class is owned by a rclcpp::Context and a
* rclcpp::Node can use an associated Context to get an instance of this class.
* Nodes which do not have a common Context will not exchange intra process
* messages because they do not share access to the same instance of this class.
*
* When a Node creates a subscription, it can also create a helper class,
* called SubscriptionIntraProcess, meant to receive intra process messages.
* It can be registered with this class.
* It is also allocated an id which is unique among all publishers
* and subscriptions in this process and that is associated to the subscription.
*
* When a Node creates a publisher, as with subscriptions, a helper class can
* be registered with this class.
* This is required in order to publish intra-process messages.
* It is also allocated an id which is unique among all publishers
* and subscriptions in this process and that is associated to the publisher.
*
* When a publisher or a subscription are registered, this class checks to see
* which other subscriptions or publishers it will communicate with,
* i.e. they have the same topic and compatible QoS.
*
* When the user publishes a message, if intra-process communication is enabled
* on the publisher, the message is given to this class.
* Using the publisher id, a list of recipients for the message is selected.
* For each subscription in the list, this class stores the message, whether
* sharing ownership or making a copy, in a buffer associated with the
* subscription helper class.
*
* The subscription helper class contains a buffer where published
* intra-process messages are stored until they are taken from the subscription.
* Depending on the data type stored in the buffer, the subscription helper
* class can request either shared or exclusive ownership on the message.
*
* Thus, when an intra-process message is published, this class knows how many
* intra-process subscriptions needs it and how many require ownership.
* This information allows this class to operate efficiently by performing the
* fewest number of copies of the message required.
*
* This class is neither CopyConstructable nor CopyAssignable.
*/
class IntraProcessManager
{
private:
RCLCPP_DISABLE_COPY(IntraProcessManager)
public:
RCLCPP_SMART_PTR_DEFINITIONS(IntraProcessManager)
RCLCPP_PUBLIC
IntraProcessManager();
RCLCPP_PUBLIC
virtual ~IntraProcessManager();
/// Register a subscription with the manager, returns subscriptions unique id.
/**
* This method stores the subscription intra process object, together with
* the information of its wrapped subscription (i.e. topic name and QoS).
*
* In addition this generates a unique intra process id for the subscription.
*
* \param subscription the SubscriptionIntraProcess to register.
* \return an unsigned 64-bit integer which is the subscription's unique id.
*/
RCLCPP_PUBLIC
uint64_t
add_subscription(rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr subscription);
/// Unregister a subscription using the subscription's unique id.
/**
* This method does not allocate memory.
*
* \param intra_process_subscription_id id of the subscription to remove.
*/
RCLCPP_PUBLIC
void
remove_subscription(uint64_t intra_process_subscription_id);
/// Register a publisher with the manager, returns the publisher unique id.
/**
* This method stores the publisher intra process object, together with
* the information of its wrapped publisher (i.e. topic name and QoS).
*
* In addition this generates a unique intra process id for the publisher.
*
* \param publisher publisher to be registered with the manager.
* \return an unsigned 64-bit integer which is the publisher's unique id.
*/
RCLCPP_PUBLIC
uint64_t
add_publisher(rclcpp::PublisherBase::SharedPtr publisher);
/// Unregister a publisher using the publisher's unique id.
/**
* This method does not allocate memory.
*
* \param intra_process_publisher_id id of the publisher to remove.
*/
RCLCPP_PUBLIC
void
remove_publisher(uint64_t intra_process_publisher_id);
/// Publishes an intra-process message, passed as a unique pointer.
/**
* This is one of the two methods for publishing intra-process.
*
* Using the intra-process publisher id, a list of recipients is obtained.
* This list is split in half, depending whether they require ownership or not.
*
* This particular method takes a unique pointer as input.
* The pointer can be promoted to a shared pointer and passed to all the subscriptions
* that do not require ownership.
* In case of subscriptions requiring ownership, the message will be copied for all of
* them except the last one, when ownership can be transferred.
*
* This method can save an additional copy compared to the shared pointer one.
*
* This method can throw an exception if the publisher id is not found or
* if the publisher shared_ptr given to add_publisher has gone out of scope.
*
* This method does allocate memory.
*
* \param intra_process_publisher_id the id of the publisher of this message.
* \param message the message that is being stored.
*/
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
void
do_intra_process_publish(
uint64_t intra_process_publisher_id,
std::unique_ptr<MessageT, Deleter> message,
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
{
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAllocatorT = typename MessageAllocTraits::allocator_type;
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
auto publisher_it = pub_to_subs_.find(intra_process_publisher_id);
if (publisher_it == pub_to_subs_.end()) {
// Publisher is either invalid or no longer exists.
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Calling do_intra_process_publish for invalid or no longer existing publisher id");
return;
}
const auto & sub_ids = publisher_it->second;
if (sub_ids.take_ownership_subscriptions.empty()) {
// None of the buffers require ownership, so we promote the pointer
std::shared_ptr<MessageT> msg = std::move(message);
this->template add_shared_msg_to_buffers<MessageT>(msg, sub_ids.take_shared_subscriptions);
} else if (!sub_ids.take_ownership_subscriptions.empty() && // NOLINT
sub_ids.take_shared_subscriptions.size() <= 1)
{
// There is at maximum 1 buffer that does not require ownership.
// So we this case is equivalent to all the buffers requiring ownership
// Merge the two vector of ids into a unique one
std::vector<uint64_t> concatenated_vector(sub_ids.take_shared_subscriptions);
concatenated_vector.insert(
concatenated_vector.end(),
sub_ids.take_ownership_subscriptions.begin(),
sub_ids.take_ownership_subscriptions.end());
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter>(
std::move(message),
concatenated_vector,
allocator);
} else if (!sub_ids.take_ownership_subscriptions.empty() && // NOLINT
sub_ids.take_shared_subscriptions.size() > 1)
{
// Construct a new shared pointer from the message
// for the buffers that do not require ownership
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
this->template add_shared_msg_to_buffers<MessageT>(shared_msg,
sub_ids.take_shared_subscriptions);
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter>(
std::move(message),
sub_ids.take_ownership_subscriptions,
allocator);
}
}
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
std::shared_ptr<const MessageT>
do_intra_process_publish_and_return_shared(
uint64_t intra_process_publisher_id,
std::unique_ptr<MessageT, Deleter> message,
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
{
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAllocatorT = typename MessageAllocTraits::allocator_type;
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
auto publisher_it = pub_to_subs_.find(intra_process_publisher_id);
if (publisher_it == pub_to_subs_.end()) {
// Publisher is either invalid or no longer exists.
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Calling do_intra_process_publish for invalid or no longer existing publisher id");
return nullptr;
}
const auto & sub_ids = publisher_it->second;
if (sub_ids.take_ownership_subscriptions.empty()) {
// If there are no owning, just convert to shared.
std::shared_ptr<MessageT> shared_msg = std::move(message);
if (!sub_ids.take_shared_subscriptions.empty()) {
this->template add_shared_msg_to_buffers<MessageT>(shared_msg,
sub_ids.take_shared_subscriptions);
}
return shared_msg;
} else {
// Construct a new shared pointer from the message for the buffers that
// do not require ownership and to return.
auto shared_msg = std::allocate_shared<MessageT, MessageAllocatorT>(*allocator, *message);
if (!sub_ids.take_shared_subscriptions.empty()) {
this->template add_shared_msg_to_buffers<MessageT>(
shared_msg,
sub_ids.take_shared_subscriptions);
}
if (!sub_ids.take_ownership_subscriptions.empty()) {
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter>(
std::move(message),
sub_ids.take_ownership_subscriptions,
allocator);
}
return shared_msg;
}
}
/// Return true if the given rmw_gid_t matches any stored Publishers.
RCLCPP_PUBLIC
bool
matches_any_publishers(const rmw_gid_t * id) const;
/// Return the number of intraprocess subscriptions that are matched with a given publisher id.
RCLCPP_PUBLIC
size_t
get_subscription_count(uint64_t intra_process_publisher_id) const;
RCLCPP_PUBLIC
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr
get_subscription_intra_process(uint64_t intra_process_subscription_id);
private:
struct SubscriptionInfo
{
SubscriptionInfo() = default;
rclcpp::experimental::SubscriptionIntraProcessBase::SharedPtr subscription;
rmw_qos_profile_t qos;
const char * topic_name;
bool use_take_shared_method;
};
struct PublisherInfo
{
PublisherInfo() = default;
rclcpp::PublisherBase::WeakPtr publisher;
rmw_qos_profile_t qos;
const char * topic_name;
};
struct SplittedSubscriptions
{
std::vector<uint64_t> take_shared_subscriptions;
std::vector<uint64_t> take_ownership_subscriptions;
};
using SubscriptionMap =
std::unordered_map<uint64_t, SubscriptionInfo>;
using PublisherMap =
std::unordered_map<uint64_t, PublisherInfo>;
using PublisherToSubscriptionIdsMap =
std::unordered_map<uint64_t, SplittedSubscriptions>;
RCLCPP_PUBLIC
static
uint64_t
get_next_unique_id();
RCLCPP_PUBLIC
void
insert_sub_id_for_pub(uint64_t sub_id, uint64_t pub_id, bool use_take_shared_method);
RCLCPP_PUBLIC
bool
can_communicate(PublisherInfo pub_info, SubscriptionInfo sub_info) const;
template<typename MessageT>
void
add_shared_msg_to_buffers(
std::shared_ptr<const MessageT> message,
std::vector<uint64_t> subscription_ids)
{
for (auto id : subscription_ids) {
auto subscription_it = subscriptions_.find(id);
if (subscription_it == subscriptions_.end()) {
throw std::runtime_error("subscription has unexpectedly gone out of scope");
}
auto subscription_base = subscription_it->second.subscription;
auto subscription = std::static_pointer_cast<
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
>(subscription_base);
subscription->provide_intra_process_message(message);
}
}
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
void
add_owned_msg_to_buffers(
std::unique_ptr<MessageT, Deleter> message,
std::vector<uint64_t> subscription_ids,
std::shared_ptr<typename allocator::AllocRebind<MessageT, Alloc>::allocator_type> allocator)
{
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
for (auto it = subscription_ids.begin(); it != subscription_ids.end(); it++) {
auto subscription_it = subscriptions_.find(*it);
if (subscription_it == subscriptions_.end()) {
throw std::runtime_error("subscription has unexpectedly gone out of scope");
}
auto subscription_base = subscription_it->second.subscription;
auto subscription = std::static_pointer_cast<
rclcpp::experimental::SubscriptionIntraProcess<MessageT>
>(subscription_base);
if (std::next(it) == subscription_ids.end()) {
// If this is the last subscription, give up ownership
subscription->provide_intra_process_message(std::move(message));
} else {
// Copy the message since we have additional subscriptions to serve
MessageUniquePtr copy_message;
Deleter deleter = message.get_deleter();
auto ptr = MessageAllocTraits::allocate(*allocator.get(), 1);
MessageAllocTraits::construct(*allocator.get(), ptr, *message);
copy_message = MessageUniquePtr(ptr, deleter);
subscription->provide_intra_process_message(std::move(copy_message));
}
}
}
PublisherToSubscriptionIdsMap pub_to_subs_;
SubscriptionMap subscriptions_;
PublisherMap publishers_;
mutable std::shared_timed_mutex mutex_;
};
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__INTRA_PROCESS_MANAGER_HPP_

View File

@@ -0,0 +1,173 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
#include <rmw/rmw.h>
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include "rcl/error_handling.h"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/experimental/buffers/intra_process_buffer.hpp"
#include "rclcpp/experimental/create_intra_process_buffer.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/waitable.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
namespace experimental
{
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>,
typename CallbackMessageT = MessageT>
class SubscriptionIntraProcess : public SubscriptionIntraProcessBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using ConstMessageSharedPtr = std::shared_ptr<const MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
MessageT,
Alloc,
Deleter
>::UniquePtr;
SubscriptionIntraProcess(
AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
std::shared_ptr<Alloc> allocator,
rclcpp::Context::SharedPtr context,
const std::string & topic_name,
rmw_qos_profile_t qos_profile,
rclcpp::IntraProcessBufferType buffer_type)
: SubscriptionIntraProcessBase(topic_name, qos_profile),
any_callback_(callback)
{
if (!std::is_same<MessageT, CallbackMessageT>::value) {
throw std::runtime_error("SubscriptionIntraProcess wrong callback type");
}
// Create the intra-process buffer.
buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
buffer_type,
qos_profile,
allocator);
// Create the guard condition.
rcl_guard_condition_options_t guard_condition_options =
rcl_guard_condition_get_default_options();
gc_ = rcl_get_zero_initialized_guard_condition();
rcl_ret_t ret = rcl_guard_condition_init(
&gc_, context->get_rcl_context().get(), guard_condition_options);
if (RCL_RET_OK != ret) {
throw std::runtime_error("SubscriptionIntraProcess init error initializing guard condition");
}
TRACEPOINT(
rclcpp_subscription_callback_added,
(const void *)this,
(const void *)&any_callback_);
// The callback object gets copied, so if registration is done too early/before this point
// (e.g. in `AnySubscriptionCallback::set()`), its address won't match any address used later
// in subsequent tracepoints.
any_callback_.register_callback_for_tracing();
}
bool
is_ready(rcl_wait_set_t * wait_set)
{
(void)wait_set;
return buffer_->has_data();
}
void execute()
{
execute_impl<CallbackMessageT>();
}
void
provide_intra_process_message(ConstMessageSharedPtr message)
{
buffer_->add_shared(std::move(message));
trigger_guard_condition();
}
void
provide_intra_process_message(MessageUniquePtr message)
{
buffer_->add_unique(std::move(message));
trigger_guard_condition();
}
bool
use_take_shared_method() const
{
return buffer_->use_take_shared_method();
}
private:
void
trigger_guard_condition()
{
rcl_ret_t ret = rcl_trigger_guard_condition(&gc_);
(void)ret;
}
template<typename T>
typename std::enable_if<std::is_same<T, rcl_serialized_message_t>::value, void>::type
execute_impl()
{
throw std::runtime_error("Subscription intra-process can't handle serialized messages");
}
template<class T>
typename std::enable_if<!std::is_same<T, rcl_serialized_message_t>::value, void>::type
execute_impl()
{
rmw_message_info_t msg_info;
msg_info.from_intra_process = true;
if (any_callback_.use_take_shared_method()) {
ConstMessageSharedPtr msg = buffer_->consume_shared();
any_callback_.dispatch_intra_process(msg, msg_info);
} else {
MessageUniquePtr msg = buffer_->consume_unique();
any_callback_.dispatch_intra_process(std::move(msg), msg_info);
}
}
AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
BufferUniquePtr buffer_;
};
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_

View File

@@ -0,0 +1,88 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BASE_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BASE_HPP_
#include <rmw/rmw.h>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <utility>
#include "rcl/error_handling.h"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/waitable.hpp"
namespace rclcpp
{
namespace experimental
{
class SubscriptionIntraProcessBase : public rclcpp::Waitable
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(SubscriptionIntraProcessBase)
RCLCPP_PUBLIC
SubscriptionIntraProcessBase(const std::string & topic_name, rmw_qos_profile_t qos_profile)
: topic_name_(topic_name), qos_profile_(qos_profile)
{}
virtual ~SubscriptionIntraProcessBase() = default;
RCLCPP_PUBLIC
size_t
get_number_of_ready_guard_conditions() {return 1;}
RCLCPP_PUBLIC
bool
add_to_wait_set(rcl_wait_set_t * wait_set);
virtual bool
is_ready(rcl_wait_set_t * wait_set) = 0;
virtual void
execute() = 0;
virtual bool
use_take_shared_method() const = 0;
RCLCPP_PUBLIC
const char *
get_topic_name() const;
RCLCPP_PUBLIC
rmw_qos_profile_t
get_actual_qos() const;
protected:
std::recursive_mutex reentrant_mutex_;
rcl_guard_condition_t gc_;
private:
virtual void
trigger_guard_condition() = 0;
std::string topic_name_;
rmw_qos_profile_t qos_profile_;
};
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BASE_HPP_

View File

@@ -96,6 +96,24 @@ struct function_traits<
: function_traits<ReturnTypeT(Args ...)>
{};
// std::bind for object const methods
template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ... FArgs>
#if defined _LIBCPP_VERSION // libc++ (Clang)
struct function_traits<std::__bind<ReturnTypeT (ClassT::*)(Args ...) const, FArgs ...>>
#elif defined _GLIBCXX_RELEASE // glibc++ (GNU C++ >= 7.1)
struct function_traits<std::_Bind<ReturnTypeT(ClassT::*(FArgs ...))(Args ...) const>>
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
struct function_traits<std::_Bind<std::_Mem_fn<ReturnTypeT (ClassT::*)(Args ...) const>(FArgs ...)>>
#elif defined _MSC_VER // MS Visual Studio
struct function_traits<
std::_Binder<std::_Unforced, ReturnTypeT(__cdecl ClassT::*)(Args ...) const, FArgs ...>
>
#else
#error "Unsupported C++ compiler / standard library"
#endif
: function_traits<ReturnTypeT(Args ...)>
{};
// std::bind for free functions
template<typename ReturnTypeT, typename ... Args, typename ... FArgs>
#if defined _LIBCPP_VERSION // libc++ (Clang)

View File

@@ -0,0 +1,35 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__INTRA_PROCESS_BUFFER_TYPE_HPP_
#define RCLCPP__INTRA_PROCESS_BUFFER_TYPE_HPP_
namespace rclcpp
{
/// Used as argument in create_publisher and create_subscriber
/// when intra-process communication is enabled
enum class IntraProcessBufferType
{
/// Set the data type used in the intra-process buffer as std::shared_ptr<MessageT>
SharedPtr,
/// Set the data type used in the intra-process buffer as std::unique_ptr<MessageT>
UniquePtr,
/// Set the data type used in the intra-process buffer as the same used in the callback
CallbackDefault
};
} // namespace rclcpp
#endif // RCLCPP__INTRA_PROCESS_BUFFER_TYPE_HPP_

View File

@@ -1,366 +0,0 @@
// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__INTRA_PROCESS_MANAGER_HPP_
#define RCLCPP__INTRA_PROCESS_MANAGER_HPP_
#include <rmw/types.h>
#include <algorithm>
#include <atomic>
#include <cstdint>
#include <exception>
#include <map>
#include <memory>
#include <mutex>
#include <unordered_map>
#include <set>
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/intra_process_manager_impl.hpp"
#include "rclcpp/mapped_ring_buffer.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace intra_process_manager
{
/// This class facilitates intra process communication between nodes.
/**
* This class is used in the creation of publishers and subscriptions.
* A singleton instance of this class is owned by a rclcpp::Context and a
* rclcpp::Node can use an associated Context to get an instance of this class.
* Nodes which do not have a common Context will not exchange intra process
* messages because they will not share access to an instance of this class.
*
* When a Node creates a publisher or subscription, it will register them
* with this class.
* The node will also hook into the publisher's publish call
* in order to do intra process related work.
*
* When a publisher is created, it advertises on the topic the user provided,
* as well as a "shadowing" topic of type rcl_interfaces/IntraProcessMessage.
* For instance, if the user specified the topic '/namespace/chatter', then the
* corresponding intra process topic might be '/namespace/chatter/_intra'.
* The publisher is also allocated an id which is unique among all publishers
* and subscriptions in this process.
* Additionally, when registered with this class a ring buffer is created and
* owned by this class as a temporary place to hold messages destined for intra
* process subscriptions.
*
* When a subscription is created, it subscribes to the topic provided by the
* user as well as to the corresponding intra process topic.
* It is also gets a unique id from the singleton instance of this class which
* is unique among publishers and subscriptions.
*
* When the user publishes a message, the message is stored by calling
* store_intra_process_message on this class.
* The instance of that message is uniquely identified by a publisher id and a
* message sequence number.
* The publisher id, message sequence pair is unique with in the process.
* At that point a list of the id's of intra process subscriptions which have
* been registered with the singleton instance of this class are stored with
* the message instance so that delivery is only made to those subscriptions.
* Then an instance of rcl_interfaces/IntraProcessMessage is published to the
* intra process topic which is specific to the topic specified by the user.
*
* When an instance of rcl_interfaces/IntraProcessMessage is received by a
* subscription, then it is handled by calling take_intra_process_message
* on a singleton of this class.
* The subscription passes a publisher id, message sequence pair which
* uniquely identifies the message instance it was suppose to receive as well
* as the subscriptions unique id.
* If the message is still being held by this class and the subscription's id
* is in the list of intended subscriptions then the message is returned.
* If either of those predicates are not satisfied then the message is not
* returned and the subscription does not call the users callback.
*
* Since the publisher builds a list of destined subscriptions on publish, and
* other requests are ignored, this class knows how many times a message
* instance should be requested.
* The final time a message is requested, the ownership is passed out of this
* class and passed to the final subscription, effectively freeing space in
* this class's internal storage.
*
* Since a topic is being used to ferry notifications about new intra process
* messages between publishers and subscriptions, it is possible for that
* notification to be lost.
* It is also possible that a subscription which was available when publish was
* called will no longer exist once the notification gets posted.
* In both cases this might result in a message instance getting requested
* fewer times than expected.
* This is why the internal storage of this class is a ring buffer.
* That way if a message is orphaned it will eventually be dropped from storage
* when a new message instance is stored and will not result in a memory leak.
*
* However, since the storage system is finite, this also means that a message
* instance might get displaced by an incoming message instance before all
* interested parties have called take_intra_process_message.
* Because of this the size of the internal storage should be carefully
* considered.
*
* /TODO(wjwwood): update to include information about handling latching.
* /TODO(wjwwood): consider thread safety of the class.
*
* This class is neither CopyConstructable nor CopyAssignable.
*/
class IntraProcessManager
{
private:
RCLCPP_DISABLE_COPY(IntraProcessManager)
public:
RCLCPP_SMART_PTR_DEFINITIONS(IntraProcessManager)
RCLCPP_PUBLIC
explicit IntraProcessManager(
IntraProcessManagerImplBase::SharedPtr state = create_default_impl());
RCLCPP_PUBLIC
virtual ~IntraProcessManager();
/// Register a subscription with the manager, returns subscriptions unique id.
/**
* In addition to generating a unique intra process id for the subscription,
* this method also stores the topic name of the subscription.
*
* This method is normally called during the creation of a subscription,
* but after it creates the internal intra process rmw_subscription_t.
*
* This method will allocate memory.
*
* \param subscription the Subscription to register.
* \return an unsigned 64-bit integer which is the subscription's unique id.
*/
RCLCPP_PUBLIC
uint64_t
add_subscription(SubscriptionBase::SharedPtr subscription);
/// Unregister a subscription using the subscription's unique id.
/**
* This method does not allocate memory.
*
* \param intra_process_subscription_id id of the subscription to remove.
*/
RCLCPP_PUBLIC
void
remove_subscription(uint64_t intra_process_subscription_id);
/// Register a publisher with the manager, returns the publisher unique id.
/**
* In addition to generating and returning a unique id for the publisher,
* this method creates internal ring buffer storage for "in-flight" intra
* process messages which are stored when store_intra_process_message is
* called with this publisher's unique id.
*
* The buffer_size must be less than or equal to the max uint64_t value.
* If the buffer_size is 0 then a buffer size is calculated using the
* publisher's QoS settings.
* The default is to use the depth field of the publisher's QoS.
* TODO(wjwwood): Consider doing depth *= 1.2, round up, or similar.
* TODO(wjwwood): Consider what to do for keep all.
*
* This method is templated on the publisher's message type so that internal
* storage of the same type can be allocated.
*
* This method will allocate memory.
*
* \param publisher publisher to be registered with the manager.
* \param buffer_size if 0 (default) a size is calculated based on the QoS.
* \return an unsigned 64-bit integer which is the publisher's unique id.
*/
template<typename MessageT, typename Alloc>
uint64_t
add_publisher(
typename Publisher<MessageT, Alloc>::SharedPtr publisher,
size_t buffer_size = 0)
{
auto id = IntraProcessManager::get_next_unique_id();
size_t size = buffer_size > 0 ? buffer_size : publisher->get_queue_size();
auto mrb = mapped_ring_buffer::MappedRingBuffer<
MessageT,
typename Publisher<MessageT, Alloc>::MessageAlloc
>::make_shared(size, publisher->get_allocator());
impl_->add_publisher(id, publisher, mrb, size);
return id;
}
/// Unregister a publisher using the publisher's unique id.
/**
* This method does not allocate memory.
*
* \param intra_process_publisher_id id of the publisher to remove.
*/
RCLCPP_PUBLIC
void
remove_publisher(uint64_t intra_process_publisher_id);
/// Store a message in the manager, and return the message sequence number.
/**
* The given message is stored in internal storage using the given publisher
* id and the newly generated message sequence, which is also returned.
* The combination of publisher id and message sequence number can later
* be used with a subscription id to retrieve the message by calling
* take_intra_process_message.
* The number of times take_intra_process_message can be called with this
* unique pair of id's is determined by the number of subscriptions currently
* subscribed to the same topic and which share the same Context, i.e. once
* for each subscription which should receive the intra process message.
*
* The ownership of the incoming message is transfered to the internal
* storage in order to avoid copying the message data.
* Therefore, the message parameter will no longer contain the original
* message after calling this method.
* Instead it will either be a nullptr or it will contain the ownership of
* the message instance which was displaced.
* If the message parameter is not equal to nullptr after calling this method
* then a message was prematurely displaced, i.e. take_intra_process_message
* had not been called on it as many times as was expected.
*
* This method can throw an exception if the publisher id is not found or
* if the publisher shared_ptr given to add_publisher has gone out of scope.
*
* This method does allocate memory.
*
* \param intra_process_publisher_id the id of the publisher of this message.
* \param message the message that is being stored.
* \return the message sequence number.
*/
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
uint64_t
store_intra_process_message(
uint64_t intra_process_publisher_id,
std::unique_ptr<MessageT, Deleter> & message)
{
using MRBMessageAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<MessageT>;
using TypedMRB = typename mapped_ring_buffer::MappedRingBuffer<MessageT, MRBMessageAlloc>;
uint64_t message_seq = 0;
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = impl_->get_publisher_info_for_id(
intra_process_publisher_id, message_seq);
typename TypedMRB::SharedPtr typed_buffer = std::static_pointer_cast<TypedMRB>(buffer);
if (!typed_buffer) {
throw std::runtime_error("Typecast failed due to incorrect message type");
}
// Insert the message into the ring buffer using the message_seq to identify it.
bool did_replace = typed_buffer->push_and_replace(message_seq, message);
// TODO(wjwwood): do something when a message was displaced. log debug?
(void)did_replace; // Avoid unused variable warning.
impl_->store_intra_process_message(intra_process_publisher_id, message_seq);
// Return the message sequence which is sent to the subscription.
return message_seq;
}
/// Take an intra process message.
/**
* The intra_process_publisher_id and message_sequence_number parameters
* uniquely identify a message instance, which should be taken.
*
* The requesting_subscriptions_intra_process_id parameter is used to make
* sure the requesting subscription was intended to receive this message
* instance.
* This check is made because it could happen that the requester
* comes up after the publish event, so it still receives the notification of
* a new intra process message, but it wasn't registered with the manager at
* the time of publishing, causing it to take when it wasn't intended.
* This should be avioded unless latching-like behavior is involved.
*
* The message parameter is used to store the taken message.
* On the last expected call to this method, the ownership is transfered out
* of internal storage and into the message parameter.
* On all previous calls a copy of the internally stored message is made and
* the ownership of the copy is transfered to the message parameter.
* TODO(wjwwood): update this documentation when latching is supported.
*
* The message parameter can be set to nullptr if:
*
* - The publisher id is not found.
* - The message sequence is not found for the given publisher id.
* - The requesting subscription's id is not in the list of intended takers.
* - The requesting subscription's id has been used before with this message.
*
* This method may allocate memory to copy the stored message.
*
* \param intra_process_publisher_id the id of the message's publisher.
* \param message_sequence_number the sequence number of the message.
* \param requesting_subscriptions_intra_process_id the subscription's id.
* \param message the message typed unique_ptr used to return the message.
*/
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
void
take_intra_process_message(
uint64_t intra_process_publisher_id,
uint64_t message_sequence_number,
uint64_t requesting_subscriptions_intra_process_id,
std::unique_ptr<MessageT, Deleter> & message)
{
using MRBMessageAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<MessageT>;
using TypedMRB = mapped_ring_buffer::MappedRingBuffer<MessageT, MRBMessageAlloc>;
message = nullptr;
size_t target_subs_size = 0;
std::lock_guard<std::mutex> lock(take_mutex_);
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = impl_->take_intra_process_message(
intra_process_publisher_id,
message_sequence_number,
requesting_subscriptions_intra_process_id,
target_subs_size
);
typename TypedMRB::SharedPtr typed_buffer = std::static_pointer_cast<TypedMRB>(buffer);
if (!typed_buffer) {
return;
}
// Return a copy or the unique_ptr (ownership) depending on how many subscriptions are left.
if (target_subs_size) {
// There are more subscriptions to serve, return a copy.
typed_buffer->get_copy_at_key(message_sequence_number, message);
} else {
// This is the last one to be returned, transfer ownership.
typed_buffer->pop_at_key(message_sequence_number, message);
}
}
/// Return true if the given rmw_gid_t matches any stored Publishers.
RCLCPP_PUBLIC
bool
matches_any_publishers(const rmw_gid_t * id) const;
/// Return the number of intraprocess subscriptions to a topic, given the publisher id.
RCLCPP_PUBLIC
size_t
get_subscription_count(uint64_t intra_process_publisher_id) const;
private:
RCLCPP_PUBLIC
static uint64_t
get_next_unique_id();
IntraProcessManagerImplBase::SharedPtr impl_;
std::mutex take_mutex_;
};
} // namespace intra_process_manager
} // namespace rclcpp
#endif // RCLCPP__INTRA_PROCESS_MANAGER_HPP_

View File

@@ -1,358 +0,0 @@
// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
#define RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
#include <algorithm>
#include <array>
#include <atomic>
#include <cstring>
#include <functional>
#include <limits>
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <stdexcept>
#include <string>
#include <unordered_map>
#include <utility>
#include "rmw/validate_full_topic_name.h"
#include "rclcpp/macros.hpp"
#include "rclcpp/mapped_ring_buffer.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace intra_process_manager
{
class IntraProcessManagerImplBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerImplBase)
IntraProcessManagerImplBase() = default;
virtual ~IntraProcessManagerImplBase() = default;
virtual void
add_subscription(uint64_t id, SubscriptionBase::SharedPtr subscription) = 0;
virtual void
remove_subscription(uint64_t intra_process_subscription_id) = 0;
virtual void add_publisher(
uint64_t id,
PublisherBase::WeakPtr publisher,
mapped_ring_buffer::MappedRingBufferBase::SharedPtr mrb,
size_t size) = 0;
virtual void
remove_publisher(uint64_t intra_process_publisher_id) = 0;
virtual mapped_ring_buffer::MappedRingBufferBase::SharedPtr
get_publisher_info_for_id(
uint64_t intra_process_publisher_id,
uint64_t & message_seq) = 0;
virtual void
store_intra_process_message(uint64_t intra_process_publisher_id, uint64_t message_seq) = 0;
virtual mapped_ring_buffer::MappedRingBufferBase::SharedPtr
take_intra_process_message(
uint64_t intra_process_publisher_id,
uint64_t message_sequence_number,
uint64_t requesting_subscriptions_intra_process_id,
size_t & size) = 0;
virtual bool
matches_any_publishers(const rmw_gid_t * id) const = 0;
virtual size_t
get_subscription_count(uint64_t intra_process_publisher_id) const = 0;
private:
RCLCPP_DISABLE_COPY(IntraProcessManagerImplBase)
};
template<typename Allocator = std::allocator<void>>
class IntraProcessManagerImpl : public IntraProcessManagerImplBase
{
public:
IntraProcessManagerImpl() = default;
~IntraProcessManagerImpl() = default;
void
add_subscription(uint64_t id, SubscriptionBase::SharedPtr subscription)
{
subscriptions_[id] = subscription;
subscription_ids_by_topic_[fixed_size_string(subscription->get_topic_name())].insert(id);
}
void
remove_subscription(uint64_t intra_process_subscription_id)
{
subscriptions_.erase(intra_process_subscription_id);
for (auto & pair : subscription_ids_by_topic_) {
pair.second.erase(intra_process_subscription_id);
}
// Iterate over all publisher infos and all stored subscription id's and
// remove references to this subscription's id.
for (auto & publisher_pair : publishers_) {
for (auto & sub_pair : publisher_pair.second.target_subscriptions_by_message_sequence) {
sub_pair.second.erase(intra_process_subscription_id);
}
}
}
void add_publisher(
uint64_t id,
PublisherBase::WeakPtr publisher,
mapped_ring_buffer::MappedRingBufferBase::SharedPtr mrb,
size_t size)
{
publishers_[id].publisher = publisher;
// As long as the size of the ring buffer is less than the max sequence number, we're safe.
if (size > std::numeric_limits<uint64_t>::max()) {
throw std::invalid_argument("the calculated buffer size is too large");
}
publishers_[id].sequence_number.store(0);
publishers_[id].buffer = mrb;
publishers_[id].target_subscriptions_by_message_sequence.reserve(size);
}
void
remove_publisher(uint64_t intra_process_publisher_id)
{
publishers_.erase(intra_process_publisher_id);
}
// return message_seq and mrb
mapped_ring_buffer::MappedRingBufferBase::SharedPtr
get_publisher_info_for_id(
uint64_t intra_process_publisher_id,
uint64_t & message_seq)
{
std::lock_guard<std::mutex> lock(runtime_mutex_);
auto it = publishers_.find(intra_process_publisher_id);
if (it == publishers_.end()) {
throw std::runtime_error("get_publisher_info_for_id called with invalid publisher id");
}
PublisherInfo & info = it->second;
// Calculate the next message sequence number.
message_seq = info.sequence_number.fetch_add(1);
return info.buffer;
}
void
store_intra_process_message(uint64_t intra_process_publisher_id, uint64_t message_seq)
{
std::lock_guard<std::mutex> lock(runtime_mutex_);
auto it = publishers_.find(intra_process_publisher_id);
if (it == publishers_.end()) {
throw std::runtime_error("store_intra_process_message called with invalid publisher id");
}
PublisherInfo & info = it->second;
auto publisher = info.publisher.lock();
if (!publisher) {
throw std::runtime_error("publisher has unexpectedly gone out of scope");
}
// Figure out what subscriptions should receive the message.
auto & destined_subscriptions =
subscription_ids_by_topic_[fixed_size_string(publisher->get_topic_name())];
// Store the list for later comparison.
if (info.target_subscriptions_by_message_sequence.count(message_seq) == 0) {
info.target_subscriptions_by_message_sequence.emplace(
message_seq, AllocSet(std::less<uint64_t>(), uint64_allocator));
} else {
info.target_subscriptions_by_message_sequence[message_seq].clear();
}
std::copy(
destined_subscriptions.begin(), destined_subscriptions.end(),
// Memory allocation occurs in info.target_subscriptions_by_message_sequence[message_seq]
std::inserter(
info.target_subscriptions_by_message_sequence[message_seq],
// This ends up only being a hint to std::set, could also be .begin().
info.target_subscriptions_by_message_sequence[message_seq].end()
)
);
}
mapped_ring_buffer::MappedRingBufferBase::SharedPtr
take_intra_process_message(
uint64_t intra_process_publisher_id,
uint64_t message_sequence_number,
uint64_t requesting_subscriptions_intra_process_id,
size_t & size
)
{
std::lock_guard<std::mutex> lock(runtime_mutex_);
PublisherInfo * info;
{
auto it = publishers_.find(intra_process_publisher_id);
if (it == publishers_.end()) {
// Publisher is either invalid or no longer exists.
return 0;
}
info = &it->second;
}
// Figure out how many subscriptions are left.
AllocSet * target_subs;
{
auto it = info->target_subscriptions_by_message_sequence.find(message_sequence_number);
if (it == info->target_subscriptions_by_message_sequence.end()) {
// Message is no longer being stored by this publisher.
return 0;
}
target_subs = &it->second;
}
{
auto it = std::find(
target_subs->begin(), target_subs->end(),
requesting_subscriptions_intra_process_id);
if (it == target_subs->end()) {
// This publisher id/message seq pair was not intended for this subscription.
return 0;
}
target_subs->erase(it);
}
size = target_subs->size();
return info->buffer;
}
bool
matches_any_publishers(const rmw_gid_t * id) const
{
for (auto & publisher_pair : publishers_) {
auto publisher = publisher_pair.second.publisher.lock();
if (!publisher) {
continue;
}
if (*publisher.get() == id) {
return true;
}
}
return false;
}
size_t
get_subscription_count(uint64_t intra_process_publisher_id) const
{
auto publisher_it = publishers_.find(intra_process_publisher_id);
if (publisher_it == publishers_.end()) {
// Publisher is either invalid or no longer exists.
return 0;
}
auto publisher = publisher_it->second.publisher.lock();
if (!publisher) {
throw std::runtime_error("publisher has unexpectedly gone out of scope");
}
auto sub_map_it =
subscription_ids_by_topic_.find(fixed_size_string(publisher->get_topic_name()));
if (sub_map_it == subscription_ids_by_topic_.end()) {
// No intraprocess subscribers
return 0;
}
return sub_map_it->second.size();
}
private:
RCLCPP_DISABLE_COPY(IntraProcessManagerImpl)
using FixedSizeString = std::array<char, RMW_TOPIC_MAX_NAME_LENGTH + 1>;
FixedSizeString
fixed_size_string(const char * str) const
{
FixedSizeString ret;
size_t size = std::strlen(str) + 1;
if (size > ret.size()) {
throw std::runtime_error("failed to copy topic name");
}
std::memcpy(ret.data(), str, size);
return ret;
}
struct strcmp_wrapper
{
bool
operator()(const FixedSizeString lhs, const FixedSizeString rhs) const
{
return std::strcmp(lhs.data(), rhs.data()) < 0;
}
};
template<typename T>
using RebindAlloc = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
RebindAlloc<uint64_t> uint64_allocator;
using AllocSet = std::set<uint64_t, std::less<uint64_t>, RebindAlloc<uint64_t>>;
using SubscriptionMap = std::unordered_map<
uint64_t, SubscriptionBase::WeakPtr,
std::hash<uint64_t>, std::equal_to<uint64_t>,
RebindAlloc<std::pair<const uint64_t, SubscriptionBase::WeakPtr>>>;
using IDTopicMap = std::map<
FixedSizeString,
AllocSet,
strcmp_wrapper,
RebindAlloc<std::pair<const FixedSizeString, AllocSet>>>;
SubscriptionMap subscriptions_;
IDTopicMap subscription_ids_by_topic_;
struct PublisherInfo
{
RCLCPP_DISABLE_COPY(PublisherInfo)
PublisherInfo() = default;
PublisherBase::WeakPtr publisher;
std::atomic<uint64_t> sequence_number;
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer;
using TargetSubscriptionsMap = std::unordered_map<
uint64_t, AllocSet,
std::hash<uint64_t>, std::equal_to<uint64_t>,
RebindAlloc<std::pair<const uint64_t, AllocSet>>>;
TargetSubscriptionsMap target_subscriptions_by_message_sequence;
};
using PublisherMap = std::unordered_map<
uint64_t, PublisherInfo,
std::hash<uint64_t>, std::equal_to<uint64_t>,
RebindAlloc<std::pair<const uint64_t, PublisherInfo>>>;
PublisherMap publishers_;
std::mutex runtime_mutex_;
};
RCLCPP_PUBLIC
IntraProcessManagerImplBase::SharedPtr
create_default_impl();
} // namespace intra_process_manager
} // namespace rclcpp
#endif // RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_

View File

@@ -0,0 +1,207 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__LOANED_MESSAGE_HPP_
#define RCLCPP__LOANED_MESSAGE_HPP_
#include <memory>
#include <utility>
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rcl/allocator.h"
#include "rcl/publisher.h"
namespace rclcpp
{
template<typename MessageT, typename AllocatorT = std::allocator<void>>
class LoanedMessage
{
using MessageAllocatorTraits = rclcpp::allocator::AllocRebind<MessageT, AllocatorT>;
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
public:
/// Constructor of the LoanedMessage class.
/**
* The constructor of this class allocates memory for a given message type
* and associates this with a given publisher.
*
* Given the publisher instance, a case differentiation is being performaned
* which decides whether the underlying middleware is able to allocate the appropriate
* memory for this message type or not.
* In the case that the middleware can not loan messages, the passed in allocator instance
* is being used to allocate the message within the scope of this class.
* Otherwise, the allocator is being ignored and the allocation is solely performaned
* in the underlying middleware with its appropriate allocation strategy.
* The need for this arises as the user code can be written explicitly targeting a middleware
* capable of loaning messages.
* However, this user code is ought to be usable even when dynamically linked against
* a middleware which doesn't support message loaning in which case the allocator will be used.
*
* \param pub rclcpp::Publisher instance to which the memory belongs
* \param allocator Allocator instance in case middleware can not allocate messages
*/
LoanedMessage(
const rclcpp::PublisherBase & pub,
std::allocator<MessageT> allocator)
: pub_(pub),
message_(nullptr),
message_allocator_(std::move(allocator))
{
if (pub_.can_loan_messages()) {
void * message_ptr = nullptr;
auto ret = rcl_borrow_loaned_message(
pub_.get_publisher_handle(),
rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
&message_ptr);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
message_ = static_cast<MessageT *>(message_ptr);
} else {
RCLCPP_INFO_ONCE(
rclcpp::get_logger("rclcpp"),
"Currently used middleware can't loan messages. Local allocator will be used.");
message_ = message_allocator_.allocate(1);
new (message_) MessageT();
}
}
/// Constructor of the LoanedMessage class.
/**
* The constructor of this class allocates memory for a given message type
* and associates this with a given publisher.
*
* Given the publisher instance, a case differentiation is being performaned
* which decides whether the underlying middleware is able to allocate the appropriate
* memory for this message type or not.
* In the case that the middleware can not loan messages, the passed in allocator instance
* is being used to allocate the message within the scope of this class.
* Otherwise, the allocator is being ignored and the allocation is solely performaned
* in the underlying middleware with its appropriate allocation strategy.
* The need for this arises as the user code can be written explicitly targeting a middleware
* capable of loaning messages.
* However, this user code is ought to be usable even when dynamically linked against
* a middleware which doesn't support message loaning in which case the allocator will be used.
*
* \param pub rclcpp::Publisher instance to which the memory belongs
* \param allocator Allocator instance in case middleware can not allocate messages
*/
LoanedMessage(
const rclcpp::PublisherBase * pub,
std::shared_ptr<std::allocator<MessageT>> allocator)
: LoanedMessage(*pub, *allocator)
{}
/// Move semantic for RVO
LoanedMessage(LoanedMessage<MessageT> && other)
: pub_(std::move(other.pub_)),
message_(std::move(other.message_)),
message_allocator_(std::move(other.message_allocator_))
{}
/// Destructor of the LoanedMessage class.
/**
* The destructor has the explicit task to return the allocated memory for its message
* instance.
* If the message was previously allocated via the middleware, the message is getting
* returned to the middleware to cleanly destroy the allocation.
* In the case that the local allocator instance was used, the same instance is then
* being used to destroy the allocated memory.
*
* The contract here is that the memory for this message is valid as long as this instance
* of the LoanedMessage class is alive.
*/
virtual ~LoanedMessage()
{
auto error_logger = rclcpp::get_logger("LoanedMessage");
if (message_ == nullptr) {
return;
}
if (pub_.can_loan_messages()) {
// return allocated memory to the middleware
auto ret =
rcl_return_loaned_message_from_publisher(pub_.get_publisher_handle(), message_);
if (ret != RCL_RET_OK) {
RCLCPP_ERROR(
error_logger, "rcl_deallocate_loaned_message failed: %s", rcl_get_error_string().str);
rcl_reset_error();
}
} else {
// call destructor before deallocating
message_->~MessageT();
message_allocator_.deallocate(message_, 1);
}
message_ = nullptr;
}
/// Validate if the message was correctly allocated.
/**
* The allocated memory might not be always consistent and valid.
* Reasons why this could fail is that an allocation step was failing,
* e.g. just like malloc could fail or a maximum amount of previously allocated
* messages is exceeded in which case the loaned messages have to be returned
* to the middleware prior to be able to allocate a new one.
*/
bool is_valid() const
{
return message_ != nullptr;
}
/// Access the ROS message instance.
/**
* A call to `get()` will return a mutable reference to the underlying ROS message instance.
* This allows a user to modify the content of the message prior to publishing it.
*
* If this reference is copied, the memory for this copy is no longer managed
* by the LoanedMessage instance and has to be cleanup individually.
*/
MessageT & get() const
{
return *message_;
}
/// Release ownership of the ROS message instance.
/**
* A call to `release()` will unmanage the memory for the ROS message.
* That means that the destructor of this class will not free the memory on scope exit.
*
* \return Raw pointer to the message instance.
*/
MessageT * release()
{
auto msg = message_;
message_ = nullptr;
return msg;
}
protected:
const rclcpp::PublisherBase & pub_;
MessageT * message_;
MessageAllocator message_allocator_;
/// Deleted copy constructor to preserve memory integrity.
LoanedMessage(const LoanedMessage<MessageT> & other) = delete;
};
} // namespace rclcpp
#endif // RCLCPP__LOANED_MESSAGE_HPP_

View File

@@ -66,6 +66,7 @@
#define RCLCPP_SMART_PTR_ALIASES_ONLY(...) \
__RCLCPP_SHARED_PTR_ALIAS(__VA_ARGS__) \
__RCLCPP_WEAK_PTR_ALIAS(__VA_ARGS__) \
__RCLCPP_UNIQUE_PTR_ALIAS(__VA_ARGS__) \
__RCLCPP_MAKE_SHARED_DEFINITION(__VA_ARGS__)
#define __RCLCPP_SHARED_PTR_ALIAS(...) \

View File

@@ -1,248 +0,0 @@
// Copyright 2015 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__MAPPED_RING_BUFFER_HPP_
#define RCLCPP__MAPPED_RING_BUFFER_HPP_
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <mutex>
#include <utility>
#include <vector>
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace mapped_ring_buffer
{
class RCLCPP_PUBLIC MappedRingBufferBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBufferBase)
};
/// Ring buffer container of unique_ptr's of T, which can be accessed by a key.
/**
* T must be a CopyConstructable and CopyAssignable.
* This class can be used in a container by using the base class MappedRingBufferBase.
* This class must have a positive, non-zero size.
* This class cannot be resized nor can it reserve additional space after construction.
* This class is not CopyConstructable nor CopyAssignable.
*
* The key's are not guaranteed to be unique because push_and_replace does not
* check for colliding keys.
* It is up to the user to only use unique keys.
* A side effect of this is that when get_copy_at_key or pop_at_key are called,
* they return the first encountered instance of the key.
* But iteration does not begin with the ring buffer's head, and therefore
* there is no guarantee on which value is returned if a key is used multiple
* times.
*/
template<typename T, typename Alloc = std::allocator<void>>
class MappedRingBuffer : public MappedRingBufferBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBuffer<T, Alloc>)
using ElemAllocTraits = allocator::AllocRebind<T, Alloc>;
using ElemAlloc = typename ElemAllocTraits::allocator_type;
using ElemDeleter = allocator::Deleter<ElemAlloc, T>;
using ElemUniquePtr = std::unique_ptr<T, ElemDeleter>;
/// Constructor.
/**
* The constructor will allocate memory while reserving space.
*
* \param size size of the ring buffer; must be positive and non-zero.
* \param allocator optional custom allocator
*/
explicit MappedRingBuffer(size_t size, std::shared_ptr<Alloc> allocator = nullptr)
: elements_(size), head_(0)
{
if (size == 0) {
throw std::invalid_argument("size must be a positive, non-zero value");
}
if (!allocator) {
allocator_ = std::make_shared<ElemAlloc>();
} else {
allocator_ = std::make_shared<ElemAlloc>(*allocator.get());
}
}
virtual ~MappedRingBuffer() {}
/// Return a copy of the value stored in the ring buffer at the given key.
/**
* The key is matched if an element in the ring buffer has a matching key.
* This method will allocate in order to return a copy.
*
* The key is not guaranteed to be unique, see the class docs for more.
*
* The contents of value before the method is called are discarded.
*
* \param key the key associated with the stored value
* \param value if the key is found, the value is stored in this parameter
*/
void
get_copy_at_key(uint64_t key, ElemUniquePtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
auto it = get_iterator_of_key(key);
value = nullptr;
if (it != elements_.end() && it->in_use) {
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->value);
value = ElemUniquePtr(ptr);
}
}
/// Return ownership of the value stored in the ring buffer, leaving a copy.
/**
* The key is matched if an element in the ring bufer has a matching key.
* This method will allocate in order to store a copy.
*
* The key is not guaranteed to be unique, see the class docs for more.
*
* The ownership of the currently stored object is returned, but a copy is
* made and stored in its place.
* This means that multiple calls to this function for a particular element
* will result in returning the copied and stored object not the original.
* This also means that later calls to pop_at_key will not return the
* originally stored object, since it was returned by the first call to this
* method.
*
* The contents of value before the method is called are discarded.
*
* \param key the key associated with the stored value
* \param value if the key is found, the value is stored in this parameter
*/
void
get_ownership_at_key(uint64_t key, ElemUniquePtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
auto it = get_iterator_of_key(key);
value = nullptr;
if (it != elements_.end() && it->in_use) {
// Make a copy.
auto ptr = ElemAllocTraits::allocate(*allocator_.get(), 1);
ElemAllocTraits::construct(*allocator_.get(), ptr, *it->value);
auto copy = ElemUniquePtr(ptr);
// Return the original.
value.swap(it->value);
// Store the copy.
it->value.swap(copy);
}
}
/// Return ownership of the value stored in the ring buffer at the given key.
/**
* The key is matched if an element in the ring buffer has a matching key.
*
* The key is not guaranteed to be unique, see the class docs for more.
*
* The contents of value before the method is called are discarded.
*
* \param key the key associated with the stored value
* \param value if the key is found, the value is stored in this parameter
*/
void
pop_at_key(uint64_t key, ElemUniquePtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
auto it = get_iterator_of_key(key);
value = nullptr;
if (it != elements_.end() && it->in_use) {
value.swap(it->value);
it->in_use = false;
}
}
/// Insert a key-value pair, displacing an existing pair if necessary.
/**
* The key's uniqueness is not checked on insertion.
* It is up to the user to ensure the key is unique.
* This method should not allocate memory.
*
* After insertion, if a pair was replaced, then value will contain ownership
* of that displaced value. Otherwise it will be a nullptr.
*
* \param key the key associated with the value to be stored
* \param value the value to store, and optionally the value displaced
*/
bool
push_and_replace(uint64_t key, ElemUniquePtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
bool did_replace = elements_[head_].in_use;
elements_[head_].key = key;
elements_[head_].value.swap(value);
elements_[head_].in_use = true;
head_ = (head_ + 1) % elements_.size();
return did_replace;
}
bool
push_and_replace(uint64_t key, ElemUniquePtr && value)
{
ElemUniquePtr temp = std::move(value);
return push_and_replace(key, temp);
}
/// Return true if the key is found in the ring buffer, otherwise false.
bool
has_key(uint64_t key)
{
std::lock_guard<std::mutex> lock(data_mutex_);
return elements_.end() != get_iterator_of_key(key);
}
private:
RCLCPP_DISABLE_COPY(MappedRingBuffer<T, Alloc>)
struct element
{
uint64_t key;
ElemUniquePtr value;
bool in_use;
};
using VectorAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<element>;
typename std::vector<element, VectorAlloc>::iterator
get_iterator_of_key(uint64_t key)
{
auto it = std::find_if(
elements_.begin(), elements_.end(),
[key](element & e) -> bool {
return e.key == key && e.in_use;
});
return it;
}
std::vector<element, VectorAlloc> elements_;
size_t head_;
std::shared_ptr<ElemAlloc> allocator_;
std::mutex data_mutex_;
};
} // namespace mapped_ring_buffer
} // namespace rclcpp
#endif // RCLCPP__MAPPED_RING_BUFFER_HPP_

View File

@@ -15,8 +15,8 @@
#ifndef RCLCPP__MEMORY_STRATEGY_HPP_
#define RCLCPP__MEMORY_STRATEGY_HPP_
#include <list>
#include <memory>
#include <vector>
#include "rcl/allocator.h"
#include "rcl/wait.h"
@@ -42,15 +42,16 @@ class RCLCPP_PUBLIC MemoryStrategy
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy)
using WeakNodeVector = std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>;
using WeakNodeList = std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>;
virtual ~MemoryStrategy() = default;
virtual bool collect_entities(const WeakNodeVector & weak_nodes) = 0;
virtual bool collect_entities(const WeakNodeList & weak_nodes) = 0;
virtual size_t number_of_ready_subscriptions() const = 0;
virtual size_t number_of_ready_services() const = 0;
virtual size_t number_of_ready_clients() const = 0;
virtual size_t number_of_ready_events() const = 0;
virtual size_t number_of_ready_timers() const = 0;
virtual size_t number_of_guard_conditions() const = 0;
virtual size_t number_of_waitables() const = 0;
@@ -66,22 +67,27 @@ public:
virtual void
get_next_subscription(
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes) = 0;
const WeakNodeList & weak_nodes) = 0;
virtual void
get_next_service(
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes) = 0;
const WeakNodeList & weak_nodes) = 0;
virtual void
get_next_client(
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes) = 0;
const WeakNodeList & weak_nodes) = 0;
virtual void
get_next_timer(
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeList & weak_nodes) = 0;
virtual void
get_next_waitable(
rclcpp::executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes) = 0;
const WeakNodeList & weak_nodes) = 0;
virtual rcl_allocator_t
get_allocator() = 0;
@@ -89,42 +95,52 @@ public:
static rclcpp::SubscriptionBase::SharedPtr
get_subscription_by_handle(
std::shared_ptr<const rcl_subscription_t> subscriber_handle,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::ServiceBase::SharedPtr
get_service_by_handle(
std::shared_ptr<const rcl_service_t> service_handle,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::ClientBase::SharedPtr
get_client_by_handle(
std::shared_ptr<const rcl_client_t> client_handle,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::TimerBase::SharedPtr
get_timer_by_handle(
std::shared_ptr<const rcl_timer_t> timer_handle,
const WeakNodeList & weak_nodes);
static rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(
rclcpp::callback_group::CallbackGroup::SharedPtr group,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_service(
rclcpp::ServiceBase::SharedPtr service,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_client(
rclcpp::ClientBase::SharedPtr client,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_timer(
rclcpp::TimerBase::SharedPtr timer,
const WeakNodeList & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_waitable(
rclcpp::Waitable::SharedPtr waitable,
const WeakNodeVector & weak_nodes);
const WeakNodeList & weak_nodes);
};
} // namespace memory_strategy

View File

@@ -95,16 +95,17 @@ public:
rclcpp::exceptions::throw_from_rcl_error(ret);
}
auto serialized_msg = std::shared_ptr<rcl_serialized_message_t>(msg,
[](rmw_serialized_message_t * msg) {
auto ret = rmw_serialized_message_fini(msg);
delete msg;
if (ret != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to destroy serialized message: %s", rcl_get_error_string().str);
}
});
auto serialized_msg = std::shared_ptr<rcl_serialized_message_t>(
msg,
[](rmw_serialized_message_t * msg) {
auto fini_ret = rmw_serialized_message_fini(msg);
delete msg;
if (fini_ret != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"failed to destroy serialized message: %s", rcl_get_error_string().str);
}
});
return serialized_msg;
}

View File

@@ -23,6 +23,7 @@
#include <mutex>
#include <string>
#include <tuple>
#include <utility>
#include <vector>
#include "rcl/error_handling.h"
@@ -41,7 +42,6 @@
#include "rclcpp/logger.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_clock_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
@@ -52,9 +52,11 @@
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/node_interfaces/node_waitables_interface.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/subscription_options.hpp"
@@ -143,59 +145,43 @@ public:
/// Create and return a Publisher.
/**
* The rclcpp::QoS has several convenient constructors, including a
* conversion constructor for size_t, which mimics older API's that
* allows just a string and size_t to create a publisher.
*
* For example, all of these cases will work:
*
* ```cpp
* pub = node->create_publisher<MsgT>("chatter", 10); // implicitly KeepLast
* pub = node->create_publisher<MsgT>("chatter", QoS(10)); // implicitly KeepLast
* pub = node->create_publisher<MsgT>("chatter", QoS(KeepLast(10)));
* pub = node->create_publisher<MsgT>("chatter", QoS(KeepAll()));
* pub = node->create_publisher<MsgT>("chatter", QoS(1).best_effort().volatile());
* {
* rclcpp::QoS custom_qos(KeepLast(10), rmw_qos_profile_sensor_data);
* pub = node->create_publisher<MsgT>("chatter", custom_qos);
* }
* ```
*
* The publisher options may optionally be passed as the third argument for
* any of the above cases.
*
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_history_depth The depth of the publisher message queue.
* \param[in] qos The Quality of Service settings for the publisher.
* \param[in] options Additional options for the created Publisher.
* \return Shared pointer to the created publisher.
*/
template<
typename MessageT,
typename AllocatorT = std::allocator<void>,
typename PublisherT = ::rclcpp::Publisher<MessageT, AllocatorT>>
typename PublisherT = rclcpp::Publisher<MessageT, AllocatorT>>
std::shared_ptr<PublisherT>
create_publisher(
const std::string & topic_name,
size_t qos_history_depth,
const PublisherOptionsWithAllocator<AllocatorT> &
options = PublisherOptionsWithAllocator<AllocatorT>());
/// Create and return a Publisher.
/**
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_history_depth The depth of the publisher message queue.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created publisher.
*/
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename PublisherT = ::rclcpp::Publisher<MessageT, Alloc>>
// cppcheck-suppress syntaxError // bug in cppcheck 1.82 for [[deprecated]] on templated function
[[deprecated(
"use the create_publisher(const std::string &, size_t, const PublisherOptions<Alloc> & = "
"PublisherOptions<Alloc>()) signature instead")]]
std::shared_ptr<PublisherT>
create_publisher(
const std::string & topic_name,
size_t qos_history_depth,
std::shared_ptr<Alloc> allocator,
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
/// Create and return a Publisher.
/**
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_profile The quality of service profile to pass on to the rmw implementation.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created publisher.
*/
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename PublisherT = ::rclcpp::Publisher<MessageT, Alloc>>
std::shared_ptr<PublisherT>
create_publisher(
const std::string & topic_name,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
std::shared_ptr<Alloc> allocator = nullptr,
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
const rclcpp::QoS & qos,
const PublisherOptionsWithAllocator<AllocatorT> & options =
PublisherOptionsWithAllocator<AllocatorT>()
);
/// Create and return a Subscription.
/**
@@ -206,98 +192,29 @@ public:
* \param[in] msg_mem_strat The message memory strategy to use for allocating messages.
* \return Shared pointer to the created subscription.
*/
/* TODO(jacquelinekay):
Windows build breaks when static member function passed as default
argument to msg_mem_strat, nullptr is a workaround.
*/
template<
typename MessageT,
typename CallbackT,
typename AllocatorT = std::allocator<void>,
typename SubscriptionT = rclcpp::Subscription<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, AllocatorT>>
typename CallbackMessageT =
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT,
AllocatorT
>
>
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
const rclcpp::QoS & qos,
CallbackT && callback,
size_t qos_history_depth,
const SubscriptionOptionsWithAllocator<AllocatorT> &
options = SubscriptionOptionsWithAllocator<AllocatorT>(),
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, AllocatorT
>::SharedPtr
msg_mem_strat = nullptr);
/// Create and return a Subscription.
/**
* \param[in] topic_name The topic to subscribe on.
* \param[in] callback The user-defined callback function.
* \param[in] qos_profile The quality of service profile to pass on to the rmw implementation.
* \param[in] group The callback group for this subscription. NULL for no callback group.
* \param[in] ignore_local_publications True to ignore local publications.
* \param[in] msg_mem_strat The message memory strategy to use for allocating messages.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created subscription.
*/
/* TODO(jacquelinekay):
Windows build breaks when static member function passed as default
argument to msg_mem_strat, nullptr is a workaround.
*/
template<
typename MessageT,
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::Subscription<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>>
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
bool ignore_local_publications = false,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
msg_mem_strat = nullptr,
std::shared_ptr<Alloc> allocator = nullptr,
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
/// Create and return a Subscription.
/**
* \param[in] topic_name The topic to subscribe on.
* \param[in] qos_history_depth The depth of the subscription's incoming message queue.
* \param[in] callback The user-defined callback function.
* \param[in] group The callback group for this subscription. NULL for no callback group.
* \param[in] ignore_local_publications True to ignore local publications.
* \param[in] msg_mem_strat The message memory strategy to use for allocating messages.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created subscription.
*/
/* TODO(jacquelinekay):
Windows build breaks when static member function passed as default
argument to msg_mem_strat, nullptr is a workaround.
*/
template<
typename MessageT,
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::Subscription<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>>
[[deprecated(
"use the create_subscription(const std::string &, CallbackT &&, size_t, "
"const SubscriptionOptions<Alloc> & = SubscriptionOptions<Alloc>(), ...) signature instead")]]
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
CallbackT && callback,
size_t qos_history_depth,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications = false,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
msg_mem_strat = nullptr,
std::shared_ptr<Alloc> allocator = nullptr,
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
const SubscriptionOptionsWithAllocator<AllocatorT> & options =
SubscriptionOptionsWithAllocator<AllocatorT>(),
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat = (
MessageMemoryStrategyT::create_default()
)
);
/// Create a timer.
/**
@@ -329,136 +246,607 @@ public:
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
/// Declare and initialize a parameter, return the effective value.
/**
* This method is used to declare that a parameter exists on this node.
* If, at run-time, the user has provided an initial value then it will be
* set in this method, otherwise the given default_value will be set.
* In either case, the resulting value is returned, whether or not it is
* based on the default value or the user provided initial value.
*
* If no parameter_descriptor is given, then the default values from the
* message definition will be used, e.g. read_only will be false.
*
* The name and type in the given rcl_interfaces::msg::ParameterDescriptor
* are ignored, and should be specified using the name argument to this
* function and the default value's type instead.
*
* If `ignore_override` is `true`, the parameter override will be ignored.
*
* This method, if successful, will result in any callback registered with
* set_on_parameters_set_callback to be called.
* If that callback prevents the initial value for the parameter from being
* set then rclcpp::exceptions::InvalidParameterValueException is thrown.
*
* The returned reference will remain valid until the parameter is
* undeclared.
*
* \param[in] name The name of the parameter.
* \param[in] default_value An initial value to be used if at run-time user
* did not override it.
* \param[in] parameter_descriptor An optional, custom description for
* the parameter.
* \param[in] ignore_override When `true`, the parameter override is ignored.
* Default to `false`.
* \return A const reference to the value of the parameter.
* \throws rclcpp::exceptions::ParameterAlreadyDeclaredException if parameter
* has already been declared.
* \throws rclcpp::exceptions::InvalidParametersException if a parameter
* name is invalid.
* \throws rclcpp::exceptions::InvalidParameterValueException if initial
* value fails to be set.
*/
RCLCPP_PUBLIC
const rclcpp::ParameterValue &
declare_parameter(
const std::string & name,
const rclcpp::ParameterValue & default_value = rclcpp::ParameterValue(),
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
rcl_interfaces::msg::ParameterDescriptor(),
bool ignore_override = false);
/// Declare and initialize a parameter with a type.
/**
* See the non-templated declare_parameter() on this class for details.
*
* If the type of the default value, and therefore also the type of return
* value, differs from the initial value provided in the node options, then
* a rclcpp::ParameterTypeException may be thrown.
* To avoid this, use the declare_parameter() method which returns an
* rclcpp::ParameterValue instead.
*
* Note, this method cannot return a const reference, because extending the
* lifetime of a temporary only works recursively with member initializers,
* and cannot be extended to members of a class returned.
* The return value of this class is a copy of the member of a ParameterValue
* which is returned by the other version of declare_parameter().
* See also:
*
* - https://en.cppreference.com/w/cpp/language/lifetime
* - https://herbsutter.com/2008/01/01/gotw-88-a-candidate-for-the-most-important-const/
* - https://www.youtube.com/watch?v=uQyT-5iWUow (cppnow 2018 presentation)
*/
template<typename ParameterT>
auto
declare_parameter(
const std::string & name,
const ParameterT & default_value,
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
rcl_interfaces::msg::ParameterDescriptor(),
bool ignore_override = false);
/// Declare and initialize several parameters with the same namespace and type.
/**
* For each key in the map, a parameter with a name of "namespace.key"
* will be set to the value in the map.
* The resulting value for each declared parameter will be returned.
*
* The name expansion is naive, so if you set the namespace to be "foo.",
* then the resulting parameter names will be like "foo..key".
* However, if the namespace is an empty string, then no leading '.' will be
* placed before each key, which would have been the case when naively
* expanding "namespace.key".
* This allows you to declare several parameters at once without a namespace.
*
* The map contains default values for parameters.
* There is another overload which takes the std::pair with the default value
* and descriptor.
*
* If `ignore_overrides` is `true`, all the overrides of the parameters declared
* by the function call will be ignored.
*
* This method, if successful, will result in any callback registered with
* set_on_parameters_set_callback to be called, once for each parameter.
* If that callback prevents the initial value for any parameter from being
* set then rclcpp::exceptions::InvalidParameterValueException is thrown.
*
* \param[in] namespace_ The namespace in which to declare the parameters.
* \param[in] parameters The parameters to set in the given namespace.
* \param[in] ignore_overrides When `true`, the parameters overrides are ignored.
* Default to `false`.
* \throws rclcpp::exceptions::ParameterAlreadyDeclaredException if parameter
* has already been declared.
* \throws rclcpp::exceptions::InvalidParametersException if a parameter
* name is invalid.
* \throws rclcpp::exceptions::InvalidParameterValueException if initial
* value fails to be set.
*/
template<typename ParameterT>
std::vector<ParameterT>
declare_parameters(
const std::string & namespace_,
const std::map<std::string, ParameterT> & parameters,
bool ignore_overrides = false);
/// Declare and initialize several parameters with the same namespace and type.
/**
* This version will take a map where the value is a pair, with the default
* parameter value as the first item and a parameter descriptor as the second.
*
* See the simpler declare_parameters() on this class for more details.
*/
template<typename ParameterT>
std::vector<ParameterT>
declare_parameters(
const std::string & namespace_,
const std::map<
std::string,
std::pair<ParameterT, rcl_interfaces::msg::ParameterDescriptor>
> & parameters,
bool ignore_overrides = false);
/// Undeclare a previously declared parameter.
/**
* This method will not cause a callback registered with
* set_on_parameters_set_callback to be called.
*
* \param[in] name The name of the parameter to be undeclared.
* \throws rclcpp::exceptions::ParameterNotDeclaredException if the parameter
* has not been declared.
* \throws rclcpp::exceptions::ParameterImmutableException if the parameter
* was create as read_only (immutable).
*/
RCLCPP_PUBLIC
void
undeclare_parameter(const std::string & name);
/// Return true if a given parameter is declared.
/**
* \param[in] name The name of the parameter to check for being declared.
* \return true if the parameter name has been declared, otherwise false.
*/
RCLCPP_PUBLIC
bool
has_parameter(const std::string & name) const;
/// Set a single parameter.
/**
* Set the given parameter and then return result of the set action.
*
* If the parameter has not been declared this function may throw the
* rclcpp::exceptions::ParameterNotDeclaredException exception, but only if
* the node was not created with the
* rclcpp::NodeOptions::allow_undeclared_parameters set to true.
* If undeclared parameters are allowed, then the parameter is implicitly
* declared with the default parameter meta data before being set.
* Parameter overrides are ignored by set_parameter.
*
* This method will result in any callback registered with
* set_on_parameters_set_callback to be called.
* If the callback prevents the parameter from being set, then it will be
* reflected in the SetParametersResult that is returned, but no exception
* will be thrown.
*
* If the value type of the parameter is rclcpp::PARAMETER_NOT_SET, and the
* existing parameter type is something else, then the parameter will be
* implicitly undeclared.
* This will result in a parameter event indicating that the parameter was
* deleted.
*
* \param[in] parameter The parameter to be set.
* \return The result of the set action.
* \throws rclcpp::exceptions::ParameterNotDeclaredException if the parameter
* has not been declared and undeclared parameters are not allowed.
*/
RCLCPP_PUBLIC
rcl_interfaces::msg::SetParametersResult
set_parameter(const rclcpp::Parameter & parameter);
/// Set one or more parameters, one at a time.
/**
* Set the given parameters, one at a time, and then return result of each
* set action.
*
* Parameters are set in the order they are given within the input vector.
*
* Like set_parameter, if any of the parameters to be set have not first been
* declared, and undeclared parameters are not allowed (the default), then
* this method will throw rclcpp::exceptions::ParameterNotDeclaredException.
*
* If setting a parameter fails due to not being declared, then the
* parameters which have already been set will stay set, and no attempt will
* be made to set the parameters which come after.
*
* If a parameter fails to be set due to any other reason, like being
* rejected by the user's callback (basically any reason other than not
* having been declared beforehand), then that is reflected in the
* corresponding SetParametersResult in the vector returned by this function.
*
* This method will result in any callback registered with
* set_on_parameters_set_callback to be called, once for each parameter.
* If the callback prevents the parameter from being set, then, as mentioned
* before, it will be reflected in the corresponding SetParametersResult
* that is returned, but no exception will be thrown.
*
* Like set_parameter() this method will implicitly undeclare parameters
* with the type rclcpp::PARAMETER_NOT_SET.
*
* \param[in] parameters The vector of parameters to be set.
* \return The results for each set action as a vector.
* \throws rclcpp::exceptions::ParameterNotDeclaredException if any parameter
* has not been declared and undeclared parameters are not allowed.
*/
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(const std::vector<rclcpp::Parameter> & parameters);
/// Set one or more parameters, all at once.
/**
* Set the given parameters, all at one time, and then aggregate result.
*
* Behaves like set_parameter, except that it sets multiple parameters,
* failing all if just one of the parameters are unsuccessfully set.
* Either all of the parameters are set or none of them are set.
*
* Like set_parameter and set_parameters, this method may throw an
* rclcpp::exceptions::ParameterNotDeclaredException exception if any of the
* parameters to be set have not first been declared.
* If the exception is thrown then none of the parameters will have been set.
*
* This method will result in any callback registered with
* set_on_parameters_set_callback to be called, just one time.
* If the callback prevents the parameters from being set, then it will be
* reflected in the SetParametersResult which is returned, but no exception
* will be thrown.
*
* If you pass multiple rclcpp::Parameter instances with the same name, then
* only the last one in the vector (forward iteration) will be set.
*
* Like set_parameter() this method will implicitly undeclare parameters
* with the type rclcpp::PARAMETER_NOT_SET.
*
* \param[in] parameters The vector of parameters to be set.
* \return The aggregate result of setting all the parameters atomically.
* \throws rclcpp::exceptions::ParameterNotDeclaredException if any parameter
* has not been declared and undeclared parameters are not allowed.
*/
RCLCPP_PUBLIC
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(const std::vector<rclcpp::Parameter> & parameters);
template<typename ParameterT>
void
set_parameter_if_not_set(
const std::string & name,
const ParameterT & value);
/// Set a map of parameters with the same prefix.
/// Return the parameter by the given name.
/**
* For each key in the map, a parameter with a name of "name.key" will be set
* to the value in the map.
* If the parameter has not been declared, then this method may throw the
* rclcpp::exceptions::ParameterNotDeclaredException exception.
*
* \param[in] name The prefix of the parameters to set.
* \param[in] values The parameters to set in the given prefix.
* If undeclared parameters are allowed, see the node option
* rclcpp::NodeOptions::allow_undeclared_parameters, then this method will
* not throw an exception, and instead return a default initialized
* rclcpp::Parameter, which has a type of
* rclcpp::ParameterType::PARAMETER_NOT_SET.
*
* \param[in] name The name of the parameter to get.
* \return The requested parameter inside of a rclcpp parameter object.
* \throws rclcpp::exceptions::ParameterNotDeclaredException if the parameter
* has not been declared and undeclared parameters are not allowed.
*/
template<typename MapValueT>
void
set_parameters_if_not_set(
const std::string & name,
const std::map<std::string, MapValueT> & values);
RCLCPP_PUBLIC
std::vector<rclcpp::Parameter>
get_parameters(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
rclcpp::Parameter
get_parameter(const std::string & name) const;
RCLCPP_PUBLIC
bool
get_parameter(
const std::string & name,
rclcpp::Parameter & parameter) const;
/// Assign the value of the parameter if set into the parameter argument.
/// Get the value of a parameter by the given name, and return true if it was set.
/**
* If the parameter was not set, then the "parameter" argument is never assigned a value.
* This method will never throw the
* rclcpp::exceptions::ParameterNotDeclaredException exception, but will
* instead return false if the parameter has not be previously declared.
*
* If the parameter was not declared, then the output argument for this
* method which is called "parameter" will not be assigned a value.
* If the parameter was declared, and therefore has a value, then it is
* assigned into the "parameter" argument of this method.
*
* \param[in] name The name of the parameter to get.
* \param[out] parameter The output where the value of the parameter should be assigned.
* \returns true if the parameter was set, false otherwise
* \param[out] parameter The output storage for the parameter being retrieved.
* \return true if the parameter was previously declared, otherwise false.
*/
RCLCPP_PUBLIC
bool
get_parameter(const std::string & name, rclcpp::Parameter & parameter) const;
/// Get the value of a parameter by the given name, and return true if it was set.
/**
* Identical to the non-templated version of this method, except that when
* assigning the output argument called "parameter", this method will attempt
* to coerce the parameter value into the type requested by the given
* template argument, which may fail and throw an exception.
*
* If the parameter has not been declared, it will not attempt to coerce the
* value into the requested type, as it is known that the type is not set.
*
* \throws rclcpp::ParameterTypeException if the requested type does not
* match the value of the parameter which is stored.
*/
template<typename ParameterT>
bool
get_parameter(const std::string & name, ParameterT & parameter) const;
/// Assign the value of the map parameter if set into the values argument.
/// Get the parameter value, or the "alternative_value" if not set, and assign it to "parameter".
/**
* Parameter names that are part of a map are of the form "name.member".
* This API gets all parameters that begin with "name", storing them into the
* map with the name of the parameter and their value.
* If there are no members in the named map, then the "values" argument is not changed.
*
* \param[in] name The prefix of the parameters to get.
* \param[out] values The map of output values, with one std::string,MapValueT
* per parameter.
* \returns true if values was changed, false otherwise
*/
template<typename MapValueT>
bool
get_parameters(
const std::string & name,
std::map<std::string, MapValueT> & values) const;
/// Get the parameter value, or the "alternative value" if not set, and assign it to "value".
/**
* If the parameter was not set, then the "value" argument is assigned
* If the parameter was not set, then the "parameter" argument is assigned
* the "alternative_value".
* In all cases, the parameter remains not set after this function is called.
*
* Like the version of get_parameter() which returns a bool, this method will
* not throw the rclcpp::exceptions::ParameterNotDeclaredException exception.
*
* In all cases, the parameter is never set or declared within the node.
*
* \param[in] name The name of the parameter to get.
* \param[out] value The output where the value of the parameter should be assigned.
* \param[out] parameter The output where the value of the parameter should be assigned.
* \param[in] alternative_value Value to be stored in output if the parameter was not set.
* \returns true if the parameter was set, false otherwise
* \returns true if the parameter was set, false otherwise.
*/
template<typename ParameterT>
bool
get_parameter_or(
const std::string & name,
ParameterT & value,
ParameterT & parameter,
const ParameterT & alternative_value) const;
/// Get the parameter value; if not set, set the "alternative value" and store it in the node.
/// Return the parameters by the given parameter names.
/**
* If the parameter is set, then the "value" argument is assigned the value
* in the parameter.
* If the parameter is not set, then the "value" argument is assigned the "alternative_value",
* and the parameter is set to the "alternative_value" on the node.
* Like get_parameters(), this method may throw the
* rclcpp::exceptions::ParameterNotDeclaredException exception if the
* requested parameter has not been declared and undeclared parameters are
* not allowed.
*
* \param[in] name The name of the parameter to get.
* \param[out] value The output where the value of the parameter should be assigned.
* \param[in] alternative_value Value to be stored in output and parameter if the parameter was not set.
* Also like get_parameters(), if undeclared parameters are allowed and the
* parameter has not been declared, then the corresponding rclcpp::Parameter
* will be default initialized and therefore have the type
* rclcpp::ParameterType::PARAMETER_NOT_SET.
*
* \param[in] names The names of the parameters to be retrieved.
* \return The parameters that were retrieved.
* \throws rclcpp::exceptions::ParameterNotDeclaredException if any of the
* parameters have not been declared and undeclared parameters are not
* allowed.
*/
RCLCPP_PUBLIC
std::vector<rclcpp::Parameter>
get_parameters(const std::vector<std::string> & names) const;
/// Get the parameter values for all parameters that have a given prefix.
/**
* The "prefix" argument is used to list the parameters which are prefixed
* with that prefix, see also list_parameters().
*
* The resulting list of parameter names are used to get the values of the
* parameters.
*
* The names which are used as keys in the values map have the prefix removed.
* For example, if you use the prefix "foo" and the parameters "foo.ping" and
* "foo.pong" exist, then the returned map will have the keys "ping" and
* "pong".
*
* An empty string for the prefix will match all parameters.
*
* If no parameters with the prefix are found, then the output parameter
* "values" will be unchanged and false will be returned.
* Otherwise, the parameter names and values will be stored in the map and
* true will be returned to indicate "values" was mutated.
*
* This method will never throw the
* rclcpp::exceptions::ParameterNotDeclaredException exception because the
* action of listing the parameters is done atomically with getting the
* values, and therefore they are only listed if already declared and cannot
* be undeclared before being retrieved.
*
* Like the templated get_parameter() variant, this method will attempt to
* coerce the parameter values into the type requested by the given
* template argument, which may fail and throw an exception.
*
* \param[in] prefix The prefix of the parameters to get.
* \param[out] values The map used to store the parameter names and values,
* respectively, with one entry per parameter matching prefix.
* \returns true if output "values" was changed, false otherwise.
* \throws rclcpp::ParameterTypeException if the requested type does not
* match the value of the parameter which is stored.
*/
template<typename ParameterT>
void
get_parameter_or_set(
const std::string & name,
ParameterT & value,
const ParameterT & alternative_value);
bool
get_parameters(
const std::string & prefix,
std::map<std::string, ParameterT> & values) const;
/// Return the parameter descriptor for the given parameter name.
/**
* Like get_parameters(), this method may throw the
* rclcpp::exceptions::ParameterNotDeclaredException exception if the
* requested parameter has not been declared and undeclared parameters are
* not allowed.
*
* If undeclared parameters are allowed, then a default initialized
* descriptor will be returned.
*
* \param[in] name The name of the parameter to describe.
* \return The descriptor for the given parameter name.
* \throws rclcpp::exceptions::ParameterNotDeclaredException if the
* parameter has not been declared and undeclared parameters are not
* allowed.
*/
RCLCPP_PUBLIC
rcl_interfaces::msg::ParameterDescriptor
describe_parameter(const std::string & name) const;
/// Return a vector of parameter descriptors, one for each of the given names.
/**
* Like get_parameters(), this method may throw the
* rclcpp::exceptions::ParameterNotDeclaredException exception if any of the
* requested parameters have not been declared and undeclared parameters are
* not allowed.
*
* If undeclared parameters are allowed, then a default initialized
* descriptor will be returned for the undeclared parameter's descriptor.
*
* If the names vector is empty, then an empty vector will be returned.
*
* \param[in] names The list of parameter names to describe.
* \return A list of parameter descriptors, one for each parameter given.
* \throws rclcpp::exceptions::ParameterNotDeclaredException if any of the
* parameters have not been declared and undeclared parameters are not
* allowed.
*/
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const;
/// Return a vector of parameter types, one for each of the given names.
/**
* Like get_parameters(), this method may throw the
* rclcpp::exceptions::ParameterNotDeclaredException exception if any of the
* requested parameters have not been declared and undeclared parameters are
* not allowed.
*
* If undeclared parameters are allowed, then the default type
* rclcpp::ParameterType::PARAMETER_NOT_SET will be returned.
*
* \param[in] names The list of parameter names to get the types.
* \return A list of parameter types, one for each parameter given.
* \throws rclcpp::exceptions::ParameterNotDeclaredException if any of the
* parameters have not been declared and undeclared parameters are not
* allowed.
*/
RCLCPP_PUBLIC
std::vector<uint8_t>
get_parameter_types(const std::vector<std::string> & names) const;
/// Return a list of parameters with any of the given prefixes, up to the given depth.
/**
* \todo: properly document and test this method.
*/
RCLCPP_PUBLIC
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const;
/// Register the callback for parameter changes
/**
* \param[in] callback User defined callback function.
* It is expected to atomically set parameters.
* \note Repeated invocations of this function will overwrite previous callbacks
*/
template<typename CallbackT>
void
register_param_change_callback(CallbackT && callback);
using OnSetParametersCallbackHandle =
rclcpp::node_interfaces::OnSetParametersCallbackHandle;
using OnParametersSetCallbackType =
rclcpp::node_interfaces::NodeParametersInterface::OnParametersSetCallbackType;
/// Add a callback for when parameters are being set.
/**
* The callback signature is designed to allow handling of any of the above
* `set_parameter*` or `declare_parameter*` methods, and so it takes a const
* reference to a vector of parameters to be set, and returns an instance of
* rcl_interfaces::msg::SetParametersResult to indicate whether or not the
* parameter should be set or not, and if not why.
*
* For an example callback:
*
* ```cpp
* rcl_interfaces::msg::SetParametersResult
* my_callback(const std::vector<rclcpp::Parameter> & parameters)
* {
* rcl_interfaces::msg::SetParametersResult result;
* result.successful = true;
* for (const auto & parameter : parameters) {
* if (!some_condition) {
* result.successful = false;
* result.reason = "the reason it could not be allowed";
* }
* }
* return result;
* }
* ```
*
* You can see that the SetParametersResult is a boolean flag for success
* and an optional reason that can be used in error reporting when it fails.
*
* This allows the node developer to control which parameters may be changed.
*
* Note that the callback is called when declare_parameter() and its variants
* are called, and so you cannot assume the parameter has been set before
* this callback, so when checking a new value against the existing one, you
* must account for the case where the parameter is not yet set.
*
* Some constraints like read_only are enforced before the callback is called.
*
* The callback may introspect other already set parameters (by calling any
* of the {get,list,describe}_parameter() methods), but may *not* modify
* other parameters (by calling any of the {set,declare}_parameter() methods)
* or modify the registered callback itself (by calling the
* set_on_parameters_set_callback() method). If a callback tries to do any
* of the latter things,
* rclcpp::exceptions::ParameterModifiedInCallbackException will be thrown.
*
* The callback functions must remain valid as long as the
* returned smart pointer is valid.
* The returned smart pointer can be promoted to a shared version.
*
* Resetting or letting the smart pointer go out of scope unregisters the callback.
* `remove_on_set_parameters_callback` can also be used.
*
* The registered callbacks are called when a parameter is set.
* When a callback returns a not successful result, the remaining callbacks aren't called.
* The order of the callback is the reverse from the registration order.
*
* \param callback The callback to register.
* \returns A shared pointer. The callback is valid as long as the smart pointer is alive.
* \throws std::bad_alloc if the allocation of the OnSetParametersCallbackHandle fails.
*/
RCLCPP_PUBLIC
OnSetParametersCallbackHandle::SharedPtr
add_on_set_parameters_callback(OnParametersSetCallbackType callback);
/// Remove a callback registered with `add_on_set_parameters_callback`.
/**
* Delete a handler returned by `add_on_set_parameters_callback`.
*
* e.g.:
*
* `remove_on_set_parameters_callback(scoped_callback.get())`
*
* As an alternative, the smart pointer can be reset:
*
* `scoped_callback.reset()`
*
* Supposing that `scoped_callback` was the only owner.
*
* Calling `remove_on_set_parameters_callback` more than once with the same handler,
* or calling it after the shared pointer has been reset is an error.
* Resetting or letting the smart pointer go out of scope after calling
* `remove_on_set_parameters_callback` is not a problem.
*
* \param handler The callback handler to remove.
* \throws std::runtime_error if the handler was not created with `add_on_set_parameters_callback`,
* or if it has been removed before.
*/
RCLCPP_PUBLIC
void
remove_on_set_parameters_callback(const OnSetParametersCallbackHandle * const handler);
/// Register a callback to be called anytime a parameter is about to be changed.
/**
* With this method, only one callback can be set at a time. The callback that was previously
* set by this method is returned or `nullptr` if no callback was previously set.
*
* The callbacks added with `add_on_set_parameters_callback` are stored in a different place.
* `remove_on_set_parameters_callback` can't be used with the callbacks registered with this
* method. For removing it, use `set_on_parameters_set_callback(nullptr)`.
*
* \param[in] callback The callback to be called when the value for a
* parameter is about to be set.
* \return The previous callback that was registered, if there was one,
* otherwise nullptr.
*/
RCLCPP_PUBLIC
OnParametersSetCallbackType
set_on_parameters_set_callback(rclcpp::Node::OnParametersSetCallbackType callback);
/// Get the fully-qualified names of all available nodes.
/**
* The fully-qualified name includes the local namespace and name of the node.
* \return A vector of fully-qualified names of nodes.
*/
RCLCPP_PUBLIC
std::vector<std::string>
get_node_names() const;
@@ -568,15 +956,17 @@ public:
*
* For example, consider:
*
* auto node = std::make_shared<rclcpp::Node>("my_node", "my_ns");
* node->get_sub_namespace(); // -> ""
* auto sub_node1 = node->create_sub_node("a");
* sub_node1->get_sub_namespace(); // -> "a"
* auto sub_node2 = sub_node1->create_sub_node("b");
* sub_node2->get_sub_namespace(); // -> "a/b"
* auto sub_node3 = node->create_sub_node("foo");
* sub_node3->get_sub_namespace(); // -> "foo"
* node->get_sub_namespace(); // -> ""
* ```cpp
* auto node = std::make_shared<rclcpp::Node>("my_node", "my_ns");
* node->get_sub_namespace(); // -> ""
* auto sub_node1 = node->create_sub_node("a");
* sub_node1->get_sub_namespace(); // -> "a"
* auto sub_node2 = sub_node1->create_sub_node("b");
* sub_node2->get_sub_namespace(); // -> "a/b"
* auto sub_node3 = node->create_sub_node("foo");
* sub_node3->get_sub_namespace(); // -> "foo"
* node->get_sub_namespace(); // -> ""
* ```
*
* get_namespace() will return the original node namespace, and will not
* include the sub-namespace if one exists.
@@ -598,15 +988,17 @@ public:
*
* For example, consider:
*
* auto node = std::make_shared<rclcpp::Node>("my_node", "my_ns");
* node->get_effective_namespace(); // -> "/my_ns"
* auto sub_node1 = node->create_sub_node("a");
* sub_node1->get_effective_namespace(); // -> "/my_ns/a"
* auto sub_node2 = sub_node1->create_sub_node("b");
* sub_node2->get_effective_namespace(); // -> "/my_ns/a/b"
* auto sub_node3 = node->create_sub_node("foo");
* sub_node3->get_effective_namespace(); // -> "/my_ns/foo"
* node->get_effective_namespace(); // -> "/my_ns"
* ```cpp
* auto node = std::make_shared<rclcpp::Node>("my_node", "my_ns");
* node->get_effective_namespace(); // -> "/my_ns"
* auto sub_node1 = node->create_sub_node("a");
* sub_node1->get_effective_namespace(); // -> "/my_ns/a"
* auto sub_node2 = sub_node1->create_sub_node("b");
* sub_node2->get_effective_namespace(); // -> "/my_ns/a/b"
* auto sub_node3 = node->create_sub_node("foo");
* sub_node3->get_effective_namespace(); // -> "/my_ns/foo"
* node->get_effective_namespace(); // -> "/my_ns"
* ```
*
* \sa get_namespace()
* \sa get_sub_namespace()
@@ -655,14 +1047,27 @@ public:
* with a leading '/'.
*/
RCLCPP_PUBLIC
Node::SharedPtr
rclcpp::Node::SharedPtr
create_sub_node(const std::string & sub_namespace);
/// Return the NodeOptions used when creating this node.
RCLCPP_PUBLIC
const NodeOptions &
const rclcpp::NodeOptions &
get_node_options() const;
/// Manually assert that this Node is alive (for RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_NODE).
/**
* If the rmw Liveliness policy is set to RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_NODE, the creator
* of this node may manually call `assert_liveliness` at some point in time to signal to the rest
* of the system that this Node is still alive.
*
* \return `true` if the liveliness was asserted successfully, otherwise `false`
*/
RCLCPP_PUBLIC
RCUTILS_WARN_UNUSED
bool
assert_liveliness() const;
protected:
/// Construct a sub-node, which will extend the namespace of all entities created with it.
/**
@@ -694,7 +1099,7 @@ private:
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr node_time_source_;
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables_;
const NodeOptions node_options_;
const rclcpp::NodeOptions node_options_;
const std::string sub_namespace_;
const std::string effective_namespace_;
};

View File

@@ -36,11 +36,12 @@
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/create_client.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rclcpp/create_service.hpp"
#include "rclcpp/create_subscription.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -67,182 +68,38 @@ template<typename MessageT, typename AllocatorT, typename PublisherT>
std::shared_ptr<PublisherT>
Node::create_publisher(
const std::string & topic_name,
size_t qos_history_depth,
const rclcpp::QoS & qos,
const PublisherOptionsWithAllocator<AllocatorT> & options)
{
std::shared_ptr<AllocatorT> allocator = options.allocator;
if (!allocator) {
allocator = std::make_shared<AllocatorT>();
}
rmw_qos_profile_t qos_profile = options.qos_profile;
qos_profile.depth = qos_history_depth;
bool use_intra_process;
switch (options.use_intra_process_comm) {
case IntraProcessSetting::Enable:
use_intra_process = true;
break;
case IntraProcessSetting::Disable:
use_intra_process = false;
break;
case IntraProcessSetting::NodeDefault:
use_intra_process = this->get_node_options().use_intra_process_comms();
break;
default:
throw std::runtime_error("Unrecognized IntraProcessSetting value");
break;
}
return rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
this->node_topics_.get(),
*this,
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
qos_profile,
use_intra_process,
allocator);
}
template<typename MessageT, typename Alloc, typename PublisherT>
std::shared_ptr<PublisherT>
Node::create_publisher(
const std::string & topic_name, size_t qos_history_depth,
std::shared_ptr<Alloc> allocator,
IntraProcessSetting use_intra_process_comm)
{
PublisherOptionsWithAllocator<Alloc> pub_options;
pub_options.allocator = allocator;
pub_options.use_intra_process_comm = use_intra_process_comm;
return this->create_publisher<MessageT, Alloc, PublisherT>(
topic_name, qos_history_depth, pub_options);
}
template<typename MessageT, typename Alloc, typename PublisherT>
std::shared_ptr<PublisherT>
Node::create_publisher(
const std::string & topic_name, const rmw_qos_profile_t & qos_profile,
std::shared_ptr<Alloc> allocator, IntraProcessSetting use_intra_process_comm)
{
PublisherOptionsWithAllocator<Alloc> pub_options;
pub_options.qos_profile = qos_profile;
pub_options.allocator = allocator;
pub_options.use_intra_process_comm = use_intra_process_comm;
return this->create_publisher<MessageT, Alloc, PublisherT>(
topic_name, qos_profile.depth, pub_options);
qos,
options);
}
template<
typename MessageT,
typename CallbackT,
typename AllocatorT,
typename SubscriptionT>
typename CallbackMessageT,
typename SubscriptionT,
typename MessageMemoryStrategyT>
std::shared_ptr<SubscriptionT>
Node::create_subscription(
const std::string & topic_name,
const rclcpp::QoS & qos,
CallbackT && callback,
size_t qos_history_depth,
const SubscriptionOptionsWithAllocator<AllocatorT> & options,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, AllocatorT>::SharedPtr
msg_mem_strat)
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat)
{
using CallbackMessageT = typename rclcpp::subscription_traits::has_message_type<CallbackT>::type;
std::shared_ptr<AllocatorT> allocator = options.allocator;
if (!allocator) {
allocator = std::make_shared<AllocatorT>();
}
rmw_qos_profile_t qos_profile = options.qos_profile;
qos_profile.depth = qos_history_depth;
if (!msg_mem_strat) {
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
msg_mem_strat = MessageMemoryStrategy<CallbackMessageT, AllocatorT>::create_default();
}
bool use_intra_process;
switch (options.use_intra_process_comm) {
case IntraProcessSetting::Enable:
use_intra_process = true;
break;
case IntraProcessSetting::Disable:
use_intra_process = false;
break;
case IntraProcessSetting::NodeDefault:
use_intra_process = this->get_node_options().use_intra_process_comms();
break;
default:
throw std::runtime_error("Unrecognized IntraProcessSetting value");
break;
}
return rclcpp::create_subscription<
MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT>(
this->node_topics_.get(),
return rclcpp::create_subscription<MessageT>(
*this,
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
qos,
std::forward<CallbackT>(callback),
qos_profile,
options.callback_group,
options.ignore_local_publications,
use_intra_process,
msg_mem_strat,
allocator);
}
template<
typename MessageT,
typename CallbackT,
typename Alloc,
typename SubscriptionT>
std::shared_ptr<SubscriptionT>
Node::create_subscription(
const std::string & topic_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
msg_mem_strat,
std::shared_ptr<Alloc> allocator,
IntraProcessSetting use_intra_process_comm)
{
SubscriptionOptionsWithAllocator<Alloc> sub_options;
sub_options.qos_profile = qos_profile;
sub_options.callback_group = group;
sub_options.ignore_local_publications = ignore_local_publications;
sub_options.allocator = allocator;
sub_options.use_intra_process_comm = use_intra_process_comm;
return this->create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
topic_name, std::forward<CallbackT>(callback), qos_profile.depth, sub_options, msg_mem_strat);
}
template<
typename MessageT,
typename CallbackT,
typename Alloc,
typename SubscriptionT>
std::shared_ptr<SubscriptionT>
Node::create_subscription(
const std::string & topic_name,
CallbackT && callback,
size_t qos_history_depth,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
msg_mem_strat,
std::shared_ptr<Alloc> allocator,
IntraProcessSetting use_intra_process_comm)
{
SubscriptionOptionsWithAllocator<Alloc> sub_options;
sub_options.callback_group = group;
sub_options.ignore_local_publications = ignore_local_publications;
sub_options.allocator = allocator;
sub_options.use_intra_process_comm = use_intra_process_comm;
return this->create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
topic_name, std::forward<CallbackT>(callback), qos_history_depth, sub_options, msg_mem_strat);
options,
msg_mem_strat);
}
template<typename DurationRepT, typename DurationT, typename CallbackT>
@@ -267,21 +124,13 @@ Node::create_client(
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
rcl_client_options_t options = rcl_client_get_default_options();
options.qos = qos_profile;
using rclcpp::Client;
using rclcpp::ClientBase;
auto cli = Client<ServiceT>::make_shared(
node_base_.get(),
return rclcpp::create_client<ServiceT>(
node_base_,
node_graph_,
node_services_,
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
options);
auto cli_base_ptr = std::dynamic_pointer_cast<ClientBase>(cli);
node_services_->add_client(cli_base_ptr, group);
return cli;
qos_profile,
group);
}
template<typename ServiceT, typename CallbackT>
@@ -301,83 +150,82 @@ Node::create_service(
group);
}
template<typename CallbackT>
void
Node::register_param_change_callback(CallbackT && callback)
template<typename ParameterT>
auto
Node::declare_parameter(
const std::string & name,
const ParameterT & default_value,
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
bool ignore_override)
{
this->node_parameters_->register_param_change_callback(std::forward<CallbackT>(callback));
return this->declare_parameter(
name,
rclcpp::ParameterValue(default_value),
parameter_descriptor,
ignore_override
).get<ParameterT>();
}
template<typename ParameterT>
void
Node::set_parameter_if_not_set(
const std::string & name,
const ParameterT & value)
std::vector<ParameterT>
Node::declare_parameters(
const std::string & namespace_,
const std::map<std::string, ParameterT> & parameters,
bool ignore_overrides)
{
std::string parameter_name_with_sub_namespace =
extend_name_with_sub_namespace(name, this->get_sub_namespace());
rclcpp::Parameter parameter;
if (!this->get_parameter(parameter_name_with_sub_namespace, parameter)) {
this->set_parameters({
rclcpp::Parameter(parameter_name_with_sub_namespace, value),
});
}
}
// this is a partially-specialized version of set_parameter_if_not_set above,
// where our concrete type for ParameterT is std::map, but the to-be-determined
// type is the value in the map.
template<typename MapValueT>
void
Node::set_parameters_if_not_set(
const std::string & name,
const std::map<std::string, MapValueT> & values)
{
std::vector<rclcpp::Parameter> params;
for (const auto & val : values) {
std::string param_name = name + "." + val.first;
rclcpp::Parameter parameter;
if (!this->get_parameter(param_name, parameter)) {
params.push_back(rclcpp::Parameter(param_name, val.second));
std::vector<ParameterT> result;
std::string normalized_namespace = namespace_.empty() ? "" : (namespace_ + ".");
std::transform(
parameters.begin(), parameters.end(), std::back_inserter(result),
[this, &normalized_namespace, ignore_overrides](auto element) {
return this->declare_parameter(
normalized_namespace + element.first,
element.second,
rcl_interfaces::msg::ParameterDescriptor(),
ignore_overrides);
}
}
this->set_parameters(params);
}
template<typename ParameterT>
bool
Node::get_parameter(const std::string & name, ParameterT & value) const
{
std::string sub_name = extend_name_with_sub_namespace(name, this->get_sub_namespace());
rclcpp::Parameter parameter;
bool result = get_parameter(sub_name, parameter);
if (result) {
value = parameter.get_value<ParameterT>();
}
);
return result;
}
// this is a partially-specialized version of get_parameter above,
// where our concrete type for ParameterT is std::map, but the to-be-determined
// type is the value in the map.
template<typename MapValueT>
bool
Node::get_parameters(
const std::string & name,
std::map<std::string, MapValueT> & values) const
template<typename ParameterT>
std::vector<ParameterT>
Node::declare_parameters(
const std::string & namespace_,
const std::map<
std::string,
std::pair<ParameterT, rcl_interfaces::msg::ParameterDescriptor>
> & parameters,
bool ignore_overrides)
{
std::map<std::string, rclcpp::Parameter> params;
bool result = node_parameters_->get_parameters_by_prefix(name, params);
if (result) {
for (const auto & param : params) {
values[param.first] = param.second.get_value<MapValueT>();
std::vector<ParameterT> result;
std::string normalized_namespace = namespace_.empty() ? "" : (namespace_ + ".");
std::transform(
parameters.begin(), parameters.end(), std::back_inserter(result),
[this, &normalized_namespace, ignore_overrides](auto element) {
return static_cast<ParameterT>(
this->declare_parameter(
normalized_namespace + element.first,
element.second.first,
element.second.second,
ignore_overrides)
);
}
);
return result;
}
template<typename ParameterT>
bool
Node::get_parameter(const std::string & name, ParameterT & parameter) const
{
std::string sub_name = extend_name_with_sub_namespace(name, this->get_sub_namespace());
rclcpp::Parameter parameter_variant;
bool result = get_parameter(sub_name, parameter_variant);
if (result) {
parameter = static_cast<ParameterT>(parameter_variant.get_value<ParameterT>());
}
return result;
@@ -387,34 +235,36 @@ template<typename ParameterT>
bool
Node::get_parameter_or(
const std::string & name,
ParameterT & value,
ParameterT & parameter,
const ParameterT & alternative_value) const
{
std::string sub_name = extend_name_with_sub_namespace(name, this->get_sub_namespace());
bool got_parameter = get_parameter(sub_name, value);
bool got_parameter = get_parameter(sub_name, parameter);
if (!got_parameter) {
value = alternative_value;
parameter = alternative_value;
}
return got_parameter;
}
// this is a partially-specialized version of get_parameter above,
// where our concrete type for ParameterT is std::map, but the to-be-determined
// type is the value in the map.
template<typename ParameterT>
void
Node::get_parameter_or_set(
const std::string & name,
ParameterT & value,
const ParameterT & alternative_value)
bool
Node::get_parameters(
const std::string & prefix,
std::map<std::string, ParameterT> & values) const
{
std::string sub_name = extend_name_with_sub_namespace(name, this->get_sub_namespace());
bool got_parameter = get_parameter(sub_name, value);
if (!got_parameter) {
this->set_parameters({
rclcpp::Parameter(sub_name, alternative_value),
});
value = alternative_value;
std::map<std::string, rclcpp::Parameter> params;
bool result = node_parameters_->get_parameters_by_prefix(prefix, params);
if (result) {
for (const auto & param : params) {
values[param.first] = static_cast<ParameterT>(param.second.get_value<ParameterT>());
}
}
return result;
}
} // namespace rclcpp

View File

@@ -0,0 +1,149 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__NODE_INTERFACES__GET_NODE_BASE_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__GET_NODE_BASE_INTERFACE_HPP_
#include <memory>
#include <utility>
#include <type_traits>
#include "rclcpp/node_interfaces/node_base_interface.hpp"
/// This header provides the get_node_base_interface() template function.
/**
* This function is useful for getting the NodeBaseInterface pointer from
* various kinds of Node-like classes.
*
* It's able to get the NodeBaseInterface pointer so long as the class
* has a method called ``get_node_base_interface()`` which returns
* either a pointer (const or not) to a NodeBaseInterface or a
* std::shared_ptr to a NodeBaseInterface.
*/
namespace rclcpp
{
namespace node_interfaces
{
namespace detail
{
// This is a meta-programming checker for if a given Node-like object has a
// getter called get_node_base_interface() which returns various types,
// e.g. const pointer or a shared pointer.
template<typename NodeType, typename ReturnType>
struct has_get_node_base_interface
{
private:
template<typename T>
static constexpr
auto
check(T *)->typename std::is_same<
decltype(std::declval<T>().get_node_base_interface()),
ReturnType
>::type;
template<typename>
static constexpr
std::false_type
check(...);
public:
using type = decltype(check<NodeType>(nullptr));
static constexpr bool value = type::value;
};
// If NodeType is a pointer to NodeBaseInterface already (just normal function overload).
inline
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface_from_pointer(rclcpp::node_interfaces::NodeBaseInterface * pointer)
{
return pointer;
}
// If NodeType has a method called get_node_base_interface() which returns a shared pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_base_interface<
typename std::remove_pointer<NodeType>::type,
std::shared_ptr<rclcpp::node_interfaces::NodeBaseInterface>
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_base_interface().get();
}
// If NodeType has a method called get_node_base_interface() which returns a pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_base_interface<
typename std::remove_pointer<NodeType>::type,
rclcpp::node_interfaces::NodeBaseInterface *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_base_interface();
}
// Forward shared_ptr's to const node pointer signatures.
template<
typename NodeType,
typename std::enable_if<std::is_same<
NodeType,
typename std::shared_ptr<typename std::remove_pointer<NodeType>::type::element_type> *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface_from_pointer(NodeType node_shared_pointer)
{
return get_node_base_interface_from_pointer(node_shared_pointer->get());
}
} // namespace detail
/// Get the NodeBaseInterface as a pointer from a pointer to a "Node like" object.
template<
typename NodeType,
typename std::enable_if<std::is_pointer<NodeType>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface(NodeType node_pointer)
{
// Forward pointers to detail implmentation directly.
return detail::get_node_base_interface_from_pointer(node_pointer);
}
/// Get the NodeBaseInterface as a pointer from a "Node like" object.
template<
typename NodeType,
typename std::enable_if<
!std::is_pointer<typename std::remove_reference<NodeType>::type>::value, int
>::type = 0
>
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface(NodeType && node_reference)
{
// Forward references to detail implmentation as a pointer.
return detail::get_node_base_interface_from_pointer(&node_reference);
}
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__GET_NODE_BASE_INTERFACE_HPP_

View File

@@ -0,0 +1,149 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__NODE_INTERFACES__GET_NODE_TIMERS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__GET_NODE_TIMERS_INTERFACE_HPP_
#include <memory>
#include <utility>
#include <type_traits>
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
/// This header provides the get_node_timers_interface() template function.
/**
* This function is useful for getting the NodeTimersInterface pointer from
* various kinds of Node-like classes.
*
* It's able to get the NodeTimersInterface pointer so long as the class
* has a method called ``get_node_timers_interface()`` which returns
* either a pointer (const or not) to a NodeTimersInterface or a
* std::shared_ptr to a NodeTimersInterface.
*/
namespace rclcpp
{
namespace node_interfaces
{
namespace detail
{
// This is a meta-programming checker for if a given Node-like object has a
// getter called get_node_timers_interface() which returns various types,
// e.g. const pointer or a shared pointer.
template<typename NodeType, typename ReturnType>
struct has_get_node_timers_interface
{
private:
template<typename T>
static constexpr
auto
check(T *)->typename std::is_same<
decltype(std::declval<T>().get_node_timers_interface()),
ReturnType
>::type;
template<typename>
static constexpr
std::false_type
check(...);
public:
using type = decltype(check<NodeType>(nullptr));
static constexpr bool value = type::value;
};
// If NodeType is a pointer to NodeTimersInterface already (just normal function overload).
inline
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface_from_pointer(rclcpp::node_interfaces::NodeTimersInterface * pointer)
{
return pointer;
}
// If NodeType has a method called get_node_timers_interface() which returns a shared pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_timers_interface<
typename std::remove_pointer<NodeType>::type,
std::shared_ptr<rclcpp::node_interfaces::NodeTimersInterface>
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_timers_interface().get();
}
// If NodeType has a method called get_node_timers_interface() which returns a pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_timers_interface<
typename std::remove_pointer<NodeType>::type,
rclcpp::node_interfaces::NodeTimersInterface *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_timers_interface();
}
// Forward shared_ptr's to const node pointer signatures.
template<
typename NodeType,
typename std::enable_if<std::is_same<
NodeType,
typename std::shared_ptr<typename std::remove_pointer<NodeType>::type::element_type> *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface_from_pointer(NodeType node_shared_pointer)
{
return get_node_timers_interface_from_pointer(node_shared_pointer->get());
}
} // namespace detail
/// Get the NodeTimersInterface as a pointer from a pointer to a "Node like" object.
template<
typename NodeType,
typename std::enable_if<std::is_pointer<NodeType>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface(NodeType node_pointer)
{
// Forward pointers to detail implmentation directly.
return detail::get_node_timers_interface_from_pointer(node_pointer);
}
/// Get the NodeTimersInterface as a pointer from a "Node like" object.
template<
typename NodeType,
typename std::enable_if<
!std::is_pointer<typename std::remove_reference<NodeType>::type>::value, int
>::type = 0
>
rclcpp::node_interfaces::NodeTimersInterface *
get_node_timers_interface(NodeType && node_reference)
{
// Forward references to detail implmentation as a pointer.
return detail::get_node_timers_interface_from_pointer(&node_reference);
}
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__GET_NODE_TIMERS_INTERFACE_HPP_

View File

@@ -0,0 +1,149 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__NODE_INTERFACES__GET_NODE_TOPICS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__GET_NODE_TOPICS_INTERFACE_HPP_
#include <memory>
#include <utility>
#include <type_traits>
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
/// This header provides the get_node_topics_interface() template function.
/**
* This function is useful for getting the NodeTopicsInterface pointer from
* various kinds of Node-like classes.
*
* It's able to get the NodeTopicsInterface pointer so long as the class
* has a method called ``get_node_topics_interface()`` which returns
* either a pointer (const or not) to a NodeTopicsInterface or a
* std::shared_ptr to a NodeTopicsInterface.
*/
namespace rclcpp
{
namespace node_interfaces
{
namespace detail
{
// This is a meta-programming checker for if a given Node-like object has a
// getter called get_node_topics_interface() which returns various types,
// e.g. const pointer or a shared pointer.
template<typename NodeType, typename ReturnType>
struct has_get_node_topics_interface
{
private:
template<typename T>
static constexpr
auto
check(T *)->typename std::is_same<
decltype(std::declval<T>().get_node_topics_interface()),
ReturnType
>::type;
template<typename>
static constexpr
std::false_type
check(...);
public:
using type = decltype(check<NodeType>(nullptr));
static constexpr bool value = type::value;
};
// If NodeType is a pointer to NodeTopicsInterface already (just normal function overload).
inline
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface_from_pointer(rclcpp::node_interfaces::NodeTopicsInterface * pointer)
{
return pointer;
}
// If NodeType has a method called get_node_topics_interface() which returns a shared pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_topics_interface<
typename std::remove_pointer<NodeType>::type,
std::shared_ptr<rclcpp::node_interfaces::NodeTopicsInterface>
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_topics_interface().get();
}
// If NodeType has a method called get_node_topics_interface() which returns a pointer.
template<
typename NodeType,
typename std::enable_if<has_get_node_topics_interface<
typename std::remove_pointer<NodeType>::type,
rclcpp::node_interfaces::NodeTopicsInterface *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface_from_pointer(NodeType node_pointer)
{
return node_pointer->get_node_topics_interface();
}
// Forward shared_ptr's to const node pointer signatures.
template<
typename NodeType,
typename std::enable_if<std::is_same<
NodeType,
typename std::shared_ptr<typename std::remove_pointer<NodeType>::type::element_type> *
>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface_from_pointer(NodeType node_shared_pointer)
{
return get_node_topics_interface_from_pointer(node_shared_pointer->get());
}
} // namespace detail
/// Get the NodeTopicsInterface as a pointer from a pointer to a "Node like" object.
template<
typename NodeType,
typename std::enable_if<std::is_pointer<NodeType>::value, int>::type = 0
>
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface(NodeType node_pointer)
{
// Forward pointers to detail implmentation directly.
return detail::get_node_topics_interface_from_pointer(node_pointer);
}
/// Get the NodeTopicsInterface as a pointer from a "Node like" object.
template<
typename NodeType,
typename std::enable_if<
!std::is_pointer<typename std::remove_reference<NodeType>::type>::value, int
>::type = 0
>
rclcpp::node_interfaces::NodeTopicsInterface *
get_node_topics_interface(NodeType && node_reference)
{
// Forward references to detail implmentation as a pointer.
return detail::get_node_topics_interface_from_pointer(&node_reference);
}
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__GET_NODE_TOPICS_INTERFACE_HPP_

View File

@@ -19,10 +19,11 @@
#include <string>
#include <vector>
#include "rcl/node.h"
#include "rclcpp/context.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_options.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -34,13 +35,15 @@ namespace node_interfaces
class NodeBase : public NodeBaseInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBase)
RCLCPP_PUBLIC
NodeBase(
const std::string & node_name,
const std::string & namespace_,
const NodeOptions & options);
rclcpp::Context::SharedPtr context,
const rcl_node_options_t & rcl_node_options,
bool use_intra_process_default);
RCLCPP_PUBLIC
virtual
@@ -86,6 +89,11 @@ public:
std::shared_ptr<const rcl_node_t>
get_shared_rcl_node_handle() const;
RCLCPP_PUBLIC
virtual
bool
assert_liveliness() const;
RCLCPP_PUBLIC
virtual
rclcpp::callback_group::CallbackGroup::SharedPtr
@@ -121,10 +129,16 @@ public:
std::unique_lock<std::recursive_mutex>
acquire_notify_guard_condition_lock() const;
RCLCPP_PUBLIC
virtual
bool
get_use_intra_process_default() const;
private:
RCLCPP_DISABLE_COPY(NodeBase)
rclcpp::Context::SharedPtr context_;
bool use_intra_process_default_;
std::shared_ptr<rcl_node_t> node_handle_;

View File

@@ -102,6 +102,12 @@ public:
std::shared_ptr<const rcl_node_t>
get_shared_rcl_node_handle() const = 0;
/// Manually assert that this Node is alive (for RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_NODE).
RCLCPP_PUBLIC
virtual
bool
assert_liveliness() const = 0;
/// Create and return a callback group.
RCLCPP_PUBLIC
virtual
@@ -149,6 +155,12 @@ public:
virtual
std::unique_lock<std::recursive_mutex>
acquire_notify_guard_condition_lock() const = 0;
/// Return the default preference for using intra process communication.
RCLCPP_PUBLIC
virtual
bool
get_use_intra_process_default() const = 0;
};
} // namespace node_interfaces

View File

@@ -17,6 +17,7 @@
#include <map>
#include <memory>
#include <list>
#include <string>
#include <vector>
@@ -40,6 +41,40 @@ namespace rclcpp
namespace node_interfaces
{
// Internal struct for holding useful info about parameters
struct ParameterInfo
{
/// Current value of the parameter.
rclcpp::ParameterValue value;
/// A description of the parameter
rcl_interfaces::msg::ParameterDescriptor descriptor;
};
// Internal RAII-style guard for mutation recursion
class ParameterMutationRecursionGuard
{
public:
explicit ParameterMutationRecursionGuard(bool & allow_mod)
: allow_modification_(allow_mod)
{
if (!allow_modification_) {
throw rclcpp::exceptions::ParameterModifiedInCallbackException(
"cannot set or declare a parameter, or change the callback from within set callback");
}
allow_modification_ = false;
}
~ParameterMutationRecursionGuard()
{
allow_modification_ = true;
}
private:
bool & allow_modification_;
};
/// Implementation of the NodeParameters part of the Node API.
class NodeParameters : public NodeParametersInterface
{
@@ -49,83 +84,117 @@ public:
RCLCPP_PUBLIC
NodeParameters(
const node_interfaces::NodeBaseInterface::SharedPtr node_base,
const node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
const node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
const node_interfaces::NodeServicesInterface::SharedPtr node_services,
const node_interfaces::NodeClockInterface::SharedPtr node_clock,
const std::vector<Parameter> & initial_parameters,
bool use_intra_process,
const std::vector<Parameter> & parameter_overrides,
bool start_parameter_services,
bool start_parameter_event_publisher,
const rmw_qos_profile_t & parameter_event_qos_profile);
const rclcpp::QoS & parameter_event_qos,
const rclcpp::PublisherOptionsBase & parameter_event_publisher_options,
bool allow_undeclared_parameters,
bool automatically_declare_parameters_from_overrides);
RCLCPP_PUBLIC
virtual
~NodeParameters();
RCLCPP_PUBLIC
virtual
const rclcpp::ParameterValue &
declare_parameter(
const std::string & name,
const rclcpp::ParameterValue & default_value,
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
bool ignore_override) override;
RCLCPP_PUBLIC
void
undeclare_parameter(const std::string & name) override;
RCLCPP_PUBLIC
bool
has_parameter(const std::string & name) const override;
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(
const std::vector<rclcpp::Parameter> & parameters);
const std::vector<rclcpp::Parameter> & parameters) override;
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(
const std::vector<rclcpp::Parameter> & parameters);
const std::vector<rclcpp::Parameter> & parameters) override;
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::Parameter>
get_parameters(const std::vector<std::string> & names) const;
get_parameters(const std::vector<std::string> & names) const override;
RCLCPP_PUBLIC
virtual
rclcpp::Parameter
get_parameter(const std::string & name) const;
get_parameter(const std::string & name) const override;
RCLCPP_PUBLIC
virtual
bool
get_parameter(
const std::string & name,
rclcpp::Parameter & parameter) const;
rclcpp::Parameter & parameter) const override;
RCLCPP_PUBLIC
virtual
bool
get_parameters_by_prefix(
const std::string & prefix,
std::map<std::string, rclcpp::Parameter> & parameters) const;
std::map<std::string, rclcpp::Parameter> & parameters) const override;
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const;
describe_parameters(const std::vector<std::string> & names) const override;
RCLCPP_PUBLIC
virtual
std::vector<uint8_t>
get_parameter_types(const std::vector<std::string> & names) const;
get_parameter_types(const std::vector<std::string> & names) const override;
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const;
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const override;
RCLCPP_PUBLIC
OnSetParametersCallbackHandle::SharedPtr
add_on_set_parameters_callback(OnParametersSetCallbackType callback) override;
RCLCPP_PUBLIC
virtual
void
register_param_change_callback(ParametersCallbackFunction callback);
remove_on_set_parameters_callback(const OnSetParametersCallbackHandle * const handler) override;
RCLCPP_PUBLIC
OnParametersSetCallbackType
set_on_parameters_set_callback(OnParametersSetCallbackType callback) override;
RCLCPP_PUBLIC
const std::map<std::string, rclcpp::ParameterValue> &
get_parameter_overrides() const override;
using CallbacksContainerType = std::list<OnSetParametersCallbackHandle::WeakPtr>;
private:
RCLCPP_DISABLE_COPY(NodeParameters)
mutable std::mutex mutex_;
mutable std::recursive_mutex mutex_;
ParametersCallbackFunction parameters_callback_ = nullptr;
// There are times when we don't want to allow modifications to parameters
// (particularly when a set_parameter callback tries to call set_parameter,
// declare_parameter, etc). In those cases, this will be set to false.
bool parameter_modification_enabled_{true};
std::map<std::string, rclcpp::Parameter> parameters_;
OnParametersSetCallbackType on_parameters_set_callback_ = nullptr;
CallbacksContainerType on_parameters_set_callback_container_;
std::map<std::string, ParameterInfo> parameters_;
std::map<std::string, rclcpp::ParameterValue> parameter_overrides_;
bool allow_undeclared_ = false;
Publisher<rcl_interfaces::msg::ParameterEvent>::SharedPtr events_publisher_;
@@ -133,6 +202,7 @@ private:
std::string combined_name_;
node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
node_interfaces::NodeClockInterface::SharedPtr node_clock_;
};

View File

@@ -16,6 +16,7 @@
#define RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_
#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -32,6 +33,18 @@ namespace rclcpp
namespace node_interfaces
{
struct OnSetParametersCallbackHandle
{
RCLCPP_SMART_PTR_DEFINITIONS(OnSetParametersCallbackHandle)
using OnParametersSetCallbackType =
std::function<
rcl_interfaces::msg::SetParametersResult(
const std::vector<rclcpp::Parameter> &)>;
OnParametersSetCallbackType callback;
};
/// Pure virtual interface class for the NodeParameters part of the Node API.
class NodeParametersInterface
{
@@ -42,12 +55,51 @@ public:
virtual
~NodeParametersInterface() = default;
/// Declare and initialize a parameter.
/**
* \sa rclcpp::Node::declare_parameter
*/
RCLCPP_PUBLIC
virtual
const rclcpp::ParameterValue &
declare_parameter(
const std::string & name,
const rclcpp::ParameterValue & default_value = rclcpp::ParameterValue(),
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor =
rcl_interfaces::msg::ParameterDescriptor(),
bool ignore_override = false) = 0;
/// Undeclare a parameter.
/**
* \sa rclcpp::Node::undeclare_parameter
*/
RCLCPP_PUBLIC
virtual
void
undeclare_parameter(const std::string & name) = 0;
/// Return true if the parameter has been declared, otherwise false.
/**
* \sa rclcpp::Node::has_parameter
*/
RCLCPP_PUBLIC
virtual
bool
has_parameter(const std::string & name) const = 0;
/// Set one or more parameters, one at a time.
/**
* \sa rclcpp::Node::set_parameters
*/
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(
const std::vector<rclcpp::Parameter> & parameters) = 0;
set_parameters(const std::vector<rclcpp::Parameter> & parameters) = 0;
/// Set and initialize a parameter, all at once.
/**
* \sa rclcpp::Node::set_parameters_atomically
*/
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::SetParametersResult
@@ -119,14 +171,40 @@ public:
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const = 0;
using ParametersCallbackFunction = std::function<
rcl_interfaces::msg::SetParametersResult(
const std::vector<rclcpp::Parameter> &)>;
using OnParametersSetCallbackType = OnSetParametersCallbackHandle::OnParametersSetCallbackType;
/// Add a callback for when parameters are being set.
/**
* \sa rclcpp::Node::add_on_set_parameters_callback
*/
RCLCPP_PUBLIC
virtual
OnSetParametersCallbackHandle::SharedPtr
add_on_set_parameters_callback(OnParametersSetCallbackType callback) = 0;
/// Remove a callback registered with `add_on_set_parameters_callback`.
/**
* \sa rclcpp::Node::remove_on_set_parameters_callback
*/
RCLCPP_PUBLIC
virtual
void
register_param_change_callback(ParametersCallbackFunction callback) = 0;
remove_on_set_parameters_callback(const OnSetParametersCallbackHandle * const handler) = 0;
/// Register a callback for when parameters are being set, return an existing one.
/**
* \sa rclcpp::Node::set_on_parameters_set_callback
*/
RCLCPP_PUBLIC
virtual
OnParametersSetCallbackType
set_on_parameters_set_callback(OnParametersSetCallbackType callback) = 0;
/// Return the initial parameter values used by the NodeParameters to override default values.
RCLCPP_PUBLIC
virtual
const std::map<std::string, rclcpp::ParameterValue> &
get_parameter_overrides() const = 0;
};
} // namespace node_interfaces

View File

@@ -42,44 +42,42 @@ public:
explicit NodeTopics(rclcpp::node_interfaces::NodeBaseInterface * node_base);
RCLCPP_PUBLIC
virtual
~NodeTopics();
~NodeTopics() override;
RCLCPP_PUBLIC
virtual
rclcpp::PublisherBase::SharedPtr
create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
bool use_intra_process);
const rclcpp::QoS & qos) override;
RCLCPP_PUBLIC
virtual
void
add_publisher(
rclcpp::PublisherBase::SharedPtr publisher);
rclcpp::PublisherBase::SharedPtr publisher,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) override;
RCLCPP_PUBLIC
virtual
rclcpp::SubscriptionBase::SharedPtr
create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
bool use_intra_process);
const rclcpp::QoS & qos) override;
RCLCPP_PUBLIC
virtual
void
add_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group);
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) override;
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface() const override;
private:
RCLCPP_DISABLE_COPY(NodeTopics)
NodeBaseInterface * node_base_;
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
};
} // namespace node_interfaces

View File

@@ -50,14 +50,14 @@ public:
create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
bool use_intra_process) = 0;
const rclcpp::QoS & qos) = 0;
RCLCPP_PUBLIC
virtual
void
add_publisher(
rclcpp::PublisherBase::SharedPtr publisher) = 0;
rclcpp::PublisherBase::SharedPtr publisher,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) = 0;
RCLCPP_PUBLIC
virtual
@@ -65,8 +65,7 @@ public:
create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
bool use_intra_process) = 0;
const rclcpp::QoS & qos) = 0;
RCLCPP_PUBLIC
virtual
@@ -74,6 +73,11 @@ public:
add_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) = 0;
RCLCPP_PUBLIC
virtual
rclcpp::node_interfaces::NodeBaseInterface *
get_node_base_interface() const = 0;
};
} // namespace node_interfaces

View File

@@ -23,6 +23,8 @@
#include "rclcpp/context.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -38,12 +40,16 @@ public:
*
* - context = rclcpp::contexts::default_context::get_global_default_context()
* - arguments = {}
* - initial_parameters = {}
* - parameter_overrides = {}
* - use_global_arguments = true
* - use_intra_process_comms = false
* - start_parameter_services = true
* - start_parameter_event_publisher = true
* - parameter_event_qos_profile = rmw_qos_profile_parameter_events
* - parameter_event_qos = rclcpp::ParameterEventQoS
* - with history setting and depth from rmw_qos_profile_parameter_events
* - parameter_event_publisher_options = rclcpp::PublisherOptionsBase
* - allow_undeclared_parameters = false
* - automatically_declare_parameters_from_overrides = false
* - allocator = rcl_get_default_allocator()
*
* \param[in] allocator allocator to use in construction of NodeOptions.
@@ -93,7 +99,7 @@ public:
/// Set the arguments, return this for parameter idiom.
/**
* These arguments are used to extract remappings used by the node and other
* settings.
* ROS specific settings, as well as user defined non-ROS arguments.
*
* This will cause the internal rcl_node_options_t struct to be invalidated.
*/
@@ -101,37 +107,37 @@ public:
NodeOptions &
arguments(const std::vector<std::string> & arguments);
/// Return a reference to the list of initial parameters.
/// Return a reference to the list of parameter overrides.
RCLCPP_PUBLIC
std::vector<rclcpp::Parameter> &
initial_parameters();
parameter_overrides();
RCLCPP_PUBLIC
const std::vector<rclcpp::Parameter> &
initial_parameters() const;
parameter_overrides() const;
/// Set the initial parameters, return this for parameter idiom.
/// Set the parameters overrides, return this for parameter idiom.
/**
* These initial parameter values are used to change the initial value
* These parameter overrides are used to change the initial value
* of declared parameters within the node, overriding hard coded default
* values if necessary.
*/
RCLCPP_PUBLIC
NodeOptions &
initial_parameters(const std::vector<rclcpp::Parameter> & initial_parameters);
parameter_overrides(const std::vector<rclcpp::Parameter> & parameter_overrides);
/// Append a single initial parameter, parameter idiom style.
/// Append a single parameter override, parameter idiom style.
template<typename ParameterT>
NodeOptions &
append_initial_parameter(const std::string & name, const ParameterT & value)
append_parameter_override(const std::string & name, const ParameterT & value)
{
this->initial_parameters().emplace_back(name, rclcpp::ParameterValue(value));
this->parameter_overrides().emplace_back(name, rclcpp::ParameterValue(value));
return *this;
}
/// Return a reference to the use_global_arguments flag.
/// Return the use_global_arguments flag.
RCLCPP_PUBLIC
const bool &
bool
use_global_arguments() const;
/// Set the use_global_arguments flag, return this for parameter idiom.
@@ -144,11 +150,11 @@ public:
*/
RCLCPP_PUBLIC
NodeOptions &
use_global_arguments(const bool & use_global_arguments);
use_global_arguments(bool use_global_arguments);
/// Return a reference to the use_intra_process_comms flag
/// Return the use_intra_process_comms flag.
RCLCPP_PUBLIC
const bool &
bool
use_intra_process_comms() const;
/// Set the use_intra_process_comms flag, return this for parameter idiom.
@@ -163,11 +169,11 @@ public:
*/
RCLCPP_PUBLIC
NodeOptions &
use_intra_process_comms(const bool & use_intra_process_comms);
use_intra_process_comms(bool use_intra_process_comms);
/// Return a reference to the start_parameter_services flag
/// Return the start_parameter_services flag.
RCLCPP_PUBLIC
const bool &
bool
start_parameter_services() const;
/// Set the start_parameter_services flag, return this for parameter idiom.
@@ -182,11 +188,11 @@ public:
*/
RCLCPP_PUBLIC
NodeOptions &
start_parameter_services(const bool & start_parameter_services);
start_parameter_services(bool start_parameter_services);
/// Return a reference to the start_parameter_event_publisher flag.
/// Return the start_parameter_event_publisher flag.
RCLCPP_PUBLIC
const bool &
bool
start_parameter_event_publisher() const;
/// Set the start_parameter_event_publisher flag, return this for parameter idiom.
@@ -198,20 +204,78 @@ public:
*/
RCLCPP_PUBLIC
NodeOptions &
start_parameter_event_publisher(const bool & start_parameter_event_publisher);
start_parameter_event_publisher(bool start_parameter_event_publisher);
/// Return a reference to the parameter_event_qos_profile QoS.
/// Return a reference to the parameter_event_qos QoS.
RCLCPP_PUBLIC
const rmw_qos_profile_t &
parameter_event_qos_profile() const;
const rclcpp::QoS &
parameter_event_qos() const;
/// Set the parameter_event_qos_profile QoS, return this for parameter idiom.
/// Set the parameter_event_qos QoS, return this for parameter idiom.
/**
* The QoS settings to be used for the parameter event publisher, if enabled.
*/
RCLCPP_PUBLIC
NodeOptions &
parameter_event_qos_profile(const rmw_qos_profile_t & parameter_event_qos_profile);
parameter_event_qos(const rclcpp::QoS & parameter_event_qos);
/// Return a reference to the parameter_event_publisher_options.
RCLCPP_PUBLIC
const rclcpp::PublisherOptionsBase &
parameter_event_publisher_options() const;
/// Set the parameter_event_publisher_options, return this for parameter idiom.
/**
* The QoS settings to be used for the parameter event publisher, if enabled.
*
* \todo(wjwwood): make this take/store an instance of
* rclcpp::PublisherOptionsWithAllocator<Allocator>, but to do that requires
* NodeOptions to also be templated based on the Allocator type.
*/
RCLCPP_PUBLIC
NodeOptions &
parameter_event_publisher_options(
const rclcpp::PublisherOptionsBase & parameter_event_publisher_options);
/// Return the allow_undeclared_parameters flag.
RCLCPP_PUBLIC
bool
allow_undeclared_parameters() const;
/// Set the allow_undeclared_parameters, return this for parameter idiom.
/**
* If true, allow any parameter name to be set on the node without first
* being declared.
* Otherwise, setting an undeclared parameter will raise an exception.
*
* This option being true does not affect parameter_overrides, as the first
* set action will implicitly declare the parameter and therefore consider
* any parameter overrides.
*/
RCLCPP_PUBLIC
NodeOptions &
allow_undeclared_parameters(bool allow_undeclared_parameters);
/// Return the automatically_declare_parameters_from_overrides flag.
RCLCPP_PUBLIC
bool
automatically_declare_parameters_from_overrides() const;
/// Set the automatically_declare_parameters_from_overrides, return this.
/**
* If true, automatically iterate through the node's parameter overrides and
* implicitly declare any that have not already been declared.
* Otherwise, parameters passed to the node's parameter_overrides, and/or the
* global arguments (e.g. parameter overrides from a YAML file), which are
* not explicitly declared will not appear on the node at all, even if
* `allow_undeclared_parameters` is true.
* Already declared parameters will not be re-declared, and parameters
* declared in this way will use the default constructed ParameterDescriptor.
*/
RCLCPP_PUBLIC
NodeOptions &
automatically_declare_parameters_from_overrides(
bool automatically_declare_parameters_from_overrides);
/// Return the rcl_allocator_t to be used.
RCLCPP_PUBLIC
@@ -244,7 +308,7 @@ private:
std::vector<std::string> arguments_ {};
std::vector<rclcpp::Parameter> initial_parameters_ {};
std::vector<rclcpp::Parameter> parameter_overrides_ {};
bool use_global_arguments_ {true};
@@ -254,7 +318,15 @@ private:
bool start_parameter_event_publisher_ {true};
rmw_qos_profile_t parameter_event_qos_profile_ {rmw_qos_profile_parameter_events};
rclcpp::QoS parameter_event_qos_ = rclcpp::ParameterEventsQoS(
rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)
);
rclcpp::PublisherOptionsBase parameter_event_publisher_options_ = rclcpp::PublisherOptionsBase();
bool allow_undeclared_parameters_ {false};
bool automatically_declare_parameters_from_overrides_ {false};
rcl_allocator_t allocator_ {rcl_get_default_allocator()};
};

View File

@@ -28,21 +28,59 @@
namespace rclcpp
{
class Parameter;
namespace node_interfaces
{
struct ParameterInfo;
} // namespace node_interfaces
namespace detail
{
// This helper function is required because you cannot do specialization on a
// class method, so instead we specialize this template function and call it
// from the unspecialized, but dependent, class method.
template<typename T>
auto
get_value_helper(const rclcpp::Parameter * parameter);
} // namespace detail
/// Structure to store an arbitrary parameter with templated get/set methods.
class Parameter
{
public:
/// Construct with an empty name and a parameter value of type rclcpp::PARAMETER_NOT_SET.
RCLCPP_PUBLIC
Parameter();
/// Construct with given name and a parameter value of type rclcpp::PARAMETER_NOT_SET.
RCLCPP_PUBLIC
explicit Parameter(const std::string & name);
/// Construct with given name and given parameter value.
RCLCPP_PUBLIC
Parameter(const std::string & name, const ParameterValue & value);
/// Construct with given name and given parameter value.
template<typename ValueTypeT>
explicit Parameter(const std::string & name, ValueTypeT value)
Parameter(const std::string & name, ValueTypeT value)
: Parameter(name, ParameterValue(value))
{
}
{}
RCLCPP_PUBLIC
explicit Parameter(const rclcpp::node_interfaces::ParameterInfo & parameter_info);
/// Equal operator.
RCLCPP_PUBLIC
bool
operator==(const Parameter & rhs) const;
/// Not equal operator.
RCLCPP_PUBLIC
bool
operator!=(const Parameter & rhs) const;
RCLCPP_PUBLIC
ParameterType
@@ -60,6 +98,11 @@ public:
rcl_interfaces::msg::ParameterValue
get_value_message() const;
/// Get the internal storage for the parameter value.
RCLCPP_PUBLIC
const rclcpp::ParameterValue &
get_parameter_value() const;
/// Get value of parameter using rclcpp::ParameterType as template argument.
template<ParameterType ParamT>
decltype(auto)
@@ -71,10 +114,7 @@ public:
/// Get value of parameter using c++ types as template argument.
template<typename T>
decltype(auto)
get_value() const
{
return value_.get<T>();
}
get_value() const;
RCLCPP_PUBLIC
bool
@@ -142,6 +182,49 @@ RCLCPP_PUBLIC
std::ostream &
operator<<(std::ostream & os, const std::vector<Parameter> & parameters);
namespace detail
{
template<typename T>
auto
get_value_helper(const rclcpp::Parameter * parameter)
{
return parameter->get_parameter_value().get<T>();
}
// Specialization allowing Parameter::get() to return a const ref to the parameter value object.
template<>
inline
auto
get_value_helper<rclcpp::ParameterValue>(const rclcpp::Parameter * parameter)
{
return parameter->get_parameter_value();
}
// Specialization allowing Parameter::get() to return a const ref to the parameter itself.
template<>
inline
auto
get_value_helper<rclcpp::Parameter>(const rclcpp::Parameter * parameter)
{
// Use this lambda to ensure it's a const reference being returned (and not a copy).
auto type_enforcing_lambda =
[&parameter]() -> const rclcpp::Parameter & {
return *parameter;
};
return type_enforcing_lambda();
}
} // namespace detail
template<typename T>
decltype(auto)
Parameter::get_value() const
{
// use the helper to specialize for the ParameterValue and Parameter cases.
return detail::get_value_helper<T>(this);
}
} // namespace rclcpp
namespace std

View File

@@ -53,19 +53,22 @@ public:
const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
RCLCPP_PUBLIC
AsyncParametersClient(
const rclcpp::Node::SharedPtr node,
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
RCLCPP_PUBLIC
AsyncParametersClient(
rclcpp::Node * node,
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
RCLCPP_PUBLIC
std::shared_future<std::vector<rclcpp::Parameter>>
@@ -110,28 +113,50 @@ public:
template<
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT =
rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent, Alloc>>
typename AllocatorT = std::allocator<void>>
typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
on_parameter_event(CallbackT && callback)
on_parameter_event(
CallbackT && callback,
const rclcpp::QoS & qos = (
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events))
),
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
))
{
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
auto msg_mem_strat =
MessageMemoryStrategy<rcl_interfaces::msg::ParameterEvent, Alloc>::create_default();
return this->on_parameter_event(
this->node_topics_interface_,
callback,
qos,
options);
}
using rcl_interfaces::msg::ParameterEvent;
return rclcpp::create_subscription<
ParameterEvent, CallbackT, Alloc, ParameterEvent, SubscriptionT>(
this->node_topics_interface_.get(),
/**
* The NodeT type only needs to have a method called get_node_topics_interface()
* which returns a shared_ptr to a NodeTopicsInterface, or be a
* NodeTopicsInterface pointer itself.
*/
template<
typename CallbackT,
typename NodeT,
typename AllocatorT = std::allocator<void>>
static typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
on_parameter_event(
NodeT && node,
CallbackT && callback,
const rclcpp::QoS & qos = (
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events))
),
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
))
{
return rclcpp::create_subscription<rcl_interfaces::msg::ParameterEvent>(
node,
"parameter_events",
qos,
std::forward<CallbackT>(callback),
rmw_qos_profile_default,
nullptr, // group,
false, // ignore_local_publications,
false, // use_intra_process_comms_,
msg_mem_strat,
std::make_shared<Alloc>());
options);
}
RCLCPP_PUBLIC
@@ -154,7 +179,7 @@ protected:
wait_for_service_nanoseconds(std::chrono::nanoseconds timeout);
private:
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface_;
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface_;
rclcpp::Client<rcl_interfaces::srv::GetParameters>::SharedPtr get_parameters_client_;
rclcpp::Client<rcl_interfaces::srv::GetParameterTypes>::SharedPtr
get_parameter_types_client_;
@@ -246,7 +271,11 @@ public:
{
return get_parameter_impl(
parameter_name,
std::function<T()>([]() -> T {throw std::runtime_error("Parameter not set");}));
std::function<T()>([&parameter_name]() -> T
{
throw std::runtime_error("Parameter '" + parameter_name + "' is not set");
})
);
}
RCLCPP_PUBLIC
@@ -271,7 +300,26 @@ public:
typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
on_parameter_event(CallbackT && callback)
{
return async_parameters_client_->on_parameter_event(std::forward<CallbackT>(callback));
return async_parameters_client_->on_parameter_event(
std::forward<CallbackT>(callback));
}
/**
* The NodeT type only needs to have a method called get_node_topics_interface()
* which returns a shared_ptr to a NodeTopicsInterface, or be a
* NodeTopicsInterface pointer itself.
*/
template<
typename CallbackT,
typename NodeT>
static typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
on_parameter_event(
NodeT && node,
CallbackT && callback)
{
return AsyncParametersClient::on_parameter_event(
node,
std::forward<CallbackT>(callback));
}
RCLCPP_PUBLIC

View File

@@ -44,16 +44,20 @@ public:
* \param[in] event The parameter event message to filter.
* \param[in] names A list of parameter names of interest.
* \param[in] types A list of the types of parameter events of iterest.
* EventType NEW, DELETED, or CHANGED
*
* Example Usage:
* If you have recieved a parameter event and are only interested in parameters foo and
* bar being added or changed but don't care about deletion.
* auto res = rclcpp::ParameterEventsFilter(
* event_shared_ptr,
* {"foo", "bar"},
* {rclcpp::ParameterEventsFilter::EventType::NEW, rclcpp::ParameterEventsFilter::EventType::CHANGED});
*/
* EventType NEW, DELETED, or CHANGED
*
* Example Usage:
*
* If you have recieved a parameter event and are only interested in parameters foo and
* bar being added or changed but don't care about deletion.
*
* ```cpp
* auto res = rclcpp::ParameterEventsFilter(
* event_shared_ptr,
* {"foo", "bar"},
* {rclcpp::ParameterEventsFilter::EventType::NEW, rclcpp::ParameterEventsFilter::EventType::CHANGED});
* ```
*/
RCLCPP_PUBLIC
ParameterEventsFilter(
rcl_interfaces::msg::ParameterEvent::SharedPtr event,

View File

@@ -43,7 +43,7 @@ public:
explicit ParameterService(
const std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
const std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
node_interfaces::NodeParametersInterface * node_params,
rclcpp::node_interfaces::NodeParametersInterface * node_params,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
private:

View File

@@ -28,7 +28,8 @@
namespace rclcpp
{
enum ParameterType
enum ParameterType : uint8_t
{
PARAMETER_NOT_SET = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET,
PARAMETER_BOOL = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
@@ -45,11 +46,11 @@ enum ParameterType
/// Return the name of a parameter type
RCLCPP_PUBLIC
std::string
to_string(const ParameterType type);
to_string(ParameterType type);
RCLCPP_PUBLIC
std::ostream &
operator<<(std::ostream & os, const ParameterType type);
operator<<(std::ostream & os, ParameterType type);
/// Indicate the parameter type does not match the expected type.
class ParameterTypeException : public std::runtime_error
@@ -129,10 +130,21 @@ public:
rcl_interfaces::msg::ParameterValue
to_value_msg() const;
/// Equal operator.
RCLCPP_PUBLIC
bool
operator==(const ParameterValue & rhs) const;
/// Not equal operator.
RCLCPP_PUBLIC
bool
operator!=(const ParameterValue & rhs) const;
// The following get() variants require the use of ParameterType
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_BOOL, bool>::type
constexpr
typename std::enable_if<type == ParameterType::PARAMETER_BOOL, const bool &>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_BOOL) {
@@ -142,7 +154,8 @@ public:
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_INTEGER, int64_t>::type
constexpr
typename std::enable_if<type == ParameterType::PARAMETER_INTEGER, const int64_t &>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER) {
@@ -152,7 +165,8 @@ public:
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_DOUBLE, double>::type
constexpr
typename std::enable_if<type == ParameterType::PARAMETER_DOUBLE, const double &>::type
get() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
@@ -162,6 +176,7 @@ public:
}
template<ParameterType type>
constexpr
typename std::enable_if<type == ParameterType::PARAMETER_STRING, const std::string &>::type
get() const
{
@@ -172,6 +187,7 @@ public:
}
template<ParameterType type>
constexpr
typename std::enable_if<
type == ParameterType::PARAMETER_BYTE_ARRAY, const std::vector<uint8_t> &>::type
get() const
@@ -183,6 +199,7 @@ public:
}
template<ParameterType type>
constexpr
typename std::enable_if<
type == ParameterType::PARAMETER_BOOL_ARRAY, const std::vector<bool> &>::type
get() const
@@ -194,6 +211,7 @@ public:
}
template<ParameterType type>
constexpr
typename std::enable_if<
type == ParameterType::PARAMETER_INTEGER_ARRAY, const std::vector<int64_t> &>::type
get() const
@@ -205,6 +223,7 @@ public:
}
template<ParameterType type>
constexpr
typename std::enable_if<
type == ParameterType::PARAMETER_DOUBLE_ARRAY, const std::vector<double> &>::type
get() const
@@ -216,6 +235,7 @@ public:
}
template<ParameterType type>
constexpr
typename std::enable_if<
type == ParameterType::PARAMETER_STRING_ARRAY, const std::vector<std::string> &>::type
get() const
@@ -229,28 +249,32 @@ public:
// The following get() variants allow the use of primitive types
template<typename type>
typename std::enable_if<std::is_same<type, bool>::value, bool>::type
constexpr
typename std::enable_if<std::is_same<type, bool>::value, const bool &>::type
get() const
{
return get<ParameterType::PARAMETER_BOOL>();
}
template<typename type>
constexpr
typename std::enable_if<
std::is_integral<type>::value && !std::is_same<type, bool>::value, int64_t>::type
std::is_integral<type>::value && !std::is_same<type, bool>::value, const int64_t &>::type
get() const
{
return get<ParameterType::PARAMETER_INTEGER>();
}
template<typename type>
typename std::enable_if<std::is_floating_point<type>::value, double>::type
constexpr
typename std::enable_if<std::is_floating_point<type>::value, const double &>::type
get() const
{
return get<ParameterType::PARAMETER_DOUBLE>();
}
template<typename type>
constexpr
typename std::enable_if<std::is_convertible<type, std::string>::value, const std::string &>::type
get() const
{
@@ -258,6 +282,7 @@ public:
}
template<typename type>
constexpr
typename std::enable_if<
std::is_convertible<
type, const std::vector<uint8_t> &>::value, const std::vector<uint8_t> &>::type
@@ -267,6 +292,7 @@ public:
}
template<typename type>
constexpr
typename std::enable_if<
std::is_convertible<
type, const std::vector<bool> &>::value, const std::vector<bool> &>::type
@@ -276,6 +302,7 @@ public:
}
template<typename type>
constexpr
typename std::enable_if<
std::is_convertible<
type, const std::vector<int64_t> &>::value, const std::vector<int64_t> &>::type
@@ -285,6 +312,7 @@ public:
}
template<typename type>
constexpr
typename std::enable_if<
std::is_convertible<
type, const std::vector<double> &>::value, const std::vector<double> &>::type
@@ -294,6 +322,7 @@ public:
}
template<typename type>
constexpr
typename std::enable_if<
std::is_convertible<
type, const std::vector<std::string> &>::value, const std::vector<std::string> &>::type

View File

@@ -23,344 +23,232 @@
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include "rcl/error_handling.h"
#include "rcl/publisher.h"
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/detail/resolve_use_intra_process.hpp"
#include "rclcpp/experimental/intra_process_manager.hpp"
#include "rclcpp/loaned_message.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
// Forward declaration is used for friend statement.
namespace node_interfaces
{
class NodeTopicsInterface;
}
namespace intra_process_manager
{
/**
* NOTE(ivanpauno): IntraProcessManager is forward declared here, avoiding a circular inclusion between intra_process_manager.hpp and publisher.hpp.
* SharedPtr and WeakPtr of the IntraProcessManager are defined again here, to avoid a warning for accessing a member of a forward declared class.
*/
class IntraProcessManager;
}
class PublisherBase
{
friend ::rclcpp::node_interfaces::NodeTopicsInterface;
public:
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
/// Default constructor.
/**
* Typically, a publisher is not created through this method, but instead is created through a
* call to `Node::create_publisher`.
* \param[in] node_base A pointer to the NodeBaseInterface for the parent node.
* \param[in] topic The topic that this publisher publishes on.
* \param[in] type_support The type support structure for the type to be published.
* \param[in] publisher_options QoS settings for this publisher.
*/
RCLCPP_PUBLIC
PublisherBase(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rosidl_message_type_support_t & type_support,
const rcl_publisher_options_t & publisher_options);
RCLCPP_PUBLIC
virtual ~PublisherBase();
/// Get the topic that this publisher publishes on.
/** \return The topic name. */
RCLCPP_PUBLIC
const char *
get_topic_name() const;
/// Get the queue size for this publisher.
/** \return The queue size. */
RCLCPP_PUBLIC
size_t
get_queue_size() const;
/// Get the global identifier for this publisher (used in rmw and by DDS).
/** \return The gid. */
RCLCPP_PUBLIC
const rmw_gid_t &
get_gid() const;
/// Get the global identifier for this publisher used by intra-process communication.
/** \return The intra-process gid. */
RCLCPP_PUBLIC
const rmw_gid_t &
get_intra_process_gid() const;
/// Get the rcl publisher handle.
/** \return The rcl publisher handle. */
RCLCPP_PUBLIC
rcl_publisher_t *
get_publisher_handle();
/// Get the rcl publisher handle.
/** \return The rcl publisher handle. */
RCLCPP_PUBLIC
const rcl_publisher_t *
get_publisher_handle() const;
/// Get subscription count
/** \return The number of subscriptions. */
RCLCPP_PUBLIC
size_t
get_subscription_count() const;
/// Get intraprocess subscription count
/** \return The number of intraprocess subscriptions. */
RCLCPP_PUBLIC
size_t
get_intra_process_subscription_count() const;
/// Get the actual QoS settings, after the defaults have been determined.
/**
* The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
* can only be resolved after the creation of the publisher, and it
* depends on the underlying rmw implementation.
* If the underlying setting in use can't be represented in ROS terms,
* it will be set to RMW_QOS_POLICY_*_UNKNOWN.
* May throw runtime_error when an unexpected error occurs.
* \return The actual qos settings.
*/
RCLCPP_PUBLIC
rmw_qos_profile_t
get_actual_qos() const;
/// Compare this publisher to a gid.
/**
* Note that this function calls the next function.
* \param[in] gid Reference to a gid.
* \return True if the publisher's gid matches the input.
*/
RCLCPP_PUBLIC
bool
operator==(const rmw_gid_t & gid) const;
/// Compare this publisher to a pointer gid.
/**
* A wrapper for comparing this publisher's gid to the input using rmw_compare_gids_equal.
* \param[in] gid A pointer to a gid.
* \return True if this publisher's gid matches the input.
*/
RCLCPP_PUBLIC
bool
operator==(const rmw_gid_t * gid) const;
using StoreMessageCallbackT = std::function<uint64_t(uint64_t, void *, const std::type_info &)>;
using IntraProcessManagerSharedPtr =
std::shared_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
/// Implementation utility function used to setup intra process publishing after creation.
RCLCPP_PUBLIC
void
setup_intra_process(
uint64_t intra_process_publisher_id,
StoreMessageCallbackT store_callback,
IntraProcessManagerSharedPtr ipm,
const rcl_publisher_options_t & intra_process_options);
protected:
std::shared_ptr<rcl_node_t> rcl_node_handle_;
rcl_publisher_t publisher_handle_ = rcl_get_zero_initialized_publisher();
rcl_publisher_t intra_process_publisher_handle_ = rcl_get_zero_initialized_publisher();
using IntraProcessManagerWeakPtr =
std::weak_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
bool intra_process_is_enabled_;
IntraProcessManagerWeakPtr weak_ipm_;
uint64_t intra_process_publisher_id_;
StoreMessageCallbackT store_intra_process_message_;
rmw_gid_t rmw_gid_;
rmw_gid_t intra_process_rmw_gid_;
};
template<typename MessageT, typename AllocatorT>
class LoanedMessage;
/// A publisher publishes messages of any type to a topic.
template<typename MessageT, typename Alloc = std::allocator<void>>
template<typename MessageT, typename AllocatorT = std::allocator<void>>
class Publisher : public PublisherBase
{
public:
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using MessageAllocatorTraits = allocator::AllocRebind<MessageT, AllocatorT>;
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAllocator, MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
using MessageSharedPtr = std::shared_ptr<const MessageT>;
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, Alloc>)
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, AllocatorT>)
Publisher(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rcl_publisher_options_t & publisher_options,
const std::shared_ptr<MessageAlloc> & allocator)
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
: PublisherBase(
node_base,
topic,
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
publisher_options),
message_allocator_(allocator)
options.template to_rcl_publisher_options<MessageT>(qos)),
options_(options),
message_allocator_(new MessageAllocator(*options.get_allocator().get()))
{
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
if (options_.event_callbacks.deadline_callback) {
this->add_event_handler(
options_.event_callbacks.deadline_callback,
RCL_PUBLISHER_OFFERED_DEADLINE_MISSED);
}
if (options_.event_callbacks.liveliness_callback) {
this->add_event_handler(
options_.event_callbacks.liveliness_callback,
RCL_PUBLISHER_LIVELINESS_LOST);
}
// Setup continues in the post construction method, post_init_setup().
}
/// Called post construction, so that construction may continue after shared_from_this() works.
virtual
void
post_init_setup(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
{
// Topic is unused for now.
(void)topic;
(void)options;
// If needed, setup intra process communication.
if (rclcpp::detail::resolve_use_intra_process(options_, *node_base)) {
auto context = node_base->get_context();
// Get the intra process manager instance for this context.
auto ipm = context->get_sub_context<rclcpp::experimental::IntraProcessManager>();
// Register the publisher with the intra process manager.
if (qos.get_rmw_qos_profile().history == RMW_QOS_POLICY_HISTORY_KEEP_ALL) {
throw std::invalid_argument(
"intraprocess communication is not allowed with keep all history qos policy");
}
if (qos.get_rmw_qos_profile().depth == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with a zero qos history depth value");
}
if (qos.get_rmw_qos_profile().durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
throw std::invalid_argument(
"intraprocess communication allowed only with volatile durability");
}
uint64_t intra_process_publisher_id = ipm->add_publisher(this->shared_from_this());
this->setup_intra_process(
intra_process_publisher_id,
ipm);
}
}
virtual ~Publisher()
{}
/// Borrow a loaned ROS message from the middleware.
/**
* If the middleware is capable of loaning memory for a ROS message instance,
* the loaned message will be directly allocated in the middleware.
* If not, the message allocator of this rclcpp::Publisher instance is being used.
*
* With a call to \sa `publish` the LoanedMessage instance is being returned to the middleware
* or free'd accordingly to the allocator.
* If the message is not being published but processed differently, the destructor of this
* class will either return the message to the middleware or deallocate it via the internal
* allocator.
* \sa rclcpp::LoanedMessage for details of the LoanedMessage class.
*
* \return LoanedMessage containing memory for a ROS message of type MessageT
*/
rclcpp::LoanedMessage<MessageT, AllocatorT>
borrow_loaned_message()
{
return rclcpp::LoanedMessage<MessageT, AllocatorT>(this, this->get_allocator());
}
/// Send a message to the topic for this publisher.
/**
* This function is templated on the input message type, MessageT.
* \param[in] msg A shared pointer to the message to send.
*/
virtual void
publish(std::unique_ptr<MessageT, MessageDeleter> & msg)
publish(std::unique_ptr<MessageT, MessageDeleter> msg)
{
bool inter_process_subscriptions_exist =
if (!intra_process_is_enabled_) {
this->do_inter_process_publish(*msg);
return;
}
// If an interprocess subscription exist, then the unique_ptr is promoted
// to a shared_ptr and published.
// This allows doing the intraprocess publish first and then doing the
// interprocess publish, resulting in lower publish-to-subscribe latency.
// It's not possible to do that with an unique_ptr,
// as do_intra_process_publish takes the ownership of the message.
bool inter_process_publish_needed =
get_subscription_count() > get_intra_process_subscription_count();
if (!intra_process_is_enabled_ || inter_process_subscriptions_exist) {
this->do_inter_process_publish(msg.get());
}
if (store_intra_process_message_) {
// Take the pointer from the unique_msg, release it and pass as a void *
// to the ipm. The ipm should then capture it again as a unique_ptr of
// the correct type.
// TODO(wjwwood):
// investigate how to transfer the custom deleter (if there is one)
// from the incoming unique_ptr through to the ipm's unique_ptr.
// See: http://stackoverflow.com/questions/11002641/dynamic-casting-for-unique-ptr
MessageT * msg_ptr = msg.get();
msg.release();
uint64_t message_seq =
store_intra_process_message_(intra_process_publisher_id_, msg_ptr, typeid(MessageT));
rcl_interfaces::msg::IntraProcessMessage ipm;
ipm.publisher_id = intra_process_publisher_id_;
ipm.message_sequence = message_seq;
auto status = rcl_publish(&intra_process_publisher_handle_, &ipm);
if (RCL_RET_PUBLISHER_INVALID == status) {
rcl_reset_error(); // next call will reset error message if not context
if (rcl_publisher_is_valid_except_context(&intra_process_publisher_handle_)) {
rcl_context_t * context = rcl_publisher_get_context(&intra_process_publisher_handle_);
if (nullptr != context && !rcl_context_is_valid(context)) {
// publisher is invalid due to context being shutdown
return;
}
}
}
if (RCL_RET_OK != status) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish intra process message");
}
if (inter_process_publish_needed) {
auto shared_msg = this->do_intra_process_publish_and_return_shared(std::move(msg));
this->do_inter_process_publish(*shared_msg);
} else {
// Always destroy the message, even if we don't consume it, for consistency.
msg.reset();
this->do_intra_process_publish(std::move(msg));
}
}
virtual void
publish(const std::shared_ptr<MessageT> & msg)
{
// Avoid allocating when not using intra process.
if (!store_intra_process_message_) {
// In this case we're not using intra process.
return this->do_inter_process_publish(msg.get());
}
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
// TODO(wjwwood):
// The intra process manager should probably also be able to store
// shared_ptr's and do the "smart" thing based on other intra process
// subscriptions. For now call the other publish().
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
MessageAllocTraits::construct(*message_allocator_.get(), ptr, *msg.get());
MessageUniquePtr unique_msg(ptr, message_deleter_);
return this->publish(unique_msg);
}
virtual void
publish(std::shared_ptr<const MessageT> msg)
{
// Avoid allocating when not using intra process.
if (!store_intra_process_message_) {
// In this case we're not using intra process.
return this->do_inter_process_publish(msg.get());
}
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
// TODO(wjwwood):
// The intra process manager should probably also be able to store
// shared_ptr's and do the "smart" thing based on other intra process
// subscriptions. For now call the other publish().
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
MessageAllocTraits::construct(*message_allocator_.get(), ptr, *msg.get());
MessageUniquePtr unique_msg(ptr, message_deleter_);
return this->publish(unique_msg);
}
virtual void
publish(const MessageT & msg)
{
// Avoid allocating when not using intra process.
if (!store_intra_process_message_) {
if (!intra_process_is_enabled_) {
// In this case we're not using intra process.
return this->do_inter_process_publish(&msg);
return this->do_inter_process_publish(msg);
}
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
auto ptr = MessageAllocTraits::allocate(*message_allocator_.get(), 1);
MessageAllocTraits::construct(*message_allocator_.get(), ptr, msg);
// As the message is not const, a copy should be made.
// A shared_ptr<const MessageT> could also be constructed here.
auto ptr = MessageAllocatorTraits::allocate(*message_allocator_.get(), 1);
MessageAllocatorTraits::construct(*message_allocator_.get(), ptr, msg);
MessageUniquePtr unique_msg(ptr, message_deleter_);
return this->publish(unique_msg);
}
virtual void
publish(const MessageT * msg)
{
if (!msg) {
throw std::runtime_error("msg argument is nullptr");
}
return this->publish(*msg);
this->publish(std::move(unique_msg));
}
void
publish(const rcl_serialized_message_t * serialized_msg)
publish(const rcl_serialized_message_t & serialized_msg)
{
if (store_intra_process_message_) {
// TODO(Karsten1987): support serialized message passed by intraprocess
throw std::runtime_error("storing serialized messages in intra process is not supported yet");
}
auto status = rcl_publish_serialized_message(&publisher_handle_, serialized_msg);
if (RCL_RET_OK != status) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish serialized message");
}
return this->do_serialized_publish(&serialized_msg);
}
/// Publish an instance of a LoanedMessage.
/**
* When publishing a loaned message, the memory for this ROS message will be deallocated
* after being published.
* The instance of the loaned message is no longer valid after this call.
*
* \param loaned_msg The LoanedMessage instance to be published.
*/
void
publish(std::shared_ptr<const rcl_serialized_message_t> serialized_msg)
publish(rclcpp::LoanedMessage<MessageT, AllocatorT> && loaned_msg)
{
return this->publish(serialized_msg.get());
if (!loaned_msg.is_valid()) {
throw std::runtime_error("loaned message is not valid");
}
if (intra_process_is_enabled_) {
// TODO(Karsten1987): support loaned message passed by intraprocess
throw std::runtime_error("storing loaned messages in intra process is not supported yet");
}
// verify that publisher supports loaned messages
// TODO(Karsten1987): This case separation has to be done in rclcpp
// otherwise we have to ensure that every middleware implements
// `rmw_publish_loaned_message` explicitly the same way as `rmw_publish`
// by taking a copy of the ros message.
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(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.
this->do_inter_process_publish(loaned_msg.get());
}
}
std::shared_ptr<MessageAlloc> get_allocator() const
std::shared_ptr<MessageAllocator>
get_allocator() const
{
return message_allocator_;
}
protected:
void
do_inter_process_publish(const MessageT * msg)
do_inter_process_publish(const MessageT & msg)
{
auto status = rcl_publish(&publisher_handle_, msg);
auto status = rcl_publish(&publisher_handle_, &msg, nullptr);
if (RCL_RET_PUBLISHER_INVALID == status) {
rcl_reset_error(); // next call will reset error message if not context
if (rcl_publisher_is_valid_except_context(&publisher_handle_)) {
@@ -376,7 +264,83 @@ protected:
}
}
std::shared_ptr<MessageAlloc> message_allocator_;
void
do_serialized_publish(const rcl_serialized_message_t * serialized_msg)
{
if (intra_process_is_enabled_) {
// TODO(Karsten1987): support serialized message passed by intraprocess
throw std::runtime_error("storing serialized messages in intra process is not supported yet");
}
auto status = rcl_publish_serialized_message(&publisher_handle_, serialized_msg, nullptr);
if (RCL_RET_OK != status) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish serialized message");
}
}
void
do_loaned_message_publish(MessageT * msg)
{
auto status = rcl_publish_loaned_message(&publisher_handle_, msg, nullptr);
if (RCL_RET_PUBLISHER_INVALID == status) {
rcl_reset_error(); // next call will reset error message if not context
if (rcl_publisher_is_valid_except_context(&publisher_handle_)) {
rcl_context_t * context = rcl_publisher_get_context(&publisher_handle_);
if (nullptr != context && !rcl_context_is_valid(context)) {
// publisher is invalid due to context being shutdown
return;
}
}
}
if (RCL_RET_OK != status) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish message");
}
}
void
do_intra_process_publish(std::unique_ptr<MessageT, MessageDeleter> msg)
{
auto ipm = weak_ipm_.lock();
if (!ipm) {
throw std::runtime_error(
"intra process publish called after destruction of intra process manager");
}
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
ipm->template do_intra_process_publish<MessageT, AllocatorT>(
intra_process_publisher_id_,
std::move(msg),
message_allocator_);
}
std::shared_ptr<const MessageT>
do_intra_process_publish_and_return_shared(std::unique_ptr<MessageT, MessageDeleter> msg)
{
auto ipm = weak_ipm_.lock();
if (!ipm) {
throw std::runtime_error(
"intra process publish called after destruction of intra process manager");
}
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
return ipm->template do_intra_process_publish_and_return_shared<MessageT, AllocatorT>(
intra_process_publisher_id_,
std::move(msg),
message_allocator_);
}
/// Copy of original options passed during construction.
/**
* It is important to save a copy of this so that the rmw payload which it
* may contain is kept alive for the duration of the publisher.
*/
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> options_;
std::shared_ptr<MessageAllocator> message_allocator_;
MessageDeleter message_deleter_;
};

View File

@@ -0,0 +1,228 @@
// Copyright 2014 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__PUBLISHER_BASE_HPP_
#define RCLCPP__PUBLISHER_BASE_HPP_
#include <rmw/error_handling.h>
#include <rmw/rmw.h>
#include <functional>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
#include "rcl/publisher.h"
#include "rclcpp/macros.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
// Forward declaration is used for friend statement.
namespace node_interfaces
{
class NodeBaseInterface;
class NodeTopicsInterface;
} // namespace node_interfaces
namespace experimental
{
/**
* IntraProcessManager is forward declared here, avoiding a circular inclusion between
* `intra_process_manager.hpp` and `publisher_base.hpp`.
*/
class IntraProcessManager;
} // namespace experimental
class PublisherBase : public std::enable_shared_from_this<PublisherBase>
{
friend ::rclcpp::node_interfaces::NodeTopicsInterface;
public:
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
/// Default constructor.
/**
* Typically, a publisher is not created through this method, but instead is created through a
* call to `Node::create_publisher`.
* \param[in] node_base A pointer to the NodeBaseInterface for the parent node.
* \param[in] topic The topic that this publisher publishes on.
* \param[in] type_support The type support structure for the type to be published.
* \param[in] publisher_options QoS settings for this publisher.
*/
RCLCPP_PUBLIC
PublisherBase(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rosidl_message_type_support_t & type_support,
const rcl_publisher_options_t & publisher_options);
RCLCPP_PUBLIC
virtual ~PublisherBase();
/// Get the topic that this publisher publishes on.
/** \return The topic name. */
RCLCPP_PUBLIC
const char *
get_topic_name() const;
/// Get the queue size for this publisher.
/** \return The queue size. */
RCLCPP_PUBLIC
size_t
get_queue_size() const;
/// Get the global identifier for this publisher (used in rmw and by DDS).
/** \return The gid. */
RCLCPP_PUBLIC
const rmw_gid_t &
get_gid() const;
/// Get the rcl publisher handle.
/** \return The rcl publisher handle. */
RCLCPP_PUBLIC
rcl_publisher_t *
get_publisher_handle();
/// Get the rcl publisher handle.
/** \return The rcl publisher handle. */
RCLCPP_PUBLIC
const rcl_publisher_t *
get_publisher_handle() const;
/// Get all the QoS event handlers associated with this publisher.
/** \return The vector of QoS event handlers. */
RCLCPP_PUBLIC
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
get_event_handlers() const;
/// Get subscription count
/** \return The number of subscriptions. */
RCLCPP_PUBLIC
size_t
get_subscription_count() const;
/// Get intraprocess subscription count
/** \return The number of intraprocess subscriptions. */
RCLCPP_PUBLIC
size_t
get_intra_process_subscription_count() 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
* of this publisher may manually call `assert_liveliness` at some point in time to signal to the
* rest of the system that this Node is still alive.
*
* \return `true` if the liveliness was asserted successfully, otherwise `false`
*/
RCLCPP_PUBLIC
RCUTILS_WARN_UNUSED
bool
assert_liveliness() const;
/// Get the actual QoS settings, after the defaults have been determined.
/**
* The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
* can only be resolved after the creation of the publisher, and it
* depends on the underlying rmw implementation.
* If the underlying setting in use can't be represented in ROS terms,
* it will be set to RMW_QOS_POLICY_*_UNKNOWN.
* May throw runtime_error when an unexpected error occurs.
*
* \return The actual qos settings.
*/
RCLCPP_PUBLIC
rclcpp::QoS
get_actual_qos() const;
/// Check if publisher instance can loan messages.
/**
* Depending on the middleware and the message type, this will return true if the middleware
* can allocate a ROS message instance.
*/
RCLCPP_PUBLIC
bool
can_loan_messages() const;
/// Compare this publisher to a gid.
/**
* Note that this function calls the next function.
* \param[in] gid Reference to a gid.
* \return True if the publisher's gid matches the input.
*/
RCLCPP_PUBLIC
bool
operator==(const rmw_gid_t & gid) const;
/// Compare this publisher to a pointer gid.
/**
* A wrapper for comparing this publisher's gid to the input using rmw_compare_gids_equal.
* \param[in] gid A pointer to a gid.
* \return True if this publisher's gid matches the input.
*/
RCLCPP_PUBLIC
bool
operator==(const rmw_gid_t * gid) const;
using IntraProcessManagerSharedPtr =
std::shared_ptr<rclcpp::experimental::IntraProcessManager>;
/// Implementation utility function used to setup intra process publishing after creation.
RCLCPP_PUBLIC
void
setup_intra_process(
uint64_t intra_process_publisher_id,
IntraProcessManagerSharedPtr ipm);
protected:
template<typename EventCallbackT>
void
add_event_handler(
const EventCallbackT & callback,
const rcl_publisher_event_type_t event_type)
{
auto handler = std::make_shared<QOSEventHandler<EventCallbackT>>(
callback,
rcl_publisher_event_init,
&publisher_handle_,
event_type);
event_handlers_.emplace_back(handler);
}
std::shared_ptr<rcl_node_t> rcl_node_handle_;
rcl_publisher_t publisher_handle_ = rcl_get_zero_initialized_publisher();
std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
using IntraProcessManagerWeakPtr =
std::weak_ptr<rclcpp::experimental::IntraProcessManager>;
bool intra_process_is_enabled_;
IntraProcessManagerWeakPtr weak_ipm_;
uint64_t intra_process_publisher_id_;
rmw_gid_t rmw_gid_;
};
} // namespace rclcpp
#endif // RCLCPP__PUBLISHER_BASE_HPP_

View File

@@ -24,8 +24,10 @@
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -41,6 +43,9 @@ namespace rclcpp
* called from a templated "create_publisher" method on the Node class, and
* is passed to the non-templated "create_publisher" method on the NodeTopics
* class where it is used to create and setup the Publisher.
*
* It also handles the two step construction of Publishers, first calling
* the constructor and then the post_init_setup() method.
*/
struct PublisherFactory
{
@@ -49,95 +54,33 @@ struct PublisherFactory
rclcpp::PublisherBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_publisher_options_t & publisher_options)>;
const rclcpp::QoS & qos
)>;
PublisherFactoryFunction create_typed_publisher;
// Adds the PublisherBase to the intraprocess manager with the correctly
// templated call to IntraProcessManager::store_intra_process_message.
using AddPublisherToIntraProcessManagerFunction = std::function<
uint64_t(
rclcpp::intra_process_manager::IntraProcessManager * ipm,
rclcpp::PublisherBase::SharedPtr publisher)>;
AddPublisherToIntraProcessManagerFunction add_publisher_to_intra_process_manager;
// Creates the callback function which is called on each
// PublisherT::publish() and which handles the intra process transmission of
// the message being published.
using SharedPublishCallbackFactoryFunction = std::function<
rclcpp::PublisherBase::StoreMessageCallbackT(
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm)>;
SharedPublishCallbackFactoryFunction create_shared_publish_callback;
const PublisherFactoryFunction create_typed_publisher;
};
/// Return a PublisherFactory with functions setup for creating a PublisherT<MessageT, Alloc>.
template<typename MessageT, typename Alloc, typename PublisherT>
/// Return a PublisherFactory with functions setup for creating a PublisherT<MessageT, AllocatorT>.
template<typename MessageT, typename AllocatorT, typename PublisherT>
PublisherFactory
create_publisher_factory(std::shared_ptr<Alloc> allocator)
create_publisher_factory(const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options)
{
PublisherFactory factory;
// factory function that creates a MessageT specific PublisherT
factory.create_typed_publisher =
[allocator](
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_publisher_options_t & publisher_options) -> std::shared_ptr<PublisherT>
PublisherFactory factory {
// factory function that creates a MessageT specific PublisherT
[options](
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
const rclcpp::QoS & qos
) -> std::shared_ptr<PublisherT>
{
auto message_alloc = std::make_shared<typename PublisherT::MessageAlloc>(*allocator.get());
publisher_options.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc.get());
return std::make_shared<PublisherT>(node_base, topic_name, publisher_options, message_alloc);
};
// function to add a publisher to the intra process manager
factory.add_publisher_to_intra_process_manager =
[](
rclcpp::intra_process_manager::IntraProcessManager * ipm,
rclcpp::PublisherBase::SharedPtr publisher) -> uint64_t
{
return ipm->add_publisher<MessageT, Alloc>(std::dynamic_pointer_cast<PublisherT>(publisher));
};
// function to create a shared publish callback std::function
using StoreMessageCallbackT = rclcpp::PublisherBase::StoreMessageCallbackT;
factory.create_shared_publish_callback =
[](rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm) -> StoreMessageCallbackT
{
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = ipm;
// this function is called on each call to publish() and handles storing
// of the published message in the intra process manager
auto shared_publish_callback =
[weak_ipm](uint64_t publisher_id, void * msg, const std::type_info & type_info) -> uint64_t
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or maybe return with a warning?
throw std::runtime_error(
"intra process publish called after destruction of intra process manager");
}
if (!msg) {
throw std::runtime_error("cannot publisher msg which is a null pointer");
}
auto & message_type_info = typeid(MessageT);
if (message_type_info != type_info) {
throw std::runtime_error(
std::string("published type '") + type_info.name() +
"' is incompatible from the publisher type '" + message_type_info.name() + "'");
}
MessageT * typed_message_ptr = static_cast<MessageT *>(msg);
using MessageDeleter = typename Publisher<MessageT, Alloc>::MessageDeleter;
std::unique_ptr<MessageT, MessageDeleter> unique_msg(typed_message_ptr);
uint64_t message_seq =
ipm->store_intra_process_message<MessageT, Alloc>(publisher_id, unique_msg);
return message_seq;
};
return shared_publish_callback;
};
auto publisher = std::make_shared<PublisherT>(node_base, topic_name, qos, options);
// This is used for setting up things like intra process comms which
// require this->shared_from_this() which cannot be called from
// the constructor.
publisher->post_init_setup(node_base, topic_name, qos, options);
return publisher;
}
};
// return the factory now that it is populated
return factory;

View File

@@ -19,22 +19,88 @@
#include <string>
#include <vector>
#include "rcl/publisher.h"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/detail/rmw_implementation_specific_publisher_payload.hpp"
#include "rclcpp/intra_process_setting.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
namespace rclcpp
{
/// Structure containing optional configuration for Publishers.
template<typename Allocator>
struct PublisherOptionsWithAllocator
namespace callback_group
{
class CallbackGroup;
} // namespace callback_group
/// Non-templated part of PublisherOptionsWithAllocator<Allocator>.
struct PublisherOptionsBase
{
/// The quality of service profile to pass on to the rmw implementation.
rmw_qos_profile_t qos_profile = rmw_qos_profile_default;
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
/// Setting to explicitly set intraprocess communications.
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault;
/// Callbacks for various events related to publishers.
PublisherEventCallbacks event_callbacks;
/// Callback group in which the waitable items from the publisher should be placed.
std::shared_ptr<rclcpp::callback_group::CallbackGroup> callback_group;
/// Optional RMW implementation specific payload to be used during creation of the publisher.
std::shared_ptr<rclcpp::detail::RMWImplementationSpecificPublisherPayload>
rmw_implementation_payload = nullptr;
};
/// Structure containing optional configuration for Publishers.
template<typename Allocator>
struct PublisherOptionsWithAllocator : public PublisherOptionsBase
{
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
PublisherOptionsWithAllocator<Allocator>() {}
/// Constructor using base class as input.
explicit PublisherOptionsWithAllocator(const PublisherOptionsBase & publisher_options_base)
: PublisherOptionsBase(publisher_options_base)
{}
/// Convert this class, and a rclcpp::QoS, into an rcl_publisher_options_t.
template<typename MessageT>
rcl_publisher_options_t
to_rcl_publisher_options(const rclcpp::QoS & qos) const
{
rcl_publisher_options_t result;
using AllocatorTraits = std::allocator_traits<Allocator>;
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
auto message_alloc = std::make_shared<MessageAllocatorT>(*this->get_allocator().get());
result.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(*message_alloc);
result.qos = qos.get_rmw_qos_profile();
// Apply payload to rcl_publisher_options if necessary.
if (rmw_implementation_payload && rmw_implementation_payload->has_been_customized()) {
rmw_implementation_payload->modify_rmw_publisher_options(result.rmw_publisher_options);
}
return result;
}
/// Get the allocator, creating one if needed.
std::shared_ptr<Allocator>
get_allocator() const
{
if (!this->allocator) {
// TODO(wjwwood): I would like to use the commented line instead, but
// cppcheck 1.89 fails with:
// Syntax Error: AST broken, binary operator '>' doesn't have two operands.
// return std::make_shared<Allocator>();
std::shared_ptr<Allocator> tmp(new Allocator());
return tmp;
}
return this->allocator;
}
};
using PublisherOptions = PublisherOptionsWithAllocator<std::allocator<void>>;

View File

@@ -0,0 +1,206 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__QOS_HPP_
#define RCLCPP__QOS_HPP_
#include <rmw/qos_profiles.h>
#include <rmw/types.h>
#include "rclcpp/duration.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// QoS initialization values, cannot be created directly, use KeepAll or KeepLast instead.
struct RCLCPP_PUBLIC QoSInitialization
{
rmw_qos_history_policy_t history_policy;
size_t depth;
/// Constructor which takes both a history policy and a depth (even if it would be unused).
QoSInitialization(rmw_qos_history_policy_t history_policy_arg, size_t depth_arg);
/// Create a QoSInitialization from an existing rmw_qos_profile_t, using its history and depth.
static
QoSInitialization
from_rmw(const rmw_qos_profile_t & rmw_qos);
};
/// Use to initialize the QoS with the keep_all history setting.
struct RCLCPP_PUBLIC KeepAll : public rclcpp::QoSInitialization
{
KeepAll();
};
/// Use to initialize the QoS with the keep_last history setting and the given depth.
struct RCLCPP_PUBLIC KeepLast : public rclcpp::QoSInitialization
{
explicit KeepLast(size_t depth);
};
/// Encapsulation of Quality of Service settings.
class RCLCPP_PUBLIC QoS
{
public:
/// Constructor which allows you to construct a QoS by giving the only required settings.
explicit
QoS(
const QoSInitialization & qos_initialization,
const rmw_qos_profile_t & initial_profile = rmw_qos_profile_default);
/// Conversion constructor to ease construction in the common case of just specifying depth.
/**
* Convenience constructor, equivalent to QoS(KeepLast(history_depth)).
*/
// cppcheck-suppress noExplicitConstructor
QoS(size_t history_depth); // NOLINT(runtime/explicit): conversion constructor
/// Return the rmw qos profile.
rmw_qos_profile_t &
get_rmw_qos_profile();
/// Return the rmw qos profile.
const rmw_qos_profile_t &
get_rmw_qos_profile() const;
/// Set the history policy.
QoS &
history(rmw_qos_history_policy_t history);
/// Set the history to keep last.
QoS &
keep_last(size_t depth);
/// Set the history to keep all.
QoS &
keep_all();
/// Set the reliability setting.
QoS &
reliability(rmw_qos_reliability_policy_t reliability);
/// Set the reliability setting to reliable.
QoS &
reliable();
/// Set the reliability setting to best effort.
QoS &
best_effort();
/// Set the durability setting.
QoS &
durability(rmw_qos_durability_policy_t durability);
/// Set the durability setting to volatile.
/**
* Note that this cannot be named `volatile` because it is a C++ keyword.
*/
QoS &
durability_volatile();
/// Set the durability setting to transient local.
QoS &
transient_local();
/// Set the deadline setting.
QoS &
deadline(rmw_time_t deadline);
/// Set the deadline setting, rclcpp::Duration.
QoS &
deadline(const rclcpp::Duration & deadline);
/// Set the lifespan setting.
QoS &
lifespan(rmw_time_t lifespan);
/// Set the lifespan setting, rclcpp::Duration.
QoS &
lifespan(const rclcpp::Duration & lifespan);
/// Set the liveliness setting.
QoS &
liveliness(rmw_qos_liveliness_policy_t liveliness);
/// Set the liveliness_lease_duration setting.
QoS &
liveliness_lease_duration(rmw_time_t liveliness_lease_duration);
/// Set the liveliness_lease_duration setting, rclcpp::Duration.
QoS &
liveliness_lease_duration(const rclcpp::Duration & liveliness_lease_duration);
/// Set the avoid_ros_namespace_conventions setting.
QoS &
avoid_ros_namespace_conventions(bool avoid_ros_namespace_conventions);
private:
rmw_qos_profile_t rmw_qos_profile_;
};
class RCLCPP_PUBLIC SensorDataQoS : public QoS
{
public:
explicit
SensorDataQoS(
const QoSInitialization & qos_initialization = (
QoSInitialization::from_rmw(rmw_qos_profile_sensor_data)
));
};
class RCLCPP_PUBLIC ParametersQoS : public QoS
{
public:
explicit
ParametersQoS(
const QoSInitialization & qos_initialization = (
QoSInitialization::from_rmw(rmw_qos_profile_parameters)
));
};
class RCLCPP_PUBLIC ServicesQoS : public QoS
{
public:
explicit
ServicesQoS(
const QoSInitialization & qos_initialization = (
QoSInitialization::from_rmw(rmw_qos_profile_services_default)
));
};
class RCLCPP_PUBLIC ParameterEventsQoS : public QoS
{
public:
explicit
ParameterEventsQoS(
const QoSInitialization & qos_initialization = (
QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)
));
};
class RCLCPP_PUBLIC SystemDefaultsQoS : public QoS
{
public:
explicit
SystemDefaultsQoS(
const QoSInitialization & qos_initialization = (
QoSInitialization::from_rmw(rmw_qos_profile_system_default)
));
};
} // namespace rclcpp
#endif // RCLCPP__QOS_HPP_

View File

@@ -0,0 +1,126 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__QOS_EVENT_HPP_
#define RCLCPP__QOS_EVENT_HPP_
#include <functional>
#include "rcl/error_handling.h"
#include "rcutils/logging_macros.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/waitable.hpp"
namespace rclcpp
{
using QOSDeadlineRequestedInfo = rmw_requested_deadline_missed_status_t;
using QOSDeadlineOfferedInfo = rmw_offered_deadline_missed_status_t;
using QOSLivelinessChangedInfo = rmw_liveliness_changed_status_t;
using QOSLivelinessLostInfo = rmw_liveliness_lost_status_t;
using QOSDeadlineRequestedCallbackType = std::function<void (QOSDeadlineRequestedInfo &)>;
using QOSDeadlineOfferedCallbackType = std::function<void (QOSDeadlineOfferedInfo &)>;
using QOSLivelinessChangedCallbackType = std::function<void (QOSLivelinessChangedInfo &)>;
using QOSLivelinessLostCallbackType = std::function<void (QOSLivelinessLostInfo &)>;
/// Contains callbacks for various types of events a Publisher can receive from the middleware.
struct PublisherEventCallbacks
{
QOSDeadlineOfferedCallbackType deadline_callback;
QOSLivelinessLostCallbackType liveliness_callback;
};
/// Contains callbacks for non-message events that a Subscription can receive from the middleware.
struct SubscriptionEventCallbacks
{
QOSDeadlineRequestedCallbackType deadline_callback;
QOSLivelinessChangedCallbackType liveliness_callback;
};
class QOSEventHandlerBase : public Waitable
{
public:
RCLCPP_PUBLIC
virtual ~QOSEventHandlerBase();
/// Get the number of ready events
RCLCPP_PUBLIC
size_t
get_number_of_ready_events() override;
/// Add the Waitable to a wait set.
RCLCPP_PUBLIC
bool
add_to_wait_set(rcl_wait_set_t * wait_set) override;
/// Check if the Waitable is ready.
RCLCPP_PUBLIC
bool
is_ready(rcl_wait_set_t * wait_set) override;
protected:
rcl_event_t event_handle_;
size_t wait_set_event_index_;
};
template<typename EventCallbackT>
class QOSEventHandler : public QOSEventHandlerBase
{
public:
template<typename InitFuncT, typename ParentHandleT, typename EventTypeEnum>
QOSEventHandler(
const EventCallbackT & callback,
InitFuncT init_func,
ParentHandleT parent_handle,
EventTypeEnum event_type)
: event_callback_(callback)
{
event_handle_ = rcl_get_zero_initialized_event();
rcl_ret_t ret = init_func(&event_handle_, parent_handle, event_type);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create event");
}
}
/// Execute any entities of the Waitable that are ready.
void
execute() override
{
EventCallbackInfoT callback_info;
rcl_ret_t ret = rcl_take_event(&event_handle_, &callback_info);
if (ret != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"Couldn't take event info: %s", rcl_get_error_string().str);
return;
}
event_callback_(callback_info);
}
private:
using EventCallbackInfoT = typename std::remove_reference<typename
rclcpp::function_traits::function_traits<EventCallbackT>::template argument_type<0>>::type;
EventCallbackT event_callback_;
};
} // namespace rclcpp
#endif // RCLCPP__QOS_EVENT_HPP_

View File

@@ -48,7 +48,8 @@
* - rclcpp::Node::get_parameter()
* - rclcpp::Node::describe_parameters()
* - rclcpp::Node::list_parameters()
* - rclcpp::Node::register_param_change_callback()
* - rclcpp::Node::add_on_set_parameters_callback()
* - rclcpp::Node::remove_on_set_parameters_callback()
* - rclcpp::Parameter
* - rclcpp::ParameterValue
* - rclcpp::AsyncParametersClient

View File

@@ -33,6 +33,7 @@
#include "rclcpp/logging.hpp"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
#include "tracetools/tracetools.h"
namespace rclcpp
{
@@ -154,6 +155,10 @@ public:
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
}
TRACEPOINT(
rclcpp_service_callback_added,
(const void *)get_service_handle().get(),
(const void *)&any_callback_);
}
Service(
@@ -172,6 +177,10 @@ public:
}
service_handle_ = service_handle;
TRACEPOINT(
rclcpp_service_callback_added,
(const void *)get_service_handle().get(),
(const void *)&any_callback_);
}
Service(
@@ -192,6 +201,10 @@ public:
// In this case, rcl owns the service handle memory
service_handle_ = std::shared_ptr<rcl_service_t>(new rcl_service_t);
service_handle_->impl = service_handle->impl;
TRACEPOINT(
rclcpp_service_callback_added,
(const void *)get_service_handle().get(),
(const void *)&any_callback_);
}
Service() = delete;

View File

@@ -150,7 +150,7 @@ public:
);
}
bool collect_entities(const WeakNodeVector & weak_nodes)
bool collect_entities(const WeakNodeList & weak_nodes)
{
bool has_invalid_weak_nodes = false;
for (auto & weak_node : weak_nodes) {
@@ -164,40 +164,28 @@ public:
if (!group || !group->can_be_taken_from().load()) {
continue;
}
for (auto & weak_subscription : group->get_subscription_ptrs()) {
auto subscription = weak_subscription.lock();
if (subscription) {
group->find_subscription_ptrs_if(
[this](const rclcpp::SubscriptionBase::SharedPtr & subscription) {
subscription_handles_.push_back(subscription->get_subscription_handle());
if (subscription->get_intra_process_subscription_handle()) {
subscription_handles_.push_back(
subscription->get_intra_process_subscription_handle());
}
}
}
for (auto & weak_service : group->get_service_ptrs()) {
auto service = weak_service.lock();
if (service) {
return false;
});
group->find_service_ptrs_if([this](const rclcpp::ServiceBase::SharedPtr & service) {
service_handles_.push_back(service->get_service_handle());
}
}
for (auto & weak_client : group->get_client_ptrs()) {
auto client = weak_client.lock();
if (client) {
return false;
});
group->find_client_ptrs_if([this](const rclcpp::ClientBase::SharedPtr & client) {
client_handles_.push_back(client->get_client_handle());
}
}
for (auto & weak_timer : group->get_timer_ptrs()) {
auto timer = weak_timer.lock();
if (timer) {
return false;
});
group->find_timer_ptrs_if([this](const rclcpp::TimerBase::SharedPtr & timer) {
timer_handles_.push_back(timer->get_timer_handle());
}
}
for (auto & weak_waitable : group->get_waitable_ptrs()) {
auto waitable = weak_waitable.lock();
if (waitable) {
return false;
});
group->find_waitable_ptrs_if([this](const rclcpp::Waitable::SharedPtr & waitable) {
waitable_handles_.push_back(waitable);
}
}
return false;
});
}
}
return has_invalid_weak_nodes;
@@ -265,17 +253,12 @@ public:
virtual void
get_next_subscription(
executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
auto it = subscription_handles_.begin();
while (it != subscription_handles_.end()) {
auto subscription = get_subscription_by_handle(*it, weak_nodes);
if (subscription) {
// Figure out if this is for intra-process or not.
bool is_intra_process = false;
if (subscription->get_intra_process_subscription_handle()) {
is_intra_process = subscription->get_intra_process_subscription_handle() == *it;
}
// Find the group for this handle and see if it can be serviced
auto group = get_group_by_subscription(subscription, weak_nodes);
if (!group) {
@@ -291,11 +274,7 @@ public:
continue;
}
// Otherwise it is safe to set and return the any_exec
if (is_intra_process) {
any_exec.subscription_intra_process = subscription;
} else {
any_exec.subscription = subscription;
}
any_exec.subscription = subscription;
any_exec.callback_group = group;
any_exec.node_base = get_node_by_group(group, weak_nodes);
subscription_handles_.erase(it);
@@ -309,7 +288,7 @@ public:
virtual void
get_next_service(
executor::AnyExecutable & any_exec,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
auto it = service_handles_.begin();
while (it != service_handles_.end()) {
@@ -342,7 +321,7 @@ public:
}
virtual void
get_next_client(executor::AnyExecutable & any_exec, const WeakNodeVector & weak_nodes)
get_next_client(executor::AnyExecutable & any_exec, const WeakNodeList & weak_nodes)
{
auto it = client_handles_.begin();
while (it != client_handles_.end()) {
@@ -375,7 +354,42 @@ public:
}
virtual void
get_next_waitable(executor::AnyExecutable & any_exec, const WeakNodeVector & weak_nodes)
get_next_timer(
executor::AnyExecutable & any_exec,
const WeakNodeList & weak_nodes)
{
auto it = timer_handles_.begin();
while (it != timer_handles_.end()) {
auto timer = get_timer_by_handle(*it, weak_nodes);
if (timer) {
// Find the group for this handle and see if it can be serviced
auto group = get_group_by_timer(timer, weak_nodes);
if (!group) {
// Group was not found, meaning the timer is not valid...
// Remove it from the ready list and continue looking
it = timer_handles_.erase(it);
continue;
}
if (!group->can_be_taken_from().load()) {
// Group is mutually exclusive and is being used, so skip it for now
// Leave it to be checked next time, but continue searching
++it;
continue;
}
// Otherwise it is safe to set and return the any_exec
any_exec.timer = timer;
any_exec.callback_group = group;
any_exec.node_base = get_node_by_group(group, weak_nodes);
timer_handles_.erase(it);
return;
}
// Else, the service is no longer valid, remove it and continue
it = timer_handles_.erase(it);
}
}
virtual void
get_next_waitable(executor::AnyExecutable & any_exec, const WeakNodeList & weak_nodes)
{
auto it = waitable_handles_.begin();
while (it != waitable_handles_.end()) {
@@ -430,6 +444,15 @@ public:
return number_of_services;
}
size_t number_of_ready_events() const
{
size_t number_of_events = 0;
for (auto waitable : waitable_handles_) {
number_of_events += waitable->get_number_of_ready_events();
}
return number_of_events;
}
size_t number_of_ready_clients() const
{
size_t number_of_clients = client_handles_.size();

View File

@@ -23,20 +23,30 @@
#include <memory>
#include <sstream>
#include <string>
#include <utility>
#include "rcl/error_handling.h"
#include "rcl/subscription.h"
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/detail/resolve_use_intra_process.hpp"
#include "rclcpp/detail/resolve_intra_process_buffer_type.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/experimental/intra_process_manager.hpp"
#include "rclcpp/experimental/subscription_intra_process.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/subscription_base.hpp"
#include "rclcpp/subscription_options.hpp"
#include "rclcpp/subscription_traits.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/waitable.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
@@ -46,169 +56,139 @@ namespace node_interfaces
class NodeTopicsInterface;
} // namespace node_interfaces
namespace intra_process_manager
{
/**
* NOTE(ivanpauno): IntraProcessManager is forward declared here, avoiding a circular inclusion between intra_process_manager.hpp and publisher.hpp.
* SharedPtr and WeakPtr of the IntraProcessManager are defined again here, to avoid a warning for accessing a member of a forward declared class.
*/
class IntraProcessManager;
}
/// Virtual base class for subscriptions. This pattern allows us to iterate over different template
/// specializations of Subscription, among other things.
class SubscriptionBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
/// Default constructor.
/**
* \param[in] node_handle The rcl representation of the node that owns this subscription.
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] subscription_options options for the subscription.
* \param[in] is_serialized is true if the message will be delivered still serialized
*/
RCLCPP_PUBLIC
SubscriptionBase(
std::shared_ptr<rcl_node_t> node_handle,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
const rcl_subscription_options_t & subscription_options,
bool is_serialized = false);
/// Default destructor.
RCLCPP_PUBLIC
virtual ~SubscriptionBase();
/// Get the topic that this subscription is subscribed on.
RCLCPP_PUBLIC
const char *
get_topic_name() const;
RCLCPP_PUBLIC
std::shared_ptr<rcl_subscription_t>
get_subscription_handle();
RCLCPP_PUBLIC
const std::shared_ptr<rcl_subscription_t>
get_subscription_handle() const;
RCLCPP_PUBLIC
virtual const std::shared_ptr<rcl_subscription_t>
get_intra_process_subscription_handle() const;
/// Borrow a new message.
/** \return Shared pointer to the fresh message. */
virtual std::shared_ptr<void>
create_message() = 0;
/// Borrow a new serialized message
/** \return Shared pointer to a rcl_message_serialized_t. */
virtual std::shared_ptr<rcl_serialized_message_t>
create_serialized_message() = 0;
/// Check if we need to handle the message, and execute the callback if we do.
/**
* \param[in] message Shared pointer to the message to handle.
* \param[in] message_info Metadata associated with this message.
*/
virtual void
handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info) = 0;
/// Return the message borrowed in create_message.
/** \param[in] message Shared pointer to the returned message. */
virtual void
return_message(std::shared_ptr<void> & message) = 0;
/// Return the message borrowed in create_serialized_message.
/** \param[in] message Shared pointer to the returned message. */
virtual void
return_serialized_message(std::shared_ptr<rcl_serialized_message_t> & message) = 0;
virtual void
handle_intra_process_message(
rcl_interfaces::msg::IntraProcessMessage & ipm,
const rmw_message_info_t & message_info) = 0;
const rosidl_message_type_support_t &
get_message_type_support_handle() const;
bool
is_serialized() const;
/// Get matching publisher count
/** \return The number of publishers on this topic. */
RCLCPP_PUBLIC
size_t
get_publisher_count() const;
protected:
std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;
std::shared_ptr<rcl_subscription_t> subscription_handle_;
std::shared_ptr<rcl_node_t> node_handle_;
using IntraProcessManagerWeakPtr =
std::weak_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
bool use_intra_process_;
IntraProcessManagerWeakPtr weak_ipm_;
uint64_t intra_process_subscription_id_;
private:
RCLCPP_DISABLE_COPY(SubscriptionBase)
rosidl_message_type_support_t type_support_;
bool is_serialized_;
};
/// Subscription implementation, templated on the type of message this subscription receives.
template<
typename CallbackMessageT,
typename Alloc = std::allocator<void>>
typename AllocatorT = std::allocator<void>,
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT,
AllocatorT
>>
class Subscription : public SubscriptionBase
{
friend class rclcpp::node_interfaces::NodeTopicsInterface;
public:
using MessageAllocTraits = allocator::AllocRebind<CallbackMessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAlloc, CallbackMessageT>;
using MessageAllocatorTraits = allocator::AllocRebind<CallbackMessageT, AllocatorT>;
using MessageAllocator = typename MessageAllocatorTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAllocator, CallbackMessageT>;
using ConstMessageSharedPtr = std::shared_ptr<const CallbackMessageT>;
using MessageUniquePtr = std::unique_ptr<CallbackMessageT, MessageDeleter>;
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
/// Default constructor.
/**
* The constructor for a subscription is almost never called directly. Instead, subscriptions
* should be instantiated through Node::create_subscription.
* \param[in] node_handle rcl representation of the node that owns this subscription.
* The constructor for a subscription is almost never called directly.
* Instead, subscriptions should be instantiated through the function
* rclcpp::create_subscription().
*
* \param[in] node_base NodeBaseInterface pointer that is used in part of the setup.
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] subscription_options options for the subscription.
* \param[in] callback User defined callback to call when a message is received.
* \param[in] memory_strategy The memory strategy to be used for managing message memory.
* \param[in] options options for the subscription.
* \param[in] message_memory_strategy The memory strategy to be used for managing message memory.
*/
Subscription(
std::shared_ptr<rcl_node_t> node_handle,
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
const rcl_subscription_options_t & subscription_options,
AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, Alloc>::SharedPtr
memory_strategy = message_memory_strategy::MessageMemoryStrategy<CallbackMessageT,
Alloc>::create_default())
const rclcpp::QoS & qos,
AnySubscriptionCallback<CallbackMessageT, AllocatorT> callback,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
typename MessageMemoryStrategyT::SharedPtr message_memory_strategy)
: SubscriptionBase(
node_handle,
node_base,
type_support_handle,
topic_name,
subscription_options,
options.template to_rcl_subscription_options<CallbackMessageT>(qos),
rclcpp::subscription_traits::is_serialized_subscription_argument<CallbackMessageT>::value),
any_callback_(callback),
message_memory_strategy_(memory_strategy),
get_intra_process_message_callback_(nullptr),
matches_any_intra_process_publishers_(nullptr)
{}
options_(options),
message_memory_strategy_(message_memory_strategy)
{
if (options.event_callbacks.deadline_callback) {
this->add_event_handler(
options.event_callbacks.deadline_callback,
RCL_SUBSCRIPTION_REQUESTED_DEADLINE_MISSED);
}
if (options.event_callbacks.liveliness_callback) {
this->add_event_handler(
options.event_callbacks.liveliness_callback,
RCL_SUBSCRIPTION_LIVELINESS_CHANGED);
}
// Setup intra process publishing if requested.
if (rclcpp::detail::resolve_use_intra_process(options, *node_base)) {
using rclcpp::detail::resolve_intra_process_buffer_type;
// Check if the QoS is compatible with intra-process.
rmw_qos_profile_t qos_profile = get_actual_qos().get_rmw_qos_profile();
if (qos_profile.history == RMW_QOS_POLICY_HISTORY_KEEP_ALL) {
throw std::invalid_argument(
"intraprocess communication is not allowed with keep all history qos policy");
}
if (qos_profile.depth == 0) {
throw std::invalid_argument(
"intraprocess communication is not allowed with 0 depth qos policy");
}
if (qos_profile.durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
throw std::invalid_argument(
"intraprocess communication allowed only with volatile durability");
}
// First create a SubscriptionIntraProcess which will be given to the intra-process manager.
auto context = node_base->get_context();
auto subscription_intra_process = std::make_shared<
rclcpp::experimental::SubscriptionIntraProcess<
CallbackMessageT,
AllocatorT,
typename MessageUniquePtr::deleter_type
>>(
callback,
options.get_allocator(),
context,
this->get_topic_name(), // important to get like this, as it has the fully-qualified name
qos_profile,
resolve_intra_process_buffer_type(options.intra_process_buffer_type, callback)
);
TRACEPOINT(
rclcpp_subscription_init,
(const void *)get_subscription_handle().get(),
(const void *)subscription_intra_process.get());
// 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);
this->setup_intra_process(intra_process_subscription_id, ipm);
}
TRACEPOINT(
rclcpp_subscription_init,
(const void *)get_subscription_handle().get(),
(const void *)this);
TRACEPOINT(
rclcpp_subscription_callback_added,
(const void *)this,
(const void *)&any_callback_);
// The callback object gets copied, so if registration is done too early/before this point
// (e.g. in `AnySubscriptionCallback::set()`), its address won't match any address used later
// in subsequent tracepoints.
any_callback_.register_callback_for_tracing();
}
/// Called after construction to continue setup that requires shared_from_this().
void post_init_setup(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const rclcpp::QoS & qos,
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options)
{
(void)node_base;
(void)qos;
(void)options;
}
/// Support dynamically setting the message memory strategy.
/**
@@ -217,12 +197,12 @@ public:
*/
void set_message_memory_strategy(
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT,
Alloc>::SharedPtr message_memory_strategy)
AllocatorT>::SharedPtr message_memory_strategy)
{
message_memory_strategy_ = message_memory_strategy;
}
std::shared_ptr<void> create_message()
std::shared_ptr<void> create_message() override
{
/* The default message memory strategy provides a dynamically allocated message on each call to
* create_message, though alternative memory strategies that re-use a preallocated message may be
@@ -231,124 +211,64 @@ public:
return message_memory_strategy_->borrow_message();
}
std::shared_ptr<rcl_serialized_message_t> create_serialized_message()
std::shared_ptr<rcl_serialized_message_t> create_serialized_message() override
{
return message_memory_strategy_->borrow_serialized_message();
}
void handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info)
void handle_message(
std::shared_ptr<void> & message, const rmw_message_info_t & message_info) override
{
if (matches_any_intra_process_publishers_) {
if (matches_any_intra_process_publishers_(&message_info.publisher_gid)) {
// In this case, the message will be delivered via intra process and
// we should ignore this copy of the message.
return;
}
if (matches_any_intra_process_publishers(&message_info.publisher_gid)) {
// In this case, the message will be delivered via intra process and
// we should ignore this copy of the message.
return;
}
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
any_callback_.dispatch(typed_message, message_info);
}
/// Return the loaned message.
void
handle_loaned_message(
void * loaned_message, const rmw_message_info_t & message_info) override
{
auto typed_message = static_cast<CallbackMessageT *>(loaned_message);
// message is loaned, so we have to make sure that the deleter does not deallocate the message
auto sptr = std::shared_ptr<CallbackMessageT>(
typed_message, [](CallbackMessageT * msg) {(void) msg;});
any_callback_.dispatch(sptr, message_info);
}
/// Return the borrowed message.
/** \param message message to be returned */
void return_message(std::shared_ptr<void> & message)
void return_message(std::shared_ptr<void> & message) override
{
auto typed_message = std::static_pointer_cast<CallbackMessageT>(message);
message_memory_strategy_->return_message(typed_message);
}
void return_serialized_message(std::shared_ptr<rcl_serialized_message_t> & message)
void return_serialized_message(std::shared_ptr<rcl_serialized_message_t> & message) override
{
message_memory_strategy_->return_serialized_message(message);
}
void handle_intra_process_message(
rcl_interfaces::msg::IntraProcessMessage & ipm,
const rmw_message_info_t & message_info)
bool use_take_shared_method() const
{
if (!get_intra_process_message_callback_) {
// throw std::runtime_error(
// "handle_intra_process_message called before setup_intra_process");
// TODO(wjwwood): for now, this could mean that intra process was just not enabled.
// However, this can only really happen if this node has it disabled, but the other doesn't.
return;
}
MessageUniquePtr msg;
get_intra_process_message_callback_(
ipm.publisher_id,
ipm.message_sequence,
intra_process_subscription_id_,
msg);
if (!msg) {
// This either occurred because the publisher no longer exists or the
// message requested is no longer being stored.
// TODO(wjwwood): should we notify someone of this? log error, log warning?
return;
}
any_callback_.dispatch_intra_process(msg, message_info);
}
using GetMessageCallbackType =
std::function<void (uint64_t, uint64_t, uint64_t, MessageUniquePtr &)>;
using MatchesAnyPublishersCallbackType = std::function<bool (const rmw_gid_t *)>;
/// Implemenation detail.
// TODO(ivanpauno): This can be moved to the base class. No reason to be here.
// Also get_intra_process_message_callback_ and matches_any_intra_process_publishers_.
void setup_intra_process(
uint64_t intra_process_subscription_id,
GetMessageCallbackType get_message_callback,
MatchesAnyPublishersCallbackType matches_any_publisher_callback,
IntraProcessManagerWeakPtr weak_ipm,
const rcl_subscription_options_t & intra_process_options)
{
std::string intra_process_topic_name = std::string(get_topic_name()) + "/_intra";
rcl_ret_t ret = rcl_subscription_init(
intra_process_subscription_handle_.get(),
node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
intra_process_topic_name.c_str(),
&intra_process_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
auto rcl_node_handle = node_handle_.get();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
intra_process_topic_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle));
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create intra process subscription");
}
intra_process_subscription_id_ = intra_process_subscription_id;
get_intra_process_message_callback_ = get_message_callback;
matches_any_intra_process_publishers_ = matches_any_publisher_callback;
weak_ipm_ = weak_ipm;
use_intra_process_ = true;
}
/// Implemenation detail.
const std::shared_ptr<rcl_subscription_t>
get_intra_process_subscription_handle() const
{
if (!get_intra_process_message_callback_) {
return nullptr;
}
return intra_process_subscription_handle_;
return any_callback_.use_take_shared_method();
}
private:
RCLCPP_DISABLE_COPY(Subscription)
AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, Alloc>::SharedPtr
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_callback_;
/// Copy of original options passed during construction.
/**
* It is important to save a copy of this so that the rmw payload which it
* may contain is kept alive for the duration of the subscription.
*/
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> options_;
typename message_memory_strategy::MessageMemoryStrategy<CallbackMessageT, AllocatorT>::SharedPtr
message_memory_strategy_;
GetMessageCallbackType get_intra_process_message_callback_;
MatchesAnyPublishersCallbackType matches_any_intra_process_publishers_;
};
} // namespace rclcpp

View File

@@ -0,0 +1,236 @@
// Copyright 2019 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__SUBSCRIPTION_BASE_HPP_
#define RCLCPP__SUBSCRIPTION_BASE_HPP_
#include <memory>
#include <string>
#include <vector>
#include "rcl/subscription.h"
#include "rmw/rmw.h"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/experimental/intra_process_manager.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
class NodeBaseInterface;
} // namespace node_interfaces
namespace experimental
{
/**
* IntraProcessManager is forward declared here, avoiding a circular inclusion between
* `intra_process_manager.hpp` and `subscription_base.hpp`.
*/
class IntraProcessManager;
} // namespace experimental
/// Virtual base class for subscriptions. This pattern allows us to iterate over different template
/// specializations of Subscription, among other things.
class SubscriptionBase : public std::enable_shared_from_this<SubscriptionBase>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
/// Default constructor.
/**
* \param[in] node_base NodeBaseInterface pointer used in parts of the setup.
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] subscription_options options for the subscription.
* \param[in] is_serialized is true if the message will be delivered still serialized
*/
RCLCPP_PUBLIC
SubscriptionBase(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
const rcl_subscription_options_t & subscription_options,
bool is_serialized = false);
/// Default destructor.
RCLCPP_PUBLIC
virtual ~SubscriptionBase();
/// Get the topic that this subscription is subscribed on.
RCLCPP_PUBLIC
const char *
get_topic_name() const;
RCLCPP_PUBLIC
std::shared_ptr<rcl_subscription_t>
get_subscription_handle();
RCLCPP_PUBLIC
const std::shared_ptr<rcl_subscription_t>
get_subscription_handle() const;
/// Get all the QoS event handlers associated with this subscription.
/** \return The vector of QoS event handlers. */
RCLCPP_PUBLIC
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
get_event_handlers() const;
/// Get the actual QoS settings, after the defaults have been determined.
/**
* The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
* can only be resolved after the creation of the publisher, and it
* depends on the underlying rmw implementation.
* If the underlying setting in use can't be represented in ROS terms,
* it will be set to RMW_QOS_POLICY_*_UNKNOWN.
* May throw runtime_error when an unexpected error occurs.
*
* \return The actual qos settings.
*/
RCLCPP_PUBLIC
rclcpp::QoS
get_actual_qos() const;
/// Borrow a new message.
/** \return Shared pointer to the fresh message. */
RCLCPP_PUBLIC
virtual
std::shared_ptr<void>
create_message() = 0;
/// Borrow a new serialized message
/** \return Shared pointer to a rcl_message_serialized_t. */
RCLCPP_PUBLIC
virtual
std::shared_ptr<rcl_serialized_message_t>
create_serialized_message() = 0;
/// Check if we need to handle the message, and execute the callback if we do.
/**
* \param[in] message Shared pointer to the message to handle.
* \param[in] message_info Metadata associated with this message.
*/
RCLCPP_PUBLIC
virtual
void
handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info) = 0;
RCLCPP_PUBLIC
virtual
void
handle_loaned_message(void * loaned_message, const rmw_message_info_t & message_info) = 0;
/// Return the message borrowed in create_message.
/** \param[in] message Shared pointer to the returned message. */
RCLCPP_PUBLIC
virtual
void
return_message(std::shared_ptr<void> & message) = 0;
/// Return the message borrowed in create_serialized_message.
/** \param[in] message Shared pointer to the returned message. */
RCLCPP_PUBLIC
virtual
void
return_serialized_message(std::shared_ptr<rcl_serialized_message_t> & message) = 0;
RCLCPP_PUBLIC
const rosidl_message_type_support_t &
get_message_type_support_handle() const;
RCLCPP_PUBLIC
bool
is_serialized() const;
/// Get matching publisher count.
/** \return The number of publishers on this topic. */
RCLCPP_PUBLIC
size_t
get_publisher_count() const;
/// Check if subscription instance can loan messages.
/**
* Depending on the middleware and the message type, this will return true if the middleware
* can allocate a ROS message instance.
*
* \return boolean flag indicating if middleware can loan messages.
*/
RCLCPP_PUBLIC
bool
can_loan_messages() const;
using IntraProcessManagerWeakPtr =
std::weak_ptr<rclcpp::experimental::IntraProcessManager>;
/// Implemenation detail.
RCLCPP_PUBLIC
void
setup_intra_process(
uint64_t intra_process_subscription_id,
IntraProcessManagerWeakPtr weak_ipm);
/// Return the waitable for intra-process, or nullptr if intra-process is not setup.
RCLCPP_PUBLIC
rclcpp::Waitable::SharedPtr
get_intra_process_waitable() const;
protected:
template<typename EventCallbackT>
void
add_event_handler(
const EventCallbackT & callback,
const rcl_subscription_event_type_t event_type)
{
auto handler = std::make_shared<QOSEventHandler<EventCallbackT>>(
callback,
rcl_subscription_event_init,
get_subscription_handle().get(),
event_type);
event_handlers_.emplace_back(handler);
}
RCLCPP_PUBLIC
bool
matches_any_intra_process_publishers(const rmw_gid_t * sender_gid) const;
rclcpp::node_interfaces::NodeBaseInterface * const node_base_;
std::shared_ptr<rcl_node_t> node_handle_;
std::shared_ptr<rcl_subscription_t> subscription_handle_;
std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;
std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
bool use_intra_process_;
IntraProcessManagerWeakPtr weak_ipm_;
uint64_t intra_process_subscription_id_;
private:
RCLCPP_DISABLE_COPY(SubscriptionBase)
rosidl_message_type_support_t type_support_;
bool is_serialized_;
};
} // namespace rclcpp
#endif // RCLCPP__SUBSCRIPTION_BASE_HPP_

View File

@@ -24,25 +24,31 @@
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/subscription_traits.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/intra_process_buffer_type.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/subscription_options.hpp"
#include "rclcpp/subscription_traits.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// Factory with functions used to create a Subscription<MessageT>.
/// Factory containing a function used to create a Subscription<MessageT>.
/**
* This factory class is used to encapsulate the template generated functions
* which are used during the creation of a Message type specific subscription
* This factory class is used to encapsulate the template generated function
* which is used during the creation of a Message type specific subscription
* within a non-templated class.
*
* It is created using the create_subscription_factory function, which is
* usually called from a templated "create_subscription" method of the Node
* class, and is passed to the non-templated "create_subscription" method of
* the NodeTopics class where it is used to create and setup the Subscription.
*
* It also handles the two step construction of Subscriptions, first calling
* the constructor and then the post_init_setup() method.
*/
struct SubscriptionFactory
{
@@ -51,126 +57,62 @@ struct SubscriptionFactory
rclcpp::SubscriptionBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_subscription_options_t & subscription_options)>;
const rclcpp::QoS & qos)>;
SubscriptionFactoryFunction create_typed_subscription;
// Function that takes a MessageT from the intra process manager
using SetupIntraProcessFunction = std::function<
void (
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm,
rclcpp::SubscriptionBase::SharedPtr subscription,
const rcl_subscription_options_t & subscription_options)>;
SetupIntraProcessFunction setup_intra_process;
const SubscriptionFactoryFunction create_typed_subscription;
};
/// Return a SubscriptionFactory with functions for creating a SubscriptionT<MessageT, Alloc>.
/// Return a SubscriptionFactory setup to create a SubscriptionT<MessageT, AllocatorT>.
template<
typename MessageT,
typename CallbackT,
typename Alloc,
typename CallbackMessageT,
typename SubscriptionT>
typename AllocatorT,
typename CallbackMessageT =
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type,
typename SubscriptionT = rclcpp::Subscription<CallbackMessageT, AllocatorT>,
typename MessageMemoryStrategyT = rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT,
AllocatorT
>>
SubscriptionFactory
create_subscription_factory(
CallbackT && callback,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
CallbackMessageT, Alloc>::SharedPtr
msg_mem_strat,
std::shared_ptr<Alloc> allocator)
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options,
typename MessageMemoryStrategyT::SharedPtr msg_mem_strat)
{
SubscriptionFactory factory;
auto allocator = options.get_allocator();
using rclcpp::AnySubscriptionCallback;
AnySubscriptionCallback<CallbackMessageT, Alloc> any_subscription_callback(allocator);
AnySubscriptionCallback<CallbackMessageT, AllocatorT> any_subscription_callback(allocator);
any_subscription_callback.set(std::forward<CallbackT>(callback));
auto message_alloc =
std::make_shared<typename Subscription<CallbackMessageT, Alloc>::MessageAlloc>();
// factory function that creates a MessageT specific SubscriptionT
factory.create_typed_subscription =
[allocator, msg_mem_strat, any_subscription_callback, message_alloc](
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_subscription_options_t & subscription_options
SubscriptionFactory factory {
// factory function that creates a MessageT specific SubscriptionT
[options, msg_mem_strat, any_subscription_callback](
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
const rclcpp::QoS & qos
) -> rclcpp::SubscriptionBase::SharedPtr
{
subscription_options.allocator =
rclcpp::allocator::get_rcl_allocator<CallbackMessageT>(*message_alloc.get());
using rclcpp::Subscription;
using rclcpp::SubscriptionBase;
auto sub = Subscription<CallbackMessageT, Alloc>::make_shared(
node_base->get_shared_rcl_node_handle(),
auto sub = Subscription<CallbackMessageT, AllocatorT>::make_shared(
node_base,
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
topic_name,
subscription_options,
qos,
any_subscription_callback,
options,
msg_mem_strat);
// This is used for setting up things like intra process comms which
// require this->shared_from_this() which cannot be called from
// the constructor.
sub->post_init_setup(node_base, qos, options);
auto sub_base_ptr = std::dynamic_pointer_cast<SubscriptionBase>(sub);
return sub_base_ptr;
};
// function that will setup intra process communications for the subscription
factory.setup_intra_process =
[message_alloc](
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm,
rclcpp::SubscriptionBase::SharedPtr subscription,
const rcl_subscription_options_t & subscription_options)
{
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = ipm;
uint64_t intra_process_subscription_id = ipm->add_subscription(subscription);
auto intra_process_options = rcl_subscription_get_default_options();
intra_process_options.allocator = rclcpp::allocator::get_rcl_allocator<CallbackMessageT>(
*message_alloc.get());
intra_process_options.qos = subscription_options.qos;
intra_process_options.ignore_local_publications = false;
// function that will be called to take a MessageT from the intra process manager
auto take_intra_process_message_func =
[weak_ipm](
uint64_t publisher_id,
uint64_t message_sequence,
uint64_t subscription_id,
typename rclcpp::Subscription<CallbackMessageT, Alloc>::MessageUniquePtr & message)
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or return with a logged warning?
throw std::runtime_error(
"intra process take called after destruction of intra process manager");
}
ipm->take_intra_process_message<CallbackMessageT, Alloc>(
publisher_id, message_sequence, subscription_id, message);
};
// function that is called to see if the publisher id matches any local publishers
auto matches_any_publisher_func =
[weak_ipm](const rmw_gid_t * sender_gid) -> bool
{
auto ipm = weak_ipm.lock();
if (!ipm) {
throw std::runtime_error(
"intra process publisher check called "
"after destruction of intra process manager");
}
return ipm->matches_any_publishers(sender_gid);
};
auto typed_sub_ptr = std::dynamic_pointer_cast<SubscriptionT>(subscription);
typed_sub_ptr->setup_intra_process(
intra_process_subscription_id,
take_intra_process_message_func,
matches_any_publisher_func,
weak_ipm,
intra_process_options
);
};
// end definition of factory function to setup intra process
}
};
// return the factory now that it is populated
return factory;

View File

@@ -19,26 +19,85 @@
#include <string>
#include <vector>
#include "rclcpp/callback_group.hpp"
#include "rclcpp/detail/rmw_implementation_specific_subscription_payload.hpp"
#include "rclcpp/intra_process_buffer_type.hpp"
#include "rclcpp/intra_process_setting.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// Structure containing optional configuration for Subscriptions.
template<typename Allocator>
struct SubscriptionOptionsWithAllocator
/// Non-template base class for subscription options.
struct SubscriptionOptionsBase
{
/// The quality of service profile to pass on to the rmw implementation.
rmw_qos_profile_t qos_profile = rmw_qos_profile_default;
/// Callbacks for events related to this subscription.
SubscriptionEventCallbacks event_callbacks;
/// True to ignore local publications.
bool ignore_local_publications = false;
/// The callback group for this subscription. NULL to use the default callback group.
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group = nullptr;
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
/// 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::CallbackDefault;
/// Optional RMW implementation specific payload to be used during creation of the subscription.
std::shared_ptr<rclcpp::detail::RMWImplementationSpecificSubscriptionPayload>
rmw_implementation_payload = nullptr;
};
/// Structure containing optional configuration for Subscriptions.
template<typename Allocator>
struct SubscriptionOptionsWithAllocator : public SubscriptionOptionsBase
{
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
SubscriptionOptionsWithAllocator<Allocator>() {}
/// Constructor using base class as input.
explicit SubscriptionOptionsWithAllocator(
const SubscriptionOptionsBase & subscription_options_base)
: SubscriptionOptionsBase(subscription_options_base)
{}
/// Convert this class, with a rclcpp::QoS, into an rcl_subscription_options_t.
template<typename MessageT>
rcl_subscription_options_t
to_rcl_subscription_options(const rclcpp::QoS & qos) const
{
rcl_subscription_options_t result = rcl_subscription_get_default_options();
using AllocatorTraits = std::allocator_traits<Allocator>;
using MessageAllocatorT = typename AllocatorTraits::template rebind_alloc<MessageT>;
auto message_alloc = std::make_shared<MessageAllocatorT>(*allocator.get());
result.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc);
result.qos = qos.get_rmw_qos_profile();
result.rmw_subscription_options.ignore_local_publications = this->ignore_local_publications;
// Apply payload to rcl_subscription_options if necessary.
if (rmw_implementation_payload && rmw_implementation_payload->has_been_customized()) {
rmw_implementation_payload->modify_rmw_subscription_options(result.rmw_subscription_options);
}
return result;
}
/// Get the allocator, creating one if needed.
std::shared_ptr<Allocator>
get_allocator() const
{
if (!this->allocator) {
return std::make_shared<Allocator>();
}
return this->allocator;
}
};
using SubscriptionOptions = SubscriptionOptionsWithAllocator<std::allocator<void>>;

View File

@@ -18,9 +18,13 @@
#include <memory>
#include "rclcpp/function_traits.hpp"
#include "rcl/types.h"
namespace rclcpp
{
class QoS;
namespace subscription_traits
{
@@ -69,7 +73,20 @@ template<typename MessageT, typename Deleter>
struct extract_message_type<std::unique_ptr<MessageT, Deleter>>: extract_message_type<MessageT>
{};
template<typename CallbackT>
template<
typename CallbackT,
// Do not attempt if CallbackT is an integer (mistaken for depth)
typename = std::enable_if_t<!std::is_integral<
std::remove_cv_t<std::remove_reference_t<CallbackT>>>::value>,
// Do not attempt if CallbackT is a QoS (mistaken for qos)
typename = std::enable_if_t<!std::is_base_of<
rclcpp::QoS,
std::remove_cv_t<std::remove_reference_t<CallbackT>>>::value>,
// Do not attempt if CallbackT is a rmw_qos_profile_t (mistaken for qos profile)
typename = std::enable_if_t<!std::is_same<
rmw_qos_profile_t,
std::remove_cv_t<std::remove_reference_t<CallbackT>>>::value>
>
struct has_message_type : extract_message_type<
typename rclcpp::function_traits::function_traits<CallbackT>::template argument_type<0>>
{};

View File

@@ -25,8 +25,7 @@
#include "rcl_interfaces/msg/parameter_event.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/parameter_client.hpp"
#include "rclcpp/parameter_events_filter.hpp"
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
namespace rclcpp
@@ -100,9 +99,6 @@ private:
// Destroy the subscription for the clock topic
void destroy_clock_sub();
// Parameter Client pointer
std::shared_ptr<rclcpp::AsyncParametersClient> parameter_client_;
// Parameter Event subscription
using ParamMessageT = rcl_interfaces::msg::ParameterEvent;
using ParamSubscriptionT = rclcpp::Subscription<ParamMessageT, Alloc>;
@@ -138,6 +134,8 @@ private:
std::mutex clock_list_lock_;
// A vector to store references to associated clocks.
std::vector<rclcpp::Clock::SharedPtr> associated_clocks_;
// A handler for the use_sim_time parameter callback.
rclcpp::node_interfaces::OnSetParametersCallbackHandle::SharedPtr sim_time_cb_handler_ = nullptr;
};
} // namespace rclcpp

View File

@@ -30,6 +30,8 @@
#include "rclcpp/rate.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
#include "tracetools/tracetools.h"
#include "tracetools/utils.hpp"
#include "rcl/error_handling.h"
#include "rcl/timer.h"
@@ -58,6 +60,16 @@ public:
void
cancel();
/// Return the timer cancellation state.
/**
* \return true if the timer has been cancelled, false otherwise
* \throws std::runtime_error if the rcl_get_error_state returns 0
* \throws RCLErrorBase some child class exception based on ret
*/
RCLCPP_PUBLIC
bool
is_canceled();
RCLCPP_PUBLIC
void
reset();
@@ -123,6 +135,14 @@ public:
)
: TimerBase(clock, period, context), callback_(std::forward<FunctorT>(callback))
{
TRACEPOINT(
rclcpp_timer_callback_added,
(const void *)get_timer_handle().get(),
(const void *)&callback_);
TRACEPOINT(
rclcpp_callback_register,
(const void *)&callback_,
get_symbol(callback_));
}
/// Default destructor.
@@ -142,7 +162,9 @@ public:
if (ret != RCL_RET_OK) {
throw std::runtime_error("Failed to notify timer that callback occurred");
}
TRACEPOINT(callback_start, (const void *)&callback_, false);
execute_callback_delegate<>();
TRACEPOINT(callback_end, (const void *)&callback_);
}
// void specialization

View File

@@ -18,17 +18,14 @@
#include <chrono>
#include <functional>
#include <limits>
#include <string>
#include <vector>
#include "rclcpp/context.hpp"
#include "rclcpp/init_options.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/macros.h"
#include "rmw/rmw.h"
#ifdef ANDROID
#include <string>
#include <sstream>
namespace std
@@ -104,6 +101,7 @@ uninstall_signal_handlers();
*
* \sa rclcpp::Context::init() for more details on arguments and possible exceptions
* \returns Members of the argument vector that are not ROS arguments.
* \throws anything remove_ros_arguments can throw
*/
RCLCPP_PUBLIC
std::vector<std::string>
@@ -122,6 +120,8 @@ init_and_remove_ros_arguments(
* \param[in] argc Number of arguments.
* \param[in] argv Argument vector.
* \returns Members of the argument vector that are not ROS arguments.
* \throws anything throw_from_rcl_error can throw
* \throws rclcpp::exceptions::RCLErrorBase if the parsing fails
*/
RCLCPP_PUBLIC
std::vector<std::string>

View File

@@ -61,6 +61,17 @@ public:
size_t
get_number_of_ready_clients();
/// Get the number of ready events
/**
* Returns a value of 0 by default.
* This should be overridden if the Waitable contains one or more events.
* \return The number of events associated with the Waitable.
*/
RCLCPP_PUBLIC
virtual
size_t
get_number_of_ready_events();
/// Get the number of ready services
/**
* Returns a value of 0 by default.
@@ -88,6 +99,7 @@ public:
/**
* \param[in] wait_set A handle to the wait set to add the Waitable to.
* \return `true` if the Waitable is added successfully, `false` otherwise.
* \throws rclcpp::execptions::RCLError from rcl_wait_set_add_*()
*/
RCLCPP_PUBLIC
virtual
@@ -116,7 +128,7 @@ public:
*
* Example usage:
*
* ```
* ```cpp
* // ... create a wait set and a Waitable
* // Add the Waitable to the wait set
* bool add_ret = waitable.add_to_wait_set(wait_set);

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>0.7.0</version>
<version>0.8.4</version>
<description>The ROS client library in C++.</description>
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
<license>Apache License 2.0</license>
@@ -26,7 +26,9 @@
<depend>rcl</depend>
<depend>rcl_yaml_param_parser</depend>
<depend>rcpputils</depend>
<depend>rmw_implementation</depend>
<depend>tracetools</depend>
<exec_depend>ament_cmake</exec_depend>

View File

@@ -45,16 +45,38 @@
#endif
@{
from collections import OrderedDict
from copy import deepcopy
from rcutils.logging import feature_combinations
from rcutils.logging import get_macro_parameters
from rcutils.logging import get_suffix_from_features
from rcutils.logging import severities
from rcutils.logging import throttle_args
from rcutils.logging import throttle_params
# TODO(dhood): Implement the throttle macro using time sources available in rclcpp
excluded_features = ['named', 'throttle']
def is_supported_feature_combination(feature_combination):
is_excluded = any([ef in feature_combination for ef in excluded_features])
return not is_excluded
throttle_args['condition_before'] = 'RCUTILS_LOG_CONDITION_THROTTLE_BEFORE(clock, duration)'
del throttle_params['get_time_point_value']
throttle_params['clock'] = 'rclcpp::Clock that will be used to get the time point.'
throttle_params.move_to_end('clock', last=False)
rclcpp_feature_combinations = OrderedDict()
for combinations, feature in feature_combinations.items():
# skip feature combinations using 'named'
if 'named' in combinations:
continue
rclcpp_feature_combinations[combinations] = feature
# add a stream variant for each available feature combination
stream_arg = 'stream_arg'
for combinations, feature in list(rclcpp_feature_combinations.items()):
combinations = ('stream', ) + combinations
feature = deepcopy(feature)
feature.params[stream_arg] = 'The argument << into a stringstream'
rclcpp_feature_combinations[combinations] = feature
def get_rclcpp_suffix_from_features(features):
suffix = get_suffix_from_features(features)
if 'stream' in features:
suffix = '_STREAM' + suffix
return suffix
}@
@[for severity in severities]@
/** @@name Logging macros for severity @(severity).
@@ -62,50 +84,77 @@ def is_supported_feature_combination(feature_combination):
///@@{
#if (RCLCPP_LOG_MIN_SEVERITY > RCLCPP_LOG_MIN_SEVERITY_@(severity))
// empty logging macros for severity @(severity) when being disabled at compile time
@[ for feature_combination in [fc for fc in feature_combinations if is_supported_feature_combination(fc)]]@
@{suffix = get_suffix_from_features(feature_combination)}@
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
/// Empty logging macro due to the preprocessor definition of RCLCPP_LOG_MIN_SEVERITY.
#define RCLCPP_@(severity)@(suffix)(...)
@[ end for]@
#else
@[ for feature_combination in [fc for fc in feature_combinations if is_supported_feature_combination(fc)]]@
@{suffix = get_suffix_from_features(feature_combination)}@
@[ for feature_combination in rclcpp_feature_combinations.keys()]@
@{suffix = get_rclcpp_suffix_from_features(feature_combination)}@
// The RCLCPP_@(severity)@(suffix) macro is surrounded by do { .. } while (0)
// to implement the standard C macro idiom to make the macro safe in all
// contexts; see http://c-faq.com/cpp/multistmt.html for more information.
/**
* \def RCLCPP_@(severity)@(suffix)
* Log a message with severity @(severity)@
@[ if feature_combinations[feature_combination].doc_lines]@
@[ if rclcpp_feature_combinations[feature_combination].doc_lines]@
with the following conditions:
@[ else]@
.
@[ end if]@
@[ for doc_line in feature_combinations[feature_combination].doc_lines]@
@[ for doc_line in rclcpp_feature_combinations[feature_combination].doc_lines]@
* @(doc_line)
@[ end for]@
* \param logger The `rclcpp::Logger` to use
@[ for param_name, doc_line in feature_combinations[feature_combination].params.items()]@
@[ for param_name, doc_line in rclcpp_feature_combinations[feature_combination].params.items()]@
* \param @(param_name) @(doc_line)
@[ end for]@
@[ if 'stream' not in feature_combination]@
* \param ... The format string, followed by the variable arguments for the format string.
* It also accepts a single argument of type std::string.
@[ end if]@
*/
#define RCLCPP_@(severity)@(suffix)(logger, @(''.join([p + ', ' for p in get_macro_parameters(feature_combination).keys()]))...) \
@{params = rclcpp_feature_combinations[feature_combination].params.keys()}@
#define RCLCPP_@(severity)@(suffix)(logger@(''.join([', ' + p for p in params]))@
@[ if 'stream' not in feature_combination]@
, ...@
@[ end if]@
) \
do { \
static_assert( \
::std::is_same<typename std::remove_reference<decltype(logger)>::type, \
::std::is_same<typename std::remove_cv<typename std::remove_reference<decltype(logger)>::type>::type, \
typename ::rclcpp::Logger>::value, \
"First argument to logging macros must be an rclcpp::Logger"); \
RCUTILS_LOG_@(severity)@(suffix)_NAMED( \
@{params = get_macro_parameters(feature_combination).keys()}@
@[ if 'throttle' in feature_combination]@ \
auto get_time_point = [&c=clock](rcutils_time_point_value_t * time_point) -> rcutils_ret_t { \
try { \
*time_point = c.now().nanoseconds(); \
} catch (...) { \
RCUTILS_SAFE_FWRITE_TO_STDERR( \
"[rclcpp|logging.hpp] RCLCPP_@(severity)@(suffix) could not get current time stamp\n"); \
return RCUTILS_RET_ERROR; \
} \
return RCUTILS_RET_OK; \
}; \
@[ end if] \
@[ if 'stream' in feature_combination]@
std::stringstream ss; \
ss << @(stream_arg); \
@[ end if]@
RCUTILS_LOG_@(severity)@(get_suffix_from_features(feature_combination))_NAMED( \
@{params = ['get_time_point' if p == 'clock' and 'throttle' in feature_combination else p for p in params]}@
@[ if params]@
@(''.join([' ' + p + ', \\\n' for p in params]))@
@(''.join([' ' + p + ', \\\n' for p in params if p != stream_arg]))@
@[ end if]@
logger.get_name(), \
@[ if 'stream' not in feature_combination]@
rclcpp::get_c_string(RCLCPP_FIRST_ARG(__VA_ARGS__, "")), \
RCLCPP_ALL_BUT_FIRST_ARGS(__VA_ARGS__,"")); \
@[ else]@
"%s", rclcpp::get_c_string(ss.str())); \
@[ end if]@
} while (0)
@[ end for]@

View File

@@ -18,7 +18,6 @@ using rclcpp::executor::AnyExecutable;
AnyExecutable::AnyExecutable()
: subscription(nullptr),
subscription_intra_process(nullptr),
timer(nullptr),
service(nullptr),
client(nullptr),

View File

@@ -23,40 +23,6 @@ CallbackGroup::CallbackGroup(CallbackGroupType group_type)
: type_(group_type), can_be_taken_from_(true)
{}
const std::vector<rclcpp::SubscriptionBase::WeakPtr> &
CallbackGroup::get_subscription_ptrs() const
{
std::lock_guard<std::mutex> lock(mutex_);
return subscription_ptrs_;
}
const std::vector<rclcpp::TimerBase::WeakPtr> &
CallbackGroup::get_timer_ptrs() const
{
std::lock_guard<std::mutex> lock(mutex_);
return timer_ptrs_;
}
const std::vector<rclcpp::ServiceBase::WeakPtr> &
CallbackGroup::get_service_ptrs() const
{
std::lock_guard<std::mutex> lock(mutex_);
return service_ptrs_;
}
const std::vector<rclcpp::ClientBase::WeakPtr> &
CallbackGroup::get_client_ptrs() const
{
std::lock_guard<std::mutex> lock(mutex_);
return client_ptrs_;
}
const std::vector<rclcpp::Waitable::WeakPtr> &
CallbackGroup::get_waitable_ptrs() const
{
std::lock_guard<std::mutex> lock(mutex_);
return waitable_ptrs_;
}
std::atomic_bool &
CallbackGroup::can_be_taken_from()
@@ -76,6 +42,12 @@ CallbackGroup::add_subscription(
{
std::lock_guard<std::mutex> lock(mutex_);
subscription_ptrs_.push_back(subscription_ptr);
subscription_ptrs_.erase(
std::remove_if(
subscription_ptrs_.begin(),
subscription_ptrs_.end(),
[](rclcpp::SubscriptionBase::WeakPtr x) {return x.expired();}),
subscription_ptrs_.end());
}
void
@@ -83,6 +55,12 @@ CallbackGroup::add_timer(const rclcpp::TimerBase::SharedPtr timer_ptr)
{
std::lock_guard<std::mutex> lock(mutex_);
timer_ptrs_.push_back(timer_ptr);
timer_ptrs_.erase(
std::remove_if(
timer_ptrs_.begin(),
timer_ptrs_.end(),
[](rclcpp::TimerBase::WeakPtr x) {return x.expired();}),
timer_ptrs_.end());
}
void
@@ -90,6 +68,12 @@ CallbackGroup::add_service(const rclcpp::ServiceBase::SharedPtr service_ptr)
{
std::lock_guard<std::mutex> lock(mutex_);
service_ptrs_.push_back(service_ptr);
service_ptrs_.erase(
std::remove_if(
service_ptrs_.begin(),
service_ptrs_.end(),
[](rclcpp::ServiceBase::WeakPtr x) {return x.expired();}),
service_ptrs_.end());
}
void
@@ -97,6 +81,12 @@ CallbackGroup::add_client(const rclcpp::ClientBase::SharedPtr client_ptr)
{
std::lock_guard<std::mutex> lock(mutex_);
client_ptrs_.push_back(client_ptr);
client_ptrs_.erase(
std::remove_if(
client_ptrs_.begin(),
client_ptrs_.end(),
[](rclcpp::ClientBase::WeakPtr x) {return x.expired();}),
client_ptrs_.end());
}
void
@@ -104,6 +94,12 @@ CallbackGroup::add_waitable(const rclcpp::Waitable::SharedPtr waitable_ptr)
{
std::lock_guard<std::mutex> lock(mutex_);
waitable_ptrs_.push_back(waitable_ptr);
waitable_ptrs_.erase(
std::remove_if(
waitable_ptrs_.begin(),
waitable_ptrs_.end(),
[](rclcpp::Waitable::WeakPtr x) {return x.expired();}),
waitable_ptrs_.end());
}
void

View File

@@ -14,14 +14,6 @@
#include "rclcpp/clock.hpp"
#include <memory>
#include <utility>
#include <vector>
#include "builtin_interfaces/msg/time.hpp"
#include "rcl/time.h"
#include "rclcpp/exceptions.hpp"
#include "rcutils/logging_macros.h"
@@ -30,8 +22,8 @@ namespace rclcpp
{
JumpHandler::JumpHandler(
std::function<void()> pre_callback,
std::function<void(const rcl_time_jump_t &)> post_callback,
pre_callback_t pre_callback,
post_callback_t post_callback,
const rcl_jump_threshold_t & threshold)
: pre_callback(pre_callback),
post_callback(post_callback),
@@ -43,8 +35,7 @@ Clock::Clock(rcl_clock_type_t clock_type)
allocator_ = rcl_get_default_allocator();
auto ret = rcl_clock_init(clock_type, &rcl_clock_, &allocator_);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "could not get current time stamp");
exceptions::throw_from_rcl_error(ret, "could not get current time stamp");
}
}
@@ -63,8 +54,7 @@ Clock::now()
auto ret = rcl_clock_get_now(&rcl_clock_, &now.rcl_time_.nanoseconds);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "could not get current time stamp");
exceptions::throw_from_rcl_error(ret, "could not get current time stamp");
}
return now;
@@ -78,23 +68,23 @@ Clock::ros_time_is_active()
return false;
}
bool is_enabled;
bool is_enabled = false;
auto ret = rcl_is_enabled_ros_time_override(&rcl_clock_, &is_enabled);
if (ret != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(
exceptions::throw_from_rcl_error(
ret, "Failed to check ros_time_override_status");
}
return is_enabled;
}
rcl_clock_t *
Clock::get_clock_handle()
Clock::get_clock_handle() noexcept
{
return &rcl_clock_;
}
rcl_clock_type_t
Clock::get_clock_type()
Clock::get_clock_type() const noexcept
{
return rcl_clock_.type;
}
@@ -105,7 +95,10 @@ Clock::on_time_jump(
bool before_jump,
void * user_data)
{
rclcpp::JumpHandler * handler = static_cast<rclcpp::JumpHandler *>(user_data);
const auto * handler = static_cast<JumpHandler *>(user_data);
if (nullptr == handler) {
return;
}
if (before_jump && handler->pre_callback) {
handler->pre_callback();
} else if (!before_jump && handler->post_callback) {
@@ -113,36 +106,31 @@ Clock::on_time_jump(
}
}
rclcpp::JumpHandler::SharedPtr
JumpHandler::SharedPtr
Clock::create_jump_callback(
std::function<void()> pre_callback,
std::function<void(const rcl_time_jump_t &)> post_callback,
JumpHandler::pre_callback_t pre_callback,
JumpHandler::post_callback_t post_callback,
const rcl_jump_threshold_t & threshold)
{
// Allocate a new jump handler
auto handler = new rclcpp::JumpHandler(pre_callback, post_callback, threshold);
JumpHandler::UniquePtr handler(new JumpHandler(pre_callback, post_callback, threshold));
if (nullptr == handler) {
rclcpp::exceptions::throw_from_rcl_error(RCL_RET_BAD_ALLOC, "Failed to allocate jump handler");
throw std::bad_alloc{};
}
// Try to add the jump callback to the clock
rcl_ret_t ret = rcl_clock_add_jump_callback(&rcl_clock_, threshold,
rclcpp::Clock::on_time_jump, handler);
rcl_ret_t ret = rcl_clock_add_jump_callback(
&rcl_clock_, threshold, Clock::on_time_jump,
handler.get());
if (RCL_RET_OK != ret) {
delete handler;
handler = nullptr;
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to add time jump callback");
}
if (nullptr == handler) {
// imposible to reach here; added to make cppcheck happy
return nullptr;
exceptions::throw_from_rcl_error(ret, "Failed to add time jump callback");
}
// *INDENT-OFF*
// create shared_ptr that removes the callback automatically when all copies are destructed
return rclcpp::JumpHandler::SharedPtr(handler, [this](rclcpp::JumpHandler * handler) noexcept {
rcl_ret_t ret = rcl_clock_remove_jump_callback(&rcl_clock_, rclcpp::Clock::on_time_jump,
// TODO(dorezyuk) UB, if the clock leaves scope before the JumpHandler
return JumpHandler::SharedPtr(handler.release(), [this](JumpHandler * handler) noexcept {
rcl_ret_t ret = rcl_clock_remove_jump_callback(&rcl_clock_, Clock::on_time_jump,
handler);
delete handler;
handler = NULL;

View File

@@ -0,0 +1,35 @@
// Copyright 2019 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/rmw_implementation_specific_payload.hpp>
namespace rclcpp
{
namespace detail
{
bool
RMWImplementationSpecificPayload::has_been_customized() const
{
return nullptr != this->get_implementation_identifier();
}
const char *
RMWImplementationSpecificPayload::get_implementation_identifier() const
{
return nullptr;
}
} // namespace detail
} // namespace rclcpp

View File

@@ -0,0 +1,33 @@
// Copyright 2019 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/rmw_implementation_specific_publisher_payload.hpp>
#include "rcl/publisher.h"
namespace rclcpp
{
namespace detail
{
void
RMWImplementationSpecificPublisherPayload::modify_rmw_publisher_options(
rmw_publisher_options_t & rmw_publisher_options) const
{
// By default, do not mutate the rmw publisher options.
(void)rmw_publisher_options;
}
} // namespace detail
} // namespace rclcpp

View File

@@ -0,0 +1,33 @@
// Copyright 2019 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/rmw_implementation_specific_subscription_payload.hpp>
#include "rcl/subscription.h"
namespace rclcpp
{
namespace detail
{
void
RMWImplementationSpecificSubscriptionPayload::modify_rmw_subscription_options(
rmw_subscription_options_t & rmw_subscription_options) const
{
// By default, do not mutate the rmw subscription options.
(void)rmw_subscription_options;
}
} // namespace detail
} // namespace rclcpp

View File

@@ -65,10 +65,6 @@ Duration::Duration(const rcl_duration_t & duration)
// noop
}
Duration::~Duration()
{
}
Duration::operator builtin_interfaces::msg::Duration() const
{
builtin_interfaces::msg::Duration msg_duration;
@@ -226,4 +222,21 @@ Duration::seconds() const
return std::chrono::duration<double>(std::chrono::nanoseconds(rcl_duration_.nanoseconds)).count();
}
rmw_time_t
Duration::to_rmw_time() const
{
// reuse conversion logic from msg creation
builtin_interfaces::msg::Duration msg = *this;
rmw_time_t result;
result.sec = msg.sec;
result.nsec = msg.nanosec;
return result;
}
Duration
Duration::from_seconds(double seconds)
{
return Duration(static_cast<int64_t>(RCL_S_TO_NS(seconds)));
}
} // namespace rclcpp

View File

@@ -17,6 +17,7 @@
#include <cstdio>
#include <functional>
#include <string>
#include <vector>
using namespace std::string_literals;
@@ -39,8 +40,8 @@ NameValidationError::format_error(
return msg;
}
void
throw_from_rcl_error(
std::exception_ptr
from_rcl_error(
rcl_ret_t ret,
const std::string & prefix,
const rcl_error_state_t * error_state,
@@ -55,9 +56,9 @@ throw_from_rcl_error(
if (!error_state) {
throw std::runtime_error("rcl error state is not set");
}
std::string formated_prefix = prefix;
std::string formatted_prefix = prefix;
if (!prefix.empty()) {
formated_prefix += ": ";
formatted_prefix += ": ";
}
RCLErrorBase base_exc(ret, error_state);
if (reset_error) {
@@ -65,14 +66,30 @@ throw_from_rcl_error(
}
switch (ret) {
case RCL_RET_BAD_ALLOC:
throw RCLBadAlloc(base_exc);
return std::make_exception_ptr(RCLBadAlloc(base_exc));
case RCL_RET_INVALID_ARGUMENT:
throw RCLInvalidArgument(base_exc, formated_prefix);
return std::make_exception_ptr(RCLInvalidArgument(base_exc, formatted_prefix));
case RCL_RET_INVALID_ROS_ARGS:
return std::make_exception_ptr(RCLInvalidROSArgsError(base_exc, formatted_prefix));
default:
throw RCLError(base_exc, formated_prefix);
return std::make_exception_ptr(RCLError(base_exc, formatted_prefix));
}
}
void
throw_from_rcl_error(
rcl_ret_t ret,
const std::string & prefix,
const rcl_error_state_t * error_state,
void (* reset_error)())
{
// We expect this to either throw a standard error,
// or to generate an error pointer (which is caught
// in err, and immediately thrown)
auto err = from_rcl_error(ret, prefix, error_state, reset_error);
std::rethrow_exception(err);
}
RCLErrorBase::RCLErrorBase(rcl_ret_t ret, const rcl_error_state_t * error_state)
: ret(ret), message(error_state->message), file(error_state->file), line(error_state->line_number),
formatted_message(rcl_get_error_string().str)
@@ -112,5 +129,18 @@ RCLInvalidArgument::RCLInvalidArgument(
: RCLErrorBase(base_exc), std::invalid_argument(prefix + base_exc.formatted_message)
{}
RCLInvalidROSArgsError::RCLInvalidROSArgsError(
rcl_ret_t ret,
const rcl_error_state_t * error_state,
const std::string & prefix)
: RCLInvalidROSArgsError(RCLErrorBase(ret, error_state), prefix)
{}
RCLInvalidROSArgsError::RCLInvalidROSArgsError(
const RCLErrorBase & base_exc,
const std::string & prefix)
: RCLErrorBase(base_exc), std::runtime_error(prefix + base_exc.formatted_message)
{}
} // namespace exceptions
} // namespace rclcpp

View File

@@ -60,7 +60,11 @@ Executor::Executor(const ExecutorArgs & args)
// Store the context for later use.
context_ = args.context;
ret = rcl_wait_set_init(&wait_set_, 0, 2, 0, 0, 0, context_->get_rcl_context().get(), allocator);
ret = rcl_wait_set_init(
&wait_set_,
0, 2, 0, 0, 0, 0,
context_->get_rcl_context().get(),
allocator);
if (RCL_RET_OK != ret) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
@@ -87,6 +91,10 @@ Executor::~Executor()
}
}
weak_nodes_.clear();
for (auto & guard_condition : guard_conditions_) {
memory_strategy_->remove_guard_condition(guard_condition);
}
guard_conditions_.clear();
// Finalize the wait set.
if (rcl_wait_set_fini(&wait_set_) != RCL_RET_OK) {
@@ -124,6 +132,7 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
}
}
weak_nodes_.push_back(node_ptr);
guard_conditions_.push_back(node_ptr->get_notify_guard_condition());
if (notify) {
// Interrupt waiting to handle new node
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
@@ -131,6 +140,7 @@ Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_pt
}
}
// Add the node's notify condition to the guard condition handles
std::unique_lock<std::mutex> lock(memory_strategy_mutex_);
memory_strategy_->add_guard_condition(node_ptr->get_notify_guard_condition());
}
@@ -144,17 +154,21 @@ void
Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
bool node_removed = false;
weak_nodes_.erase(
std::remove_if(
weak_nodes_.begin(), weak_nodes_.end(),
[&](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr & i)
{
bool matched = (i.lock() == node_ptr);
node_removed |= matched;
return matched;
{
auto node_it = weak_nodes_.begin();
auto gc_it = guard_conditions_.begin();
while (node_it != weak_nodes_.end()) {
bool matched = (node_it->lock() == node_ptr);
if (matched) {
node_it = weak_nodes_.erase(node_it);
gc_it = guard_conditions_.erase(gc_it);
node_removed = true;
} else {
++node_it;
++gc_it;
}
)
);
}
}
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
has_executor.store(false);
if (notify) {
@@ -165,6 +179,7 @@ Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node
}
}
}
std::unique_lock<std::mutex> lock(memory_strategy_mutex_);
memory_strategy_->remove_guard_condition(node_ptr->get_notify_guard_condition());
}
@@ -272,9 +287,6 @@ Executor::execute_any_executable(AnyExecutable & any_exec)
if (any_exec.subscription) {
execute_subscription(any_exec.subscription);
}
if (any_exec.subscription_intra_process) {
execute_intra_process_subscription(any_exec.subscription_intra_process);
}
if (any_exec.service) {
execute_service(any_exec.service);
}
@@ -304,7 +316,7 @@ Executor::execute_subscription(
auto serialized_msg = subscription->create_serialized_message();
auto ret = rcl_take_serialized_message(
subscription->get_subscription_handle().get(),
serialized_msg.get(), &message_info);
serialized_msg.get(), &message_info, nullptr);
if (RCL_RET_OK == ret) {
auto void_serialized_msg = std::static_pointer_cast<void>(serialized_msg);
subscription->handle_message(void_serialized_msg, message_info);
@@ -316,11 +328,37 @@ Executor::execute_subscription(
rcl_reset_error();
}
subscription->return_serialized_message(serialized_msg);
} else if (subscription->can_loan_messages()) {
void * loaned_msg = nullptr;
auto ret = rcl_take_loaned_message(
subscription->get_subscription_handle().get(),
&loaned_msg,
&message_info,
nullptr);
if (RCL_RET_OK == ret) {
subscription->handle_loaned_message(loaned_msg, message_info);
} else if (RCL_RET_SUBSCRIPTION_TAKE_FAILED != ret) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"take_loaned failed for subscription on topic '%s': %s",
subscription->get_topic_name(), rcl_get_error_string().str);
rcl_reset_error();
}
ret = rcl_return_loaned_message_from_subscription(
subscription->get_subscription_handle().get(),
loaned_msg);
if (RCL_RET_OK != ret) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"return_loaned_message failed for subscription on topic '%s': %s",
subscription->get_topic_name(), rcl_get_error_string().str);
}
loaned_msg = nullptr;
} else {
std::shared_ptr<void> message = subscription->create_message();
auto ret = rcl_take(
subscription->get_subscription_handle().get(),
message.get(), &message_info);
message.get(), &message_info, nullptr);
if (RCL_RET_OK == ret) {
subscription->handle_message(message, message_info);
} else if (RCL_RET_SUBSCRIPTION_TAKE_FAILED != ret) {
@@ -334,29 +372,6 @@ Executor::execute_subscription(
}
}
void
Executor::execute_intra_process_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription)
{
rcl_interfaces::msg::IntraProcessMessage ipm;
rmw_message_info_t message_info;
rcl_ret_t status = rcl_take(
subscription->get_intra_process_subscription_handle().get(),
&ipm,
&message_info);
if (status == RCL_RET_OK) {
message_info.from_intra_process = true;
subscription->handle_intra_process_message(ipm, message_info);
} else if (status != RCL_RET_SUBSCRIPTION_TAKE_FAILED) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
"take failed for intra process subscription on topic '%s': %s",
subscription->get_topic_name(), rcl_get_error_string().str);
rcl_reset_error();
}
}
void
Executor::execute_timer(
rclcpp::TimerBase::SharedPtr timer)
@@ -409,39 +424,47 @@ Executor::execute_client(
void
Executor::wait_for_work(std::chrono::nanoseconds timeout)
{
// Collect the subscriptions and timers to be waited on
memory_strategy_->clear_handles();
bool has_invalid_weak_nodes = memory_strategy_->collect_entities(weak_nodes_);
{
std::unique_lock<std::mutex> lock(memory_strategy_mutex_);
// Clean up any invalid nodes, if they were detected
if (has_invalid_weak_nodes) {
weak_nodes_.erase(
remove_if(
weak_nodes_.begin(), weak_nodes_.end(),
[](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr i)
{
return i.expired();
// Collect the subscriptions and timers to be waited on
memory_strategy_->clear_handles();
bool has_invalid_weak_nodes = memory_strategy_->collect_entities(weak_nodes_);
// Clean up any invalid nodes, if they were detected
if (has_invalid_weak_nodes) {
auto node_it = weak_nodes_.begin();
auto gc_it = guard_conditions_.begin();
while (node_it != weak_nodes_.end()) {
if (node_it->expired()) {
node_it = weak_nodes_.erase(node_it);
memory_strategy_->remove_guard_condition(*gc_it);
gc_it = guard_conditions_.erase(gc_it);
} else {
++node_it;
++gc_it;
}
)
);
}
// clear wait set
if (rcl_wait_set_clear(&wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear wait set");
}
}
}
// clear wait set
if (rcl_wait_set_clear(&wait_set_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear wait set");
}
// The size of waitables are accounted for in size of the other entities
rcl_ret_t ret = rcl_wait_set_resize(
&wait_set_, memory_strategy_->number_of_ready_subscriptions(),
memory_strategy_->number_of_guard_conditions(), memory_strategy_->number_of_ready_timers(),
memory_strategy_->number_of_ready_clients(), memory_strategy_->number_of_ready_services());
if (RCL_RET_OK != ret) {
throw std::runtime_error(
std::string("Couldn't resize the wait set : ") + rcl_get_error_string().str);
}
// The size of waitables are accounted for in size of the other entities
rcl_ret_t ret = rcl_wait_set_resize(
&wait_set_, memory_strategy_->number_of_ready_subscriptions(),
memory_strategy_->number_of_guard_conditions(), memory_strategy_->number_of_ready_timers(),
memory_strategy_->number_of_ready_clients(), memory_strategy_->number_of_ready_services(),
memory_strategy_->number_of_ready_events());
if (RCL_RET_OK != ret) {
throw std::runtime_error(
std::string("Couldn't resize the wait set : ") + rcl_get_error_string().str);
}
if (!memory_strategy_->add_handles_to_wait_set(&wait_set_)) {
throw std::runtime_error("Couldn't fill wait set");
if (!memory_strategy_->add_handles_to_wait_set(&wait_set_)) {
throw std::runtime_error("Couldn't fill wait set");
}
}
rcl_ret_t status =
rcl_wait(&wait_set_, std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count());
@@ -493,54 +516,29 @@ Executor::get_group_by_timer(rclcpp::TimerBase::SharedPtr timer)
if (!group) {
continue;
}
for (auto & weak_timer : group->get_timer_ptrs()) {
auto t = weak_timer.lock();
if (t == timer) {
return group;
}
auto timer_ref = group->find_timer_ptrs_if(
[timer](const rclcpp::TimerBase::SharedPtr & timer_ptr) -> bool {
return timer_ptr == timer;
});
if (timer_ref) {
return group;
}
}
}
return rclcpp::callback_group::CallbackGroup::SharedPtr();
}
void
Executor::get_next_timer(AnyExecutable & any_exec)
{
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group || !group->can_be_taken_from().load()) {
continue;
}
for (auto & timer_ref : group->get_timer_ptrs()) {
auto timer = timer_ref.lock();
if (timer && timer->is_ready()) {
any_exec.timer = timer;
any_exec.callback_group = group;
node = get_node_by_group(group);
return;
}
}
}
}
}
bool
Executor::get_next_ready_executable(AnyExecutable & any_executable)
{
// Check the timers to see if there are any that are ready, if so return
get_next_timer(any_executable);
// Check the timers to see if there are any that are ready
memory_strategy_->get_next_timer(any_executable, weak_nodes_);
if (any_executable.timer) {
return true;
}
// Check the subscriptions to see if there are any that are ready
memory_strategy_->get_next_subscription(any_executable, weak_nodes_);
if (any_executable.subscription || any_executable.subscription_intra_process) {
if (any_executable.subscription) {
return true;
}
// Check the services to see if there are any that are ready

View File

@@ -27,8 +27,11 @@ using rclcpp::executors::MultiThreadedExecutor;
MultiThreadedExecutor::MultiThreadedExecutor(
const rclcpp::executor::ExecutorArgs & args,
size_t number_of_threads,
bool yield_before_execute)
: executor::Executor(args), yield_before_execute_(yield_before_execute)
bool yield_before_execute,
std::chrono::nanoseconds next_exec_timeout)
: executor::Executor(args),
yield_before_execute_(yield_before_execute),
next_exec_timeout_(next_exec_timeout)
{
number_of_threads_ = number_of_threads ? number_of_threads : std::thread::hardware_concurrency();
if (number_of_threads_ == 0) {
@@ -77,12 +80,17 @@ MultiThreadedExecutor::run(size_t)
if (!rclcpp::ok(this->context_) || !spinning.load()) {
return;
}
if (!get_next_executable(any_exec)) {
if (!get_next_executable(any_exec, next_exec_timeout_)) {
continue;
}
if (any_exec.timer) {
// Guard against multiple threads getting the same timer.
if (scheduled_timers_.count(any_exec.timer) != 0) {
// Make sure that any_exec's callback group is reset before
// the lock is released.
if (any_exec.callback_group) {
any_exec.callback_group->can_be_taken_from().store(true);
}
continue;
}
scheduled_timers_.insert(any_exec.timer);
@@ -101,5 +109,8 @@ MultiThreadedExecutor::run(size_t)
scheduled_timers_.erase(it);
}
}
// Clear the callback_group to prevent the AnyExecutable destructor from
// resetting the callback group `can_be_taken_from`
any_exec.callback_group.reset();
}
}

View File

@@ -79,6 +79,7 @@ GraphListener::start_if_not_started()
0, // number_of_timers
0, // number_of_clients
0, // number_of_services
0, // number_of_events
this->parent_context_->get_rcl_context().get(),
rcl_get_default_allocator());
if (RCL_RET_OK != ret) {
@@ -145,7 +146,7 @@ GraphListener::run_loop()
const size_t node_graph_interfaces_size = node_graph_interfaces_.size();
// Add 2 for the interrupt and shutdown guard conditions
if (wait_set_.size_of_guard_conditions < (node_graph_interfaces_size + 2)) {
ret = rcl_wait_set_resize(&wait_set_, 0, node_graph_interfaces_size + 2, 0, 0, 0);
ret = rcl_wait_set_resize(&wait_set_, 0, node_graph_interfaces_size + 2, 0, 0, 0, 0);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to resize wait set");
}

View File

@@ -42,7 +42,9 @@ InitOptions::InitOptions(const rcl_init_options_t & init_options)
InitOptions::InitOptions(const InitOptions & other)
: InitOptions(*other.get_rcl_init_options())
{}
{
shutdown_on_sigint = other.shutdown_on_sigint;
}
InitOptions &
InitOptions::operator=(const InitOptions & other)
@@ -53,6 +55,7 @@ InitOptions::operator=(const InitOptions & other)
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to copy rcl init options");
}
this->shutdown_on_sigint = other.shutdown_on_sigint;
}
return *this;
}

View File

@@ -12,54 +12,153 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/experimental/intra_process_manager.hpp"
#include <atomic>
#include <memory>
#include <mutex>
namespace rclcpp
{
namespace intra_process_manager
namespace experimental
{
static std::atomic<uint64_t> _next_unique_id {1};
IntraProcessManager::IntraProcessManager(
rclcpp::intra_process_manager::IntraProcessManagerImplBase::SharedPtr impl)
: impl_(impl)
IntraProcessManager::IntraProcessManager()
{}
IntraProcessManager::~IntraProcessManager()
{}
uint64_t
IntraProcessManager::add_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription)
IntraProcessManager::add_publisher(rclcpp::PublisherBase::SharedPtr publisher)
{
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
auto id = IntraProcessManager::get_next_unique_id();
impl_->add_subscription(id, subscription);
publishers_[id].publisher = publisher;
publishers_[id].topic_name = publisher->get_topic_name();
publishers_[id].qos = publisher->get_actual_qos().get_rmw_qos_profile();
// Initialize the subscriptions storage for this publisher.
pub_to_subs_[id] = SplittedSubscriptions();
// create an entry for the publisher id and populate with already existing subscriptions
for (auto & pair : subscriptions_) {
if (can_communicate(publishers_[id], pair.second)) {
insert_sub_id_for_pub(pair.first, id, pair.second.use_take_shared_method);
}
}
return id;
}
uint64_t
IntraProcessManager::add_subscription(SubscriptionIntraProcessBase::SharedPtr subscription)
{
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
auto id = IntraProcessManager::get_next_unique_id();
subscriptions_[id].subscription = subscription;
subscriptions_[id].topic_name = subscription->get_topic_name();
subscriptions_[id].qos = subscription->get_actual_qos();
subscriptions_[id].use_take_shared_method = subscription->use_take_shared_method();
// adds the subscription id to all the matchable publishers
for (auto & pair : publishers_) {
if (can_communicate(pair.second, subscriptions_[id])) {
insert_sub_id_for_pub(id, pair.first, subscriptions_[id].use_take_shared_method);
}
}
return id;
}
void
IntraProcessManager::remove_subscription(uint64_t intra_process_subscription_id)
{
impl_->remove_subscription(intra_process_subscription_id);
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
subscriptions_.erase(intra_process_subscription_id);
for (auto & pair : pub_to_subs_) {
pair.second.take_shared_subscriptions.erase(
std::remove(
pair.second.take_shared_subscriptions.begin(),
pair.second.take_shared_subscriptions.end(),
intra_process_subscription_id),
pair.second.take_shared_subscriptions.end());
pair.second.take_ownership_subscriptions.erase(
std::remove(
pair.second.take_ownership_subscriptions.begin(),
pair.second.take_ownership_subscriptions.end(),
intra_process_subscription_id),
pair.second.take_ownership_subscriptions.end());
}
}
void
IntraProcessManager::remove_publisher(uint64_t intra_process_publisher_id)
{
impl_->remove_publisher(intra_process_publisher_id);
std::unique_lock<std::shared_timed_mutex> lock(mutex_);
publishers_.erase(intra_process_publisher_id);
pub_to_subs_.erase(intra_process_publisher_id);
}
bool
IntraProcessManager::matches_any_publishers(const rmw_gid_t * id) const
{
return impl_->matches_any_publishers(id);
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
for (auto & publisher_pair : publishers_) {
auto publisher = publisher_pair.second.publisher.lock();
if (!publisher) {
continue;
}
if (*publisher.get() == id) {
return true;
}
}
return false;
}
size_t
IntraProcessManager::get_subscription_count(uint64_t intra_process_publisher_id) const
{
return impl_->get_subscription_count(intra_process_publisher_id);
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
auto publisher_it = pub_to_subs_.find(intra_process_publisher_id);
if (publisher_it == pub_to_subs_.end()) {
// Publisher is either invalid or no longer exists.
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Calling get_subscription_count for invalid or no longer existing publisher id");
return 0;
}
auto count =
publisher_it->second.take_shared_subscriptions.size() +
publisher_it->second.take_ownership_subscriptions.size();
return count;
}
SubscriptionIntraProcessBase::SharedPtr
IntraProcessManager::get_subscription_intra_process(uint64_t intra_process_subscription_id)
{
std::shared_lock<std::shared_timed_mutex> lock(mutex_);
auto subscription_it = subscriptions_.find(intra_process_subscription_id);
if (subscription_it == subscriptions_.end()) {
return nullptr;
} else {
return subscription_it->second.subscription;
}
}
uint64_t
@@ -84,5 +183,45 @@ IntraProcessManager::get_next_unique_id()
return next_id;
}
} // namespace intra_process_manager
void
IntraProcessManager::insert_sub_id_for_pub(
uint64_t sub_id,
uint64_t pub_id,
bool use_take_shared_method)
{
if (use_take_shared_method) {
pub_to_subs_[pub_id].take_shared_subscriptions.push_back(sub_id);
} else {
pub_to_subs_[pub_id].take_ownership_subscriptions.push_back(sub_id);
}
}
bool
IntraProcessManager::can_communicate(
PublisherInfo pub_info,
SubscriptionInfo sub_info) const
{
// publisher and subscription must be on the same topic
if (strcmp(pub_info.topic_name, sub_info.topic_name) != 0) {
return false;
}
// TODO(alsora): the following checks for qos compatibility should be provided by the RMW
// a reliable subscription can't be connected with a best effort publisher
if (
sub_info.qos.reliability == RMW_QOS_POLICY_RELIABILITY_RELIABLE &&
pub_info.qos.reliability == RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT)
{
return false;
}
// a publisher and a subscription with different durability can't communicate
if (sub_info.qos.durability != pub_info.qos.durability) {
return false;
}
return true;
}
} // namespace experimental
} // namespace rclcpp

View File

@@ -20,7 +20,7 @@ using rclcpp::memory_strategy::MemoryStrategy;
rclcpp::SubscriptionBase::SharedPtr
MemoryStrategy::get_subscription_by_handle(
std::shared_ptr<const rcl_subscription_t> subscriber_handle,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -32,16 +32,12 @@ MemoryStrategy::get_subscription_by_handle(
if (!group) {
continue;
}
for (auto & weak_subscription : group->get_subscription_ptrs()) {
auto subscription = weak_subscription.lock();
if (subscription) {
if (subscription->get_subscription_handle() == subscriber_handle) {
return subscription;
}
if (subscription->get_intra_process_subscription_handle() == subscriber_handle) {
return subscription;
}
}
auto match_subscription = group->find_subscription_ptrs_if(
[&subscriber_handle](const rclcpp::SubscriptionBase::SharedPtr & subscription) -> bool {
return subscription->get_subscription_handle() == subscriber_handle;
});
if (match_subscription) {
return match_subscription;
}
}
}
@@ -51,7 +47,7 @@ MemoryStrategy::get_subscription_by_handle(
rclcpp::ServiceBase::SharedPtr
MemoryStrategy::get_service_by_handle(
std::shared_ptr<const rcl_service_t> service_handle,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -63,11 +59,12 @@ MemoryStrategy::get_service_by_handle(
if (!group) {
continue;
}
for (auto & weak_service : group->get_service_ptrs()) {
auto service = weak_service.lock();
if (service && service->get_service_handle() == service_handle) {
return service;
}
auto service_ref = group->find_service_ptrs_if(
[&service_handle](const rclcpp::ServiceBase::SharedPtr & service) -> bool {
return service->get_service_handle() == service_handle;
});
if (service_ref) {
return service_ref;
}
}
}
@@ -77,7 +74,7 @@ MemoryStrategy::get_service_by_handle(
rclcpp::ClientBase::SharedPtr
MemoryStrategy::get_client_by_handle(
std::shared_ptr<const rcl_client_t> client_handle,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -89,11 +86,39 @@ MemoryStrategy::get_client_by_handle(
if (!group) {
continue;
}
for (auto & weak_client : group->get_client_ptrs()) {
auto client = weak_client.lock();
if (client && client->get_client_handle() == client_handle) {
return client;
}
auto client_ref = group->find_client_ptrs_if(
[&client_handle](const rclcpp::ClientBase::SharedPtr & client) -> bool {
return client->get_client_handle() == client_handle;
});
if (client_ref) {
return client_ref;
}
}
}
return nullptr;
}
rclcpp::TimerBase::SharedPtr
MemoryStrategy::get_timer_by_handle(
std::shared_ptr<const rcl_timer_t> timer_handle,
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
auto timer_ref = group->find_timer_ptrs_if(
[&timer_handle](const rclcpp::TimerBase::SharedPtr & timer) -> bool {
return timer->get_timer_handle() == timer_handle;
});
if (timer_ref) {
return timer_ref;
}
}
}
@@ -103,7 +128,7 @@ MemoryStrategy::get_client_by_handle(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
MemoryStrategy::get_node_by_group(
rclcpp::callback_group::CallbackGroup::SharedPtr group,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
if (!group) {
return nullptr;
@@ -126,7 +151,7 @@ MemoryStrategy::get_node_by_group(
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -138,11 +163,12 @@ MemoryStrategy::get_group_by_subscription(
if (!group) {
continue;
}
for (auto & weak_sub : group->get_subscription_ptrs()) {
auto sub = weak_sub.lock();
if (sub == subscription) {
return group;
}
auto match_sub = group->find_subscription_ptrs_if(
[&subscription](const rclcpp::SubscriptionBase::SharedPtr & sub) -> bool {
return sub == subscription;
});
if (match_sub) {
return group;
}
}
}
@@ -152,7 +178,7 @@ MemoryStrategy::get_group_by_subscription(
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_service(
rclcpp::ServiceBase::SharedPtr service,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -164,11 +190,12 @@ MemoryStrategy::get_group_by_service(
if (!group) {
continue;
}
for (auto & weak_serv : group->get_service_ptrs()) {
auto serv = weak_serv.lock();
if (serv && serv == service) {
return group;
}
auto service_ref = group->find_service_ptrs_if(
[&service](const rclcpp::ServiceBase::SharedPtr & serv) -> bool {
return serv == service;
});
if (service_ref) {
return group;
}
}
}
@@ -178,7 +205,7 @@ MemoryStrategy::get_group_by_service(
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_client(
rclcpp::ClientBase::SharedPtr client,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -190,11 +217,39 @@ MemoryStrategy::get_group_by_client(
if (!group) {
continue;
}
for (auto & weak_client : group->get_client_ptrs()) {
auto cli = weak_client.lock();
if (cli && cli == client) {
return group;
}
auto client_ref = group->find_client_ptrs_if(
[&client](const rclcpp::ClientBase::SharedPtr & cli) -> bool {
return cli == client;
});
if (client_ref) {
return group;
}
}
}
return nullptr;
}
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_timer(
rclcpp::TimerBase::SharedPtr timer,
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
auto timer_ref = group->find_timer_ptrs_if(
[&timer](const rclcpp::TimerBase::SharedPtr & time) -> bool {
return time == timer;
});
if (timer_ref) {
return group;
}
}
}
@@ -204,7 +259,7 @@ MemoryStrategy::get_group_by_client(
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_waitable(
rclcpp::Waitable::SharedPtr waitable,
const WeakNodeVector & weak_nodes)
const WeakNodeList & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
@@ -216,11 +271,12 @@ MemoryStrategy::get_group_by_waitable(
if (!group) {
continue;
}
for (auto & weak_waitable : group->get_waitable_ptrs()) {
auto group_waitable = weak_waitable.lock();
if (group_waitable && group_waitable == waitable) {
return group;
}
auto waitable_ref = group->find_waitable_ptrs_if(
[&waitable](const rclcpp::Waitable::SharedPtr & group_waitable) -> bool {
return group_waitable == waitable;
});
if (waitable_ref) {
return group;
}
}
}

View File

@@ -99,7 +99,11 @@ Node::Node(
const std::string & namespace_,
const NodeOptions & options)
: node_base_(new rclcpp::node_interfaces::NodeBase(
node_name, namespace_, options)),
node_name,
namespace_,
options.context(),
*(options.get_rcl_node_options()),
options.use_intra_process_comms())),
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
node_logging_(new rclcpp::node_interfaces::NodeLogging(node_base_.get())),
node_timers_(new rclcpp::node_interfaces::NodeTimers(node_base_.get())),
@@ -114,14 +118,17 @@ Node::Node(
)),
node_parameters_(new rclcpp::node_interfaces::NodeParameters(
node_base_,
node_logging_,
node_topics_,
node_services_,
node_clock_,
options.initial_parameters(),
options.use_intra_process_comms(),
options.parameter_overrides(),
options.start_parameter_services(),
options.start_parameter_event_publisher(),
options.parameter_event_qos_profile()
options.parameter_event_qos(),
options.parameter_event_publisher_options(),
options.allow_undeclared_parameters(),
options.automatically_declare_parameters_from_overrides()
)),
node_time_source_(new rclcpp::node_interfaces::NodeTimeSource(
node_base_,
@@ -215,20 +222,62 @@ Node::group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group)
return node_base_->callback_group_in_node(group);
}
const rclcpp::ParameterValue &
Node::declare_parameter(
const std::string & name,
const rclcpp::ParameterValue & default_value,
const rcl_interfaces::msg::ParameterDescriptor & parameter_descriptor,
bool ignore_override)
{
return this->node_parameters_->declare_parameter(
name,
default_value,
parameter_descriptor,
ignore_override);
}
void
Node::undeclare_parameter(const std::string & name)
{
this->node_parameters_->undeclare_parameter(name);
}
bool
Node::has_parameter(const std::string & name) const
{
return this->node_parameters_->has_parameter(name);
}
rcl_interfaces::msg::SetParametersResult
Node::set_parameter(const rclcpp::Parameter & parameter)
{
return this->set_parameters_atomically({parameter});
}
std::vector<rcl_interfaces::msg::SetParametersResult>
Node::set_parameters(
const std::vector<rclcpp::Parameter> & parameters)
Node::set_parameters(const std::vector<rclcpp::Parameter> & parameters)
{
return node_parameters_->set_parameters(parameters);
}
rcl_interfaces::msg::SetParametersResult
Node::set_parameters_atomically(
const std::vector<rclcpp::Parameter> & parameters)
Node::set_parameters_atomically(const std::vector<rclcpp::Parameter> & parameters)
{
return node_parameters_->set_parameters_atomically(parameters);
}
rclcpp::Parameter
Node::get_parameter(const std::string & name) const
{
return node_parameters_->get_parameter(name);
}
bool
Node::get_parameter(const std::string & name, rclcpp::Parameter & parameter) const
{
return node_parameters_->get_parameter(name, parameter);
}
std::vector<rclcpp::Parameter>
Node::get_parameters(
const std::vector<std::string> & names) const
@@ -236,40 +285,55 @@ Node::get_parameters(
return node_parameters_->get_parameters(names);
}
rclcpp::Parameter
Node::get_parameter(const std::string & name) const
rcl_interfaces::msg::ParameterDescriptor
Node::describe_parameter(const std::string & name) const
{
return node_parameters_->get_parameter(name);
}
bool Node::get_parameter(
const std::string & name,
rclcpp::Parameter & parameter) const
{
return node_parameters_->get_parameter(name, parameter);
auto result = node_parameters_->describe_parameters({name});
if (0 == result.size()) {
throw rclcpp::exceptions::ParameterNotDeclaredException(name);
}
if (result.size() > 1) {
throw std::runtime_error("number of described parameters unexpectedly more than one");
}
return result.front();
}
std::vector<rcl_interfaces::msg::ParameterDescriptor>
Node::describe_parameters(
const std::vector<std::string> & names) const
Node::describe_parameters(const std::vector<std::string> & names) const
{
return node_parameters_->describe_parameters(names);
}
std::vector<uint8_t>
Node::get_parameter_types(
const std::vector<std::string> & names) const
Node::get_parameter_types(const std::vector<std::string> & names) const
{
return node_parameters_->get_parameter_types(names);
}
rcl_interfaces::msg::ListParametersResult
Node::list_parameters(
const std::vector<std::string> & prefixes, uint64_t depth) const
Node::list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const
{
return node_parameters_->list_parameters(prefixes, depth);
}
rclcpp::Node::OnSetParametersCallbackHandle::SharedPtr
Node::add_on_set_parameters_callback(OnParametersSetCallbackType callback)
{
return node_parameters_->add_on_set_parameters_callback(callback);
}
void
Node::remove_on_set_parameters_callback(const OnSetParametersCallbackHandle * const callback)
{
return node_parameters_->remove_on_set_parameters_callback(callback);
}
rclcpp::Node::OnParametersSetCallbackType
Node::set_on_parameters_set_callback(rclcpp::Node::OnParametersSetCallbackType callback)
{
return node_parameters_->set_on_parameters_set_callback(callback);
}
std::vector<std::string>
Node::get_node_names() const
{
@@ -417,3 +481,9 @@ Node::get_node_options() const
{
return this->node_options_;
}
bool
Node::assert_liveliness() const
{
return node_base_->assert_liveliness();
}

View File

@@ -32,8 +32,11 @@ using rclcpp::node_interfaces::NodeBase;
NodeBase::NodeBase(
const std::string & node_name,
const std::string & namespace_,
const rclcpp::NodeOptions & options)
: context_(options.context()),
rclcpp::Context::SharedPtr context,
const rcl_node_options_t & rcl_node_options,
bool use_intra_process_default)
: context_(context),
use_intra_process_default_(use_intra_process_default),
node_handle_(nullptr),
default_callback_group_(nullptr),
associated_with_executor_(false),
@@ -42,7 +45,7 @@ NodeBase::NodeBase(
// Setup the guard condition that is notified when changes occur in the graph.
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
rcl_ret_t ret = rcl_guard_condition_init(
&notify_guard_condition_, options.context()->get_rcl_context().get(), guard_condition_options);
&notify_guard_condition_, context_->get_rcl_context().get(), guard_condition_options);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
}
@@ -63,7 +66,7 @@ NodeBase::NodeBase(
ret = rcl_node_init(
rcl_node.get(),
node_name.c_str(), namespace_.c_str(),
options.context()->get_rcl_context().get(), options.get_rcl_node_options());
context_->get_rcl_context().get(), &rcl_node_options);
if (ret != RCL_RET_OK) {
// Finalize the interrupt guard condition.
finalize_notify_guard_condition();
@@ -197,6 +200,12 @@ NodeBase::get_shared_rcl_node_handle() const
return node_handle_;
}
bool
NodeBase::assert_liveliness() const
{
return RCL_RET_OK == rcl_node_assert_liveliness(get_rcl_node_handle());
}
rclcpp::callback_group::CallbackGroup::SharedPtr
NodeBase::create_callback_group(rclcpp::callback_group::CallbackGroupType group_type)
{
@@ -253,3 +262,9 @@ NodeBase::acquire_notify_guard_condition_lock() const
{
return std::unique_lock<std::recursive_mutex>(notify_guard_condition_mutex_);
}
bool
NodeBase::get_use_intra_process_default() const
{
return use_intra_process_default_;
}

View File

@@ -14,6 +14,7 @@
#include "rclcpp/node_interfaces/node_graph.hpp"
#include <algorithm>
#include <map>
#include <string>
#include <utility>
@@ -136,9 +137,25 @@ NodeGraph::get_node_names() const
std::vector<std::string> nodes;
auto names_and_namespaces = get_node_names_and_namespaces();
for (const auto & it : names_and_namespaces) {
nodes.push_back(it.first);
}
std::transform(
names_and_namespaces.begin(),
names_and_namespaces.end(),
std::back_inserter(nodes),
[](std::pair<std::string, std::string> nns) {
std::string return_string;
if (nns.second.back() == '/') {
return_string = nns.second + nns.first;
} else {
return_string = nns.second + '/' + nns.first;
}
// Quick check to make sure that we start with a slash
// Since fully-qualified strings need to
if (return_string.front() != '/') {
return_string = "/" + return_string;
}
return return_string;
}
);
return nodes;
}
@@ -173,10 +190,12 @@ NodeGraph::get_node_names_and_namespaces() const
throw std::runtime_error(error_msg);
}
std::vector<std::pair<std::string, std::string>> node_names(node_names_c.size);
std::vector<std::pair<std::string, std::string>> node_names;
node_names.reserve(node_names_c.size);
for (size_t i = 0; i < node_names_c.size; ++i) {
if (node_names_c.data[i] && node_namespaces_c.data[i]) {
node_names.push_back(std::make_pair(node_names_c.data[i], node_namespaces_c.data[i]));
node_names.emplace_back(node_names_c.data[i], node_namespaces_c.data[i]);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -16,7 +16,6 @@
#include <string>
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/exceptions.hpp"
using rclcpp::exceptions::throw_from_rcl_error;
@@ -34,41 +33,30 @@ rclcpp::PublisherBase::SharedPtr
NodeTopics::create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
bool use_intra_process)
const rclcpp::QoS & qos)
{
// Create the MessageT specific Publisher using the factory, but store it as PublisherBase.
auto publisher = publisher_factory.create_typed_publisher(
node_base_, topic_name, publisher_options);
// Setup intra process publishing if requested.
if (use_intra_process) {
auto context = node_base_->get_context();
// Get the intra process manager instance for this context.
auto ipm = context->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
// Register the publisher with the intra process manager.
uint64_t intra_process_publisher_id =
publisher_factory.add_publisher_to_intra_process_manager(ipm.get(), publisher);
// Create a function to be called when publisher to do the intra process publish.
auto shared_publish_callback = publisher_factory.create_shared_publish_callback(ipm);
publisher->setup_intra_process(
intra_process_publisher_id,
shared_publish_callback,
ipm,
publisher_options);
}
// Return the completed publisher.
return publisher;
// Create the MessageT specific Publisher using the factory, but return it as PublisherBase.
return publisher_factory.create_typed_publisher(node_base_, topic_name, qos);
}
void
NodeTopics::add_publisher(
rclcpp::PublisherBase::SharedPtr publisher)
rclcpp::PublisherBase::SharedPtr publisher,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group)
{
// The publisher is not added to a callback group or anthing like that for now.
// It may be stored within the NodeTopics class or the NodeBase class in the future.
(void)publisher;
// Assign to a group.
if (callback_group) {
if (!node_base_->callback_group_in_node(callback_group)) {
throw std::runtime_error("Cannot create publisher, callback group not in node.");
}
} else {
callback_group = node_base_->get_default_callback_group();
}
for (auto & publisher_event : publisher->get_event_handlers()) {
callback_group->add_waitable(publisher_event);
}
// Notify the executor that a new publisher was created using the parent Node.
{
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
@@ -84,23 +72,10 @@ rclcpp::SubscriptionBase::SharedPtr
NodeTopics::create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
bool use_intra_process)
const rclcpp::QoS & qos)
{
auto subscription = subscription_factory.create_typed_subscription(
node_base_, topic_name, subscription_options);
// Setup intra process publishing if requested.
if (use_intra_process) {
auto context = node_base_->get_context();
auto intra_process_manager =
context->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
subscription_factory.setup_intra_process(
intra_process_manager, subscription, subscription_options);
}
// Return the completed subscription.
return subscription;
// Create the MessageT specific Subscription using the factory, but return a SubscriptionBase.
return subscription_factory.create_typed_subscription(node_base_, topic_name, qos);
}
void
@@ -114,19 +89,35 @@ NodeTopics::add_subscription(
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create subscription, callback group not in node.");
}
callback_group->add_subscription(subscription);
} else {
node_base_->get_default_callback_group()->add_subscription(subscription);
callback_group = node_base_->get_default_callback_group();
}
callback_group->add_subscription(subscription);
for (auto & subscription_event : subscription->get_event_handlers()) {
callback_group->add_waitable(subscription_event);
}
auto intra_process_waitable = subscription->get_intra_process_waitable();
if (nullptr != intra_process_waitable) {
// Add to the callback group to be notified about intra-process msgs.
callback_group->add_waitable(intra_process_waitable);
}
// Notify the executor that a new subscription was created using the parent Node.
{
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string("Failed to notify wait set on subscription creation: ") +
rmw_get_error_string().str
);
auto ret = rcl_trigger_guard_condition(node_base_->get_notify_guard_condition());
if (ret != RCL_RET_OK) {
using rclcpp::exceptions::throw_from_rcl_error;
throw_from_rcl_error(ret, "failed to notify wait set on subscription creation");
}
}
}
rclcpp::node_interfaces::NodeBaseInterface *
NodeTopics::get_node_base_interface() const
{
return node_base_;
}

View File

@@ -18,9 +18,12 @@
#include <memory>
#include <string>
#include <vector>
#include <utility>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/publisher_options.hpp"
#include "rclcpp/qos.hpp"
using rclcpp::exceptions::throw_from_rcl_error;
@@ -42,6 +45,9 @@ rcl_node_options_t_destructor(rcl_node_options_t * node_options)
"failed to finalize rcl node options: %s", rcl_get_error_string().str);
rcl_reset_error();
}
delete node_options;
node_options = nullptr;
}
}
} // namespace detail
@@ -62,11 +68,14 @@ NodeOptions::operator=(const NodeOptions & other)
if (this != &other) {
this->context_ = other.context_;
this->arguments_ = other.arguments_;
this->initial_parameters_ = other.initial_parameters_;
this->parameter_overrides_ = other.parameter_overrides_;
this->use_global_arguments_ = other.use_global_arguments_;
this->use_intra_process_comms_ = other.use_intra_process_comms_;
this->start_parameter_services_ = other.start_parameter_services_;
this->allocator_ = other.allocator_;
this->allow_undeclared_parameters_ = other.allow_undeclared_parameters_;
this->automatically_declare_parameters_from_overrides_ =
other.automatically_declare_parameters_from_overrides_;
}
return *this;
}
@@ -82,25 +91,48 @@ NodeOptions::get_rcl_node_options() const
node_options_->use_global_arguments = this->use_global_arguments_;
node_options_->domain_id = this->get_domain_id_from_env();
std::unique_ptr<const char *[]> c_args;
int c_argc = 0;
std::unique_ptr<const char *[]> c_argv;
if (!this->arguments_.empty()) {
c_args.reset(new const char *[this->arguments_.size()]);
if (this->arguments_.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "Too many args");
}
c_argc = static_cast<int>(this->arguments_.size());
c_argv.reset(new const char *[c_argc]);
for (std::size_t i = 0; i < this->arguments_.size(); ++i) {
c_args[i] = this->arguments_[i].c_str();
c_argv[i] = this->arguments_[i].c_str();
}
}
if (this->arguments_.size() > std::numeric_limits<int>::max()) {
throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "Too many args");
}
rmw_ret_t ret = rcl_parse_arguments(
static_cast<int>(this->arguments_.size()), c_args.get(), this->allocator_,
&(node_options_->arguments));
rcl_ret_t ret = rcl_parse_arguments(
c_argc, c_argv.get(), this->allocator_, &(node_options_->arguments));
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to parse arguments");
}
int unparsed_ros_args_count =
rcl_arguments_get_count_unparsed_ros(&(node_options_->arguments));
if (unparsed_ros_args_count > 0) {
int * unparsed_ros_args_indices = nullptr;
ret = rcl_arguments_get_unparsed_ros(
&(node_options_->arguments), this->allocator_, &unparsed_ros_args_indices);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to get unparsed ROS arguments");
}
try {
std::vector<std::string> unparsed_ros_args;
for (int i = 0; i < unparsed_ros_args_count; ++i) {
unparsed_ros_args.push_back(c_argv[unparsed_ros_args_indices[i]]);
}
throw exceptions::UnknownROSArgsError(std::move(unparsed_ros_args));
} catch (...) {
this->allocator_.deallocate(unparsed_ros_args_indices, this->allocator_.state);
throw;
}
}
}
return node_options_.get();
@@ -134,87 +166,129 @@ NodeOptions::arguments(const std::vector<std::string> & arguments)
}
std::vector<rclcpp::Parameter> &
NodeOptions::initial_parameters()
NodeOptions::parameter_overrides()
{
return this->initial_parameters_;
return this->parameter_overrides_;
}
const std::vector<rclcpp::Parameter> &
NodeOptions::initial_parameters() const
NodeOptions::parameter_overrides() const
{
return this->initial_parameters_;
return this->parameter_overrides_;
}
NodeOptions &
NodeOptions::initial_parameters(const std::vector<rclcpp::Parameter> & initial_parameters)
NodeOptions::parameter_overrides(const std::vector<rclcpp::Parameter> & parameter_overrides)
{
this->initial_parameters_ = initial_parameters;
this->parameter_overrides_ = parameter_overrides;
return *this;
}
const bool &
bool
NodeOptions::use_global_arguments() const
{
return this->node_options_->use_global_arguments;
}
NodeOptions &
NodeOptions::use_global_arguments(const bool & use_global_arguments)
NodeOptions::use_global_arguments(bool use_global_arguments)
{
this->node_options_.reset(); // reset node options to make it be recreated on next access.
this->use_global_arguments_ = use_global_arguments;
return *this;
}
const bool &
bool
NodeOptions::use_intra_process_comms() const
{
return this->use_intra_process_comms_;
}
NodeOptions &
NodeOptions::use_intra_process_comms(const bool & use_intra_process_comms)
NodeOptions::use_intra_process_comms(bool use_intra_process_comms)
{
this->use_intra_process_comms_ = use_intra_process_comms;
return *this;
}
const bool &
bool
NodeOptions::start_parameter_services() const
{
return this->start_parameter_services_;
}
NodeOptions &
NodeOptions::start_parameter_services(const bool & start_parameter_services)
NodeOptions::start_parameter_services(bool start_parameter_services)
{
this->start_parameter_services_ = start_parameter_services;
return *this;
}
const bool &
bool
NodeOptions::start_parameter_event_publisher() const
{
return this->start_parameter_event_publisher_;
}
NodeOptions &
NodeOptions::start_parameter_event_publisher(const bool & start_parameter_event_publisher)
NodeOptions::start_parameter_event_publisher(bool start_parameter_event_publisher)
{
this->start_parameter_event_publisher_ = start_parameter_event_publisher;
return *this;
}
const rmw_qos_profile_t &
NodeOptions::parameter_event_qos_profile() const
const rclcpp::QoS &
NodeOptions::parameter_event_qos() const
{
return this->parameter_event_qos_profile_;
return this->parameter_event_qos_;
}
NodeOptions &
NodeOptions::parameter_event_qos_profile(const rmw_qos_profile_t & parameter_event_qos_profile)
NodeOptions::parameter_event_qos(const rclcpp::QoS & parameter_event_qos)
{
this->parameter_event_qos_profile_ = parameter_event_qos_profile;
this->parameter_event_qos_ = parameter_event_qos;
return *this;
}
const rclcpp::PublisherOptionsBase &
NodeOptions::parameter_event_publisher_options() const
{
return parameter_event_publisher_options_;
}
NodeOptions &
NodeOptions::parameter_event_publisher_options(
const rclcpp::PublisherOptionsBase & parameter_event_publisher_options)
{
parameter_event_publisher_options_ = parameter_event_publisher_options;
return *this;
}
bool
NodeOptions::allow_undeclared_parameters() const
{
return this->allow_undeclared_parameters_;
}
NodeOptions &
NodeOptions::allow_undeclared_parameters(bool allow_undeclared_parameters)
{
this->allow_undeclared_parameters_ = allow_undeclared_parameters;
return *this;
}
bool
NodeOptions::automatically_declare_parameters_from_overrides() const
{
return this->automatically_declare_parameters_from_overrides_;
}
NodeOptions &
NodeOptions::automatically_declare_parameters_from_overrides(
bool automatically_declare_parameters_from_overrides)
{
this->automatically_declare_parameters_from_overrides_ =
automatically_declare_parameters_from_overrides;
return *this;
}

View File

@@ -12,12 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/parameter.hpp"
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
#include "rclcpp/parameter.hpp"
#include "rclcpp/node_interfaces/node_parameters.hpp"
#include "rclcpp/utilities.hpp"
using rclcpp::ParameterType;
@@ -28,11 +30,33 @@ Parameter::Parameter()
{
}
Parameter::Parameter(const std::string & name)
: name_(name), value_()
{
}
Parameter::Parameter(const std::string & name, const rclcpp::ParameterValue & value)
: name_(name), value_(value)
{
}
Parameter::Parameter(const rclcpp::node_interfaces::ParameterInfo & parameter_info)
: Parameter(parameter_info.descriptor.name, parameter_info.value)
{
}
bool
Parameter::operator==(const Parameter & rhs) const
{
return this->name_ == rhs.name_ && this->value_ == rhs.value_;
}
bool
Parameter::operator!=(const Parameter & rhs) const
{
return !(*this == rhs);
}
ParameterType
Parameter::get_type() const
{
@@ -57,6 +81,12 @@ Parameter::get_value_message() const
return value_.to_value_msg();
}
const rclcpp::ParameterValue &
Parameter::get_parameter_value() const
{
return value_;
}
bool
Parameter::as_bool() const
{

View File

@@ -30,7 +30,8 @@ AsyncParametersClient::AsyncParametersClient(
const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
const std::string & remote_node_name,
const rmw_qos_profile_t & qos_profile)
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
: node_topics_interface_(node_topics_interface)
{
if (remote_node_name != "") {
@@ -51,7 +52,7 @@ AsyncParametersClient::AsyncParametersClient(
remote_node_name_ + "/" + parameter_service_names::get_parameters,
options);
auto get_parameters_base = std::dynamic_pointer_cast<ClientBase>(get_parameters_client_);
node_services_interface->add_client(get_parameters_base, nullptr);
node_services_interface->add_client(get_parameters_base, group);
get_parameter_types_client_ = Client<rcl_interfaces::srv::GetParameterTypes>::make_shared(
node_base_interface.get(),
@@ -60,7 +61,7 @@ AsyncParametersClient::AsyncParametersClient(
options);
auto get_parameter_types_base =
std::dynamic_pointer_cast<ClientBase>(get_parameter_types_client_);
node_services_interface->add_client(get_parameter_types_base, nullptr);
node_services_interface->add_client(get_parameter_types_base, group);
set_parameters_client_ = Client<rcl_interfaces::srv::SetParameters>::make_shared(
node_base_interface.get(),
@@ -68,16 +69,17 @@ AsyncParametersClient::AsyncParametersClient(
remote_node_name_ + "/" + parameter_service_names::set_parameters,
options);
auto set_parameters_base = std::dynamic_pointer_cast<ClientBase>(set_parameters_client_);
node_services_interface->add_client(set_parameters_base, nullptr);
node_services_interface->add_client(set_parameters_base, group);
set_parameters_atomically_client_ =
Client<rcl_interfaces::srv::SetParametersAtomically>::make_shared(node_base_interface.get(),
node_graph_interface,
remote_node_name_ + "/" + parameter_service_names::set_parameters_atomically,
options);
Client<rcl_interfaces::srv::SetParametersAtomically>::make_shared(
node_base_interface.get(),
node_graph_interface,
remote_node_name_ + "/" + parameter_service_names::set_parameters_atomically,
options);
auto set_parameters_atomically_base = std::dynamic_pointer_cast<ClientBase>(
set_parameters_atomically_client_);
node_services_interface->add_client(set_parameters_atomically_base, nullptr);
node_services_interface->add_client(set_parameters_atomically_base, group);
list_parameters_client_ = Client<rcl_interfaces::srv::ListParameters>::make_shared(
node_base_interface.get(),
@@ -85,7 +87,7 @@ AsyncParametersClient::AsyncParametersClient(
remote_node_name_ + "/" + parameter_service_names::list_parameters,
options);
auto list_parameters_base = std::dynamic_pointer_cast<ClientBase>(list_parameters_client_);
node_services_interface->add_client(list_parameters_base, nullptr);
node_services_interface->add_client(list_parameters_base, group);
describe_parameters_client_ = Client<rcl_interfaces::srv::DescribeParameters>::make_shared(
node_base_interface.get(),
@@ -94,33 +96,37 @@ AsyncParametersClient::AsyncParametersClient(
options);
auto describe_parameters_base =
std::dynamic_pointer_cast<ClientBase>(describe_parameters_client_);
node_services_interface->add_client(describe_parameters_base, nullptr);
node_services_interface->add_client(describe_parameters_base, group);
}
AsyncParametersClient::AsyncParametersClient(
const rclcpp::Node::SharedPtr node,
const std::string & remote_node_name,
const rmw_qos_profile_t & qos_profile)
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
: AsyncParametersClient(
node->get_node_base_interface(),
node->get_node_topics_interface(),
node->get_node_graph_interface(),
node->get_node_services_interface(),
remote_node_name,
qos_profile)
qos_profile,
group)
{}
AsyncParametersClient::AsyncParametersClient(
rclcpp::Node * node,
const std::string & remote_node_name,
const rmw_qos_profile_t & qos_profile)
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
: AsyncParametersClient(
node->get_node_base_interface(),
node->get_node_topics_interface(),
node->get_node_graph_interface(),
node->get_node_services_interface(),
remote_node_name,
qos_profile)
qos_profile,
group)
{}
std::shared_future<std::vector<rclcpp::Parameter>>
@@ -150,8 +156,7 @@ AsyncParametersClient::get_parameters(
rcl_interfaces::msg::Parameter parameter;
parameter.name = request->names[i];
parameter.value = pvalue;
parameters.push_back(rclcpp::Parameter::from_parameter_msg(
parameter));
parameters.push_back(rclcpp::Parameter::from_parameter_msg(parameter));
}
promise_result->set_value(parameters);
@@ -211,10 +216,9 @@ AsyncParametersClient::set_parameters(
auto request = std::make_shared<rcl_interfaces::srv::SetParameters::Request>();
std::transform(parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
[](rclcpp::Parameter p) {
return p.to_parameter_msg();
}
std::transform(
parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
[](rclcpp::Parameter p) {return p.to_parameter_msg();}
);
set_parameters_client_->async_send_request(
@@ -245,10 +249,9 @@ AsyncParametersClient::set_parameters_atomically(
auto request = std::make_shared<rcl_interfaces::srv::SetParametersAtomically::Request>();
std::transform(parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
[](rclcpp::Parameter p) {
return p.to_parameter_msg();
}
std::transform(
parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
[](rclcpp::Parameter p) {return p.to_parameter_msg();}
);
set_parameters_atomically_client_->async_send_request(
@@ -411,8 +414,10 @@ SyncParametersClient::get_parameters(const std::vector<std::string> & parameter_
{
auto f = async_parameters_client_->get_parameters(parameter_names);
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_base_interface_, f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
if (
spin_node_until_future_complete(
*executor_, node_base_interface_,
f) == rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
}
@@ -435,8 +440,10 @@ SyncParametersClient::get_parameter_types(const std::vector<std::string> & param
auto f = async_parameters_client_->get_parameter_types(parameter_names);
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_base_interface_, f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
if (
spin_node_until_future_complete(
*executor_, node_base_interface_,
f) == rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
}
@@ -450,8 +457,10 @@ SyncParametersClient::set_parameters(
auto f = async_parameters_client_->set_parameters(parameters);
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_base_interface_, f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
if (
spin_node_until_future_complete(
*executor_, node_base_interface_,
f) == rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
}
@@ -465,8 +474,10 @@ SyncParametersClient::set_parameters_atomically(
auto f = async_parameters_client_->set_parameters_atomically(parameters);
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_base_interface_, f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
if (
spin_node_until_future_complete(
*executor_, node_base_interface_,
f) == rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
}
@@ -482,8 +493,10 @@ SyncParametersClient::list_parameters(
auto f = async_parameters_client_->list_parameters(parameter_prefixes, depth);
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_base_interface_, f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
if (
spin_node_until_future_complete(
*executor_, node_base_interface_,
f) == rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
}

View File

@@ -57,11 +57,16 @@ ParameterService::ParameterService(
const std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Request> request,
std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Response> response)
{
auto types = node_params->get_parameter_types(request->names);
std::transform(types.cbegin(), types.cend(),
std::back_inserter(response->types), [](const uint8_t & type) {
return static_cast<rclcpp::ParameterType>(type);
});
try {
auto types = node_params->get_parameter_types(request->names);
std::transform(
types.cbegin(), types.cend(),
std::back_inserter(response->types), [](const uint8_t & type) {
return static_cast<rclcpp::ParameterType>(type);
});
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to get parameter types: %s", ex.what());
}
},
qos_profile, nullptr);
@@ -73,12 +78,20 @@ ParameterService::ParameterService(
const std::shared_ptr<rcl_interfaces::srv::SetParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::SetParameters::Response> response)
{
std::vector<rclcpp::Parameter> pvariants;
// Set parameters one-by-one, since there's no way to return a partial result if
// set_parameters() fails.
auto result = rcl_interfaces::msg::SetParametersResult();
for (auto & p : request->parameters) {
pvariants.push_back(rclcpp::Parameter::from_parameter_msg(p));
try {
result = node_params->set_parameters_atomically(
{rclcpp::Parameter::from_parameter_msg(p)});
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to set parameter: %s", ex.what());
result.successful = false;
result.reason = ex.what();
}
response->results.push_back(result);
}
auto results = node_params->set_parameters(pvariants);
response->results = results;
},
qos_profile, nullptr);
@@ -91,13 +104,21 @@ ParameterService::ParameterService(
std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Response> response)
{
std::vector<rclcpp::Parameter> pvariants;
std::transform(request->parameters.cbegin(), request->parameters.cend(),
std::back_inserter(pvariants),
[](const rcl_interfaces::msg::Parameter & p) {
return rclcpp::Parameter::from_parameter_msg(p);
});
auto result = node_params->set_parameters_atomically(pvariants);
response->result = result;
std::transform(
request->parameters.cbegin(), request->parameters.cend(),
std::back_inserter(pvariants),
[](const rcl_interfaces::msg::Parameter & p) {
return rclcpp::Parameter::from_parameter_msg(p);
});
try {
auto result = node_params->set_parameters_atomically(pvariants);
response->result = result;
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"), "Failed to set parameters atomically: %s", ex.what());
response->result.successful = false;
response->result.reason = "One or more parameters wer not declared before setting";
}
},
qos_profile, nullptr);
@@ -109,8 +130,12 @@ ParameterService::ParameterService(
const std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Response> response)
{
auto descriptors = node_params->describe_parameters(request->names);
response->descriptors = descriptors;
try {
auto descriptors = node_params->describe_parameters(request->names);
response->descriptors = descriptors;
} catch (const rclcpp::exceptions::ParameterNotDeclaredException & ex) {
RCLCPP_WARN(rclcpp::get_logger("rclcpp"), "Failed to describe parameters: %s", ex.what());
}
},
qos_profile, nullptr);

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