Compare commits

...

231 Commits

Author SHA1 Message Date
Michael Carroll
f03f2c2828 @wip
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-18 01:59:11 +00:00
Michael Carroll
aff46a4abf Merge branch 'mjcarroll/rclcpp_waitset_executor_abi_only' into mjcarroll/rclcpp_waitset_executor
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-17 23:56:18 +00:00
Michael Carroll
acfc0e29fb Add PIMPL
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-17 23:45:07 +00:00
Michael Carroll
e3f692bf51 Merge branch 'mjcarroll/rclcpp_waitset_executor_abi_only' into mjcarroll/rclcpp_waitset_executor
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-17 22:30:02 +00:00
Michael Carroll
ad5931b129 Picking ABI-incompatible executor changes
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-17 22:21:54 +00:00
Michael Carroll
80077ddb82 Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-17 18:55:55 +00:00
Alberto Soragna
a7048e115d add events-executor and timers-manager in rclcpp (#2155)
* add events-executor and timers-manager in rclcpp

fix check for execute_timers_separate_thread; ignore on_ready_callback if asked to execute a timer;  reduce usage of void pointers

* have the executor notify waitable fiddle with guard condition callbacks only if necessary

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2023-04-17 14:33:58 -04:00
Michael Carroll
ffdb562927 Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-17 01:13:38 +00:00
Michael Carroll
e3d9d819af Create common structures for executors to use (#2143)
* Deprecate callback_group call taking context

* Add base executor objects that can be used by implementors

* Template common operations

* Add callback to EntitiesCollector constructor
* Make function to check automatically added callback groups take a list

* Make executor own the notify waitable

* Add pending queue to collector, remove from waitable

Also change node's get_guard_condition to return shared_ptr

* Change interrupt guard condition to shared_ptr

Check if guard condition is valid before adding it to the waitable

* Make get_notify_guard_condition follow API tick-tock

* Improve callback group tick-tocking

* Add thread safety annotations and make locks consistent

* Remove the "add_valid_node" API

* Only notify if the trigger condition is valid

* Only trigger if valid and needed

Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-14 14:56:41 -04:00
Michael Babenko
6ffd54d61d Support publishing loaned messages in LifecyclePublisher (#2159)
* Support loaned messages in LifecyclePublisher

Signed-off-by: Michael Babenko <mbabenko@wayve.ai>
2023-04-14 13:15:03 -04:00
methylDragon
fd7a0dc219 Implement deliver message kind (#2168)
* Implement deliver message kind

Signed-off-by: methylDragon <methylDragon@gmail.com>

* Add examples to docstring

Signed-off-by: methylDragon <methylDragon@gmail.com>

---------

Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-13 20:08:27 -07:00
Michael Carroll
039d2b19b5 Merge branch 'mjcarroll/executor_structures' into mjcarroll/rclcpp_waitset_executor 2023-04-13 21:15:20 +00:00
Michael Carroll
ab3bbf4e16 Merge branch 'rolling' into mjcarroll/executor_structures 2023-04-13 21:14:44 +00:00
Michael Carroll
2c3a36cdcc Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-13 21:13:49 +00:00
Chris Lalancette
eaf6edd6b2 20.0.0 2023-04-13 19:48:07 +00:00
Chris Lalancette
d478525778 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-04-13 19:48:00 +00:00
ymski
82a693e028 applied tracepoints for ring_buffer (#2091)
applied tracepoints for intra_publish
add tracepoints for linking buffer and subscription

Signed-off-by: Kodai Yamasaki <114902604+ymski@users.noreply.github.com>
2023-04-13 15:44:02 -04:00
Michael Carroll
838d1ae214 Merge branch 'rolling' into mjcarroll/executor_structures 2023-04-13 09:57:17 -05:00
Michael Carroll
49962fd9e2 Merge branch 'rolling' into mjcarroll/rclcpp_waitset_executor 2023-04-13 09:56:41 -05:00
Michael Carroll
64cba3b781 Restore single threaded executor
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-13 09:56:24 -05:00
methylDragon
b8173e28c6 Dynamic Subscription (REP-2011 Subset): Stubs for rclcpp (#2165)
* Implement subscription base changes

* Add stubbed out classes

Signed-off-by: methylDragon <methylDragon@gmail.com>
2023-04-13 10:32:33 -04:00
Michael Carroll
fcc33e9692 Fix spin_some/spin_all implementation
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-12 20:00:48 -05:00
Michael Carroll
43c8f45407 Merge branch 'mjcarroll/executor_structures' into mjcarroll/rclcpp_waitset_executor
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-12 19:27:19 -05:00
Michael Carroll
d9a92061c5 Only trigger if valid and needed
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-12 19:24:09 -05:00
Michael Carroll
855c64dc3f Only notify if the trigger condition is valid
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-12 18:51:46 -05:00
Michael Carroll
4b2e280e9e Merge branch 'mjcarroll/executor_structures' into mjcarroll/rclcpp_waitset_executor
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-12 18:21:39 -05:00
Michael Carroll
6379f0cfa0 Remove the "add_valid_node" API
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-12 18:11:54 -05:00
Michael Carroll
3a80b86164 Don't enforce removing callback groups before nodes
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-12 13:07:07 -05:00
Michael Carroll
cd56124c14 Change ready_executables signature back
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-12 12:29:04 -05:00
Michael Carroll
1ad6ad66cf Fix constructor test
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-12 08:49:47 -05:00
Emerson Knapp
3088b536cc Add type_hash to cpp TopicEndpointInfo (#2137)
* Add type_hash to cpp TopicEndpointInfo

Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>
2023-04-12 08:57:57 -04:00
Michael Carroll
5f9695afb0 Trigger the intraprocess guard condition with data (#2164)
If the intraprocess buffer still has data after taking, re-trigger the
guard condition to ensure that the executor will continue to service it,
even if incoming publications stop.


Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 19:20:21 -05:00
Michael Carroll
a2f397715e Fix assert
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 17:53:53 -05:00
Michael Carroll
38387e0a29 Fix multithreaded test
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 16:24:07 -05:00
Michael Carroll
7a81a8fb8a Restore more tests
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 15:21:58 -05:00
Michael Carroll
38c80fd352 Merge branch 'mjcarroll/executor_structures' into mjcarroll/rclcpp_waitset_executor
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 15:19:44 -05:00
Michael Carroll
31d25fc0f7 Restore static single threaded tests that weren't working before
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 14:56:58 -05:00
Michael Carroll
5c70cb6808 reduce diff and lint
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 13:10:44 -05:00
Michael Carroll
03471fc97a Back to weak_ptr and reduce test time
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 12:55:37 -05:00
Michael Carroll
985c1f4e81 Restore tests
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 12:21:58 -05:00
Michael Carroll
200f733a8f Uncrustify
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 12:13:23 -05:00
Michael Carroll
d2d271b8a0 Reduce diff
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 12:09:34 -05:00
Michael Carroll
0c3c8999a6 Reducing diff
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 12:04:19 -05:00
Michael Carroll
e52b2420d6 Remove tracepoints
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 10:37:48 -05:00
Michael Carroll
d8ff831e8f Trace points
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 10:25:49 -05:00
Michael Carroll
cd7aaba5ca Address reviewer feedback
Signed-off-by: Michael Carroll <michael@openrobotics.org>
2023-04-11 10:22:21 -05:00
Michael Carroll
20d3ccaf57 Re-trigger guard condition if buffer has data
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-06 19:47:42 +00:00
Michael Carroll
3db897ad2f Avoid many small function calls when building executables
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-06 16:17:45 +00:00
Michael Carroll
ae9a845620 Reset callback groups for multithreaded executor
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-06 15:16:06 +00:00
Michael Carroll
0c912b6a6a @wip
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-05 18:35:56 +00:00
Michael Carroll
8782fffaf7 Merge branch 'mjcarroll/executor_structures' into mjcarroll/rclcpp_waitset_executor 2023-04-04 14:15:21 +00:00
Michael Carroll
c4b658935f Add thread safety annotations and make locks consistent
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-04 14:13:12 +00:00
Michael Carroll
debe396b71 Address reviewer feedback
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-04 13:15:28 +00:00
Michael Carroll
58093288f8 Don't lock twice
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-03 20:47:38 +00:00
Michael Carroll
87f41bff1d Improve callback group tick-tocking
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-03 20:44:42 +00:00
Michael Carroll
0ae0bea1fa Make get_notify_guard_condition follow API tick-tock
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-03 20:40:18 +00:00
Michael Carroll
0a9c9a6403 Fix add_node and add more tests
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-03 20:16:37 +00:00
Michael Carroll
1b1a9154d5 Don't exchange atomic twice
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-03 19:30:12 +00:00
Michael Carroll
974e845582 Utilize rclcpp::WaitSet as part of the executors
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-03 18:55:34 +00:00
Michael Carroll
6267741212 Lint and docs
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-03 18:20:10 +00:00
Michael Carroll
9dd48ce6c2 Change interrupt guard condition to shared_ptr
Check if guard condition is valid before adding it to the waitable

Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-03 17:55:36 +00:00
Michael Carroll
a6c4c1b435 Add pending queue to collector, remove from waitable
Also change node's get_guard_condition to return shared_ptr

Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-04-03 17:09:11 +00:00
Yadu
71a06985af Minor grammar fix (#2149)
Signed-off-by: Yadunund <yadunund@openrobotics.org>
2023-04-03 09:53:49 -07:00
Christopher Wecht
73d555b402 Fix unnecessary allocations in executor.cpp (#2135)
Signed-off-by: Christopher Wecht <cwecht@mailbox.org>
2023-04-03 09:01:39 -05:00
Michael Carroll
653d1a3868 Make executor own the notify waitable
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-03-31 01:45:19 +00:00
Michael Carroll
e173e5a62a Lint and docs
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-03-30 19:40:10 +00:00
Michael Carroll
9695eaaee7 Merge branch 'rolling' into mjcarroll/executor_structures 2023-03-30 19:20:54 +00:00
Michael Carroll
89f210687d Address reviewer feedback and fix templates
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-03-30 19:20:07 +00:00
Tomoya Fujita
a5368e6fe4 add Logger::get_effective_level(). (#2141)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-03-29 21:50:17 -07:00
Michael Carroll
a524bf016a Lint
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-03-29 20:13:01 +00:00
Michael Carroll
173ffd686f Address reviewer feedback:
* Add callback to EntitiesCollector constructor
* Make function to check automatically added callback groups take a list

Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-03-29 20:04:35 +00:00
Michael Carroll
2426056b9c Template common operations
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-03-29 19:42:48 +00:00
Michael Carroll
9099635103 Add base executor objects that can be used by implementors
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-03-29 17:37:35 +00:00
Michael Carroll
2bf88de912 Deprecate callback_group call taking context
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-03-29 17:37:30 +00:00
Emerson Knapp
20e9cd17f6 Remove deprecated header (#2139)
Signed-off-by: Emerson Knapp <emerson.b.knapp@gmail.com>
2023-03-26 08:45:36 -04:00
Barry Xu
cb08c79a0a Implement matched event (#2105)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2023-03-22 08:36:47 -05:00
Tomoya Fujita
bff59925de extract the result response before the callback is issued. (#2132)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-03-21 14:11:48 -07:00
Tomoya Fujita
1a796b5515 use allocator via init_options argument. (#2129)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2023-03-17 12:35:34 -07:00
Chris Lalancette
cbd48c0eb4 Fixes to silence some clang warnings. (#2127)
This does 2 separate things:

* Adds (void)unused_variable things where needed.
* Stops doing some checks on moved-from items in tests.

With this in place, most of the remaining clang static analysis
warnings are gone.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-03-14 15:38:20 -04:00
Michael Carroll
18dd05fba5 Documentation improvements on the executor (#2125)
Signed-off-by: Michael Carroll <mjcarroll@intrinsic.ai>
2023-03-13 17:23:43 -05:00
Barry Xu
232262c02a Avoid losing waitable handles while using MultiThreadedExecutor (#2109)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2023-03-13 10:10:24 -07:00
Chris Lalancette
6c4afb3a70 Hook up the incompatible type event inside of rclcpp (#2069)
* Rename QOSEventHandler* to EventHandler.

We are going to be using it for more than just QOS events, so
rename it to just EventHandler and EventHandlerBase for now.
Leave the old names in place but deprecated.

* Rename qos_event.hpp -> event_handler.hpp
* Revamp incompatible_qos callback setting.
* Add in incompatible type implementation.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-03-13 09:33:30 -04:00
Chris Lalancette
b6a803f48c Update all rclcpp packages to C++17. (#2121)
The main reason to do this is so that we can compile rclcpp
with the clang static analyzer.  As of clang++-14 (what is in
Ubuntu 22.04), the default still seems to be C++14, so we need
to specify C++17 so that new things in the rclcpp headers work
properly.

Further, due to reasons I don't fully understand, I needed to
set CMAKE_CXX_STANDARD_REQUIRED in order for clang to really use
that version.  So set this as well.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-03-07 14:43:58 -05:00
Chris Lalancette
dbe555a3c3 Use the correct macro for LifecycleNode::get_fully_qualified_name (#2117)
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-03-06 12:45:24 -05:00
mauropasse
1a9b117d53 Fix clang warning: bugprone-use-after-move (#2116)
Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
2023-03-05 12:53:14 -05:00
Steve Macenski
11778f5048 add get_fully_qualified_name to rclcpp_lifecycle (#2115)
Signed-off-by: stevemacenski <stevenmacenski@gmail.com>
2023-03-04 16:13:56 -05:00
Nathan Wiebe Neufeldt
399f4df739 Fix the GoalUUID to_string representation (#1999)
* Fix expected results of the goal_uuid_to_string test

Signed-off-by: Nathan Wiebe Neufeldt <nwiebeneufeldt@clearpath.ai>
Co-authored-by: Chris Lalancette <clalancette@gmail.com>
2023-03-02 07:45:22 -05:00
Chris Lalancette
e7890b7c62 19.3.0 2023-03-01 14:27:21 +00:00
Chris Lalancette
b589b490c3 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-03-01 14:27:01 +00:00
Christophe Bedard
72c05ecee0 Fix memory leak in tracetools::get_symbol() (#2104)
Signed-off-by: Christophe Bedard <christophe.bedard@apex.ai>
2023-02-28 16:42:39 -06:00
Brian
968ce0a03f Service introspection (#1985)
* Implementation of service introspection.

To do this, we add a new method on the Client and
Service classes that allows the user to change the
introspection method at runtime.  These end up calling
into the rcl layer to do the actual configuration,
at which point service introspection messages will be
sent as configured.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
Signed-off-by: Brian Chen <brian.chen@openrobotics.org>
2023-02-28 13:43:39 -05:00
Miguel Company
3062dec77e Allow publishing borrowed messages with intra-process enabled (#2108)
Signed-off-by: Miguel Company <MiguelCompany@eprosima.com>
2023-02-24 14:48:23 -08:00
Chen Lihui
9ea55ba620 to fix flaky test about TestTimeSource.callbacks (#2111)
Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2023-02-24 15:16:02 -05:00
Chris Lalancette
f57f4077fd 19.2.0 2023-02-24 18:27:42 +00:00
Chris Lalancette
006d1fa1df Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-02-24 18:27:32 +00:00
Chen Lihui
b1e834a8df to create a sublogger while getting child of Logger (#1717)
* to create a sublogger while getting child of Logger

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2023-02-24 11:03:10 -05:00
Silvio Traversaro
28e4b1bd73 Fix documentation of Context class (#2107)
Signed-off-by: Silvio Traversaro <silvio@traversaro.it>
2023-02-16 16:32:10 -05:00
Alberto Soragna
35a5d6a66c fixes for rmw callbacks in qos_event class (#2102)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2023-02-16 07:56:47 -05:00
Chris Lalancette
01b19247f1 19.1.0 2023-02-14 14:36:12 +00:00
Chris Lalancette
15ea024d48 Changelog.
Signed-off-by: Chris Lalancette <clalancette@gmail.com>
2023-02-14 14:35:58 +00:00
mauropasse
ce5a2614fa Add support for timers on reset callback (#1979)
Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Alberto Soragna <alberto.soragna@gmail.com>
2023-02-10 08:25:52 -08:00
Chen Lihui
beda0966db Topic node guard condition in executor (#2074)
* add a test

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

* rollback the node guard condition for exectuor

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

---------

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2023-02-10 11:39:27 +00:00
Barry Xu
1fd5a96561 Fix bug on the disorder of calling shutdown callback (#2097)
* Fix bug on the disorder of calling shutdown callback

Signed-off-by: Barry Xu <barry.xu@sony.com>
2023-02-06 12:46:47 -05:00
Shane Loretz
be8c5d01c6 19.0.0
Signed-off-by: Shane Loretz <sloretz@openrobotics.org>
2023-01-30 18:26:56 -08:00
Shane Loretz
97c5c11c25 Add default constructor to NodeInterfaces (#2094)
* Add default constructor to NodeInterfaces

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

* Remove unnecessary NOLINT

Signed-off-by: Shane Loretz <sloretz@google.com>

---------

Signed-off-by: Shane Loretz <sloretz@openrobotics.org>
Signed-off-by: Shane Loretz <sloretz@google.com>
2023-01-30 13:47:11 -08:00
Chris Lalancette
7d660acc05 Fix clock state cached time to be a copy, not a reference. (#2092)
That is, in cache_last_msg(), we were just taking a shared_ptr
reference.  While this is pretty fast, it also means that
any changes to that message would be reflected internally.
Instead, make a new shared pointer out of that message when
it comes in, which essentially causes this to be a copy.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-01-27 14:16:31 -05:00
Michael Carroll
ab71df3ce1 Improve component_manager_isolated shutdown (#2085)
This eliminates a potential hang when the isolated container is being
shutdown via the rclcpp SignalHandler.

Signed-off-by: Michael Carroll <michael@openrobotics.org>
Co-authored-by: Chris Lalancette <clalancette@openrobotics.org>
2023-01-23 09:46:50 -06:00
Alexander Hans
37adc03c11 Fix -Wmaybe-uninitialized warning (#2081)
* Fix -Wmaybe-uninitialized warning

gcc 12 warned about `callback_list_ptr` potentially being uninitialized
when compiling in release mode (i.e., with `-O2`). Since `shutdown_type`
is a compile-time parameter, we fix the warning by enforcing the
decision at compile time.

Signed-off-by: Alexander Hans <ahans@users.noreply.github.com>
2023-01-17 14:50:44 -05:00
Chris Lalancette
3db2ece145 Fix the keep_last warning when using system defaults. (#2082)
If the user creates SystemDefaultsQoS setting, they are
explicitly asking for SystemDefaults.  In that case, we should
*not* warn, and just let the underlying RMW choose what it
wants.  Implement that here by passing a boolean parameter
through that decides when we should print out the warning,
and then skip printing that warning when SystemDefaultsQoS
is created.

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

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-01-13 13:47:13 -08:00
Chris Lalancette
1bbb03302a Add in a fix for older compilers. (#2075)
* Add in a fix for older compilers.

The addition of the NodeInterfaces class made it stop compiling
with older compilers (such as gcc 9.4.0 on Ubuntu 20.04).
The error has to do with calling the copy constructor on
rclcpp::Node, which is deleted.  Work around this by removing
the NodeInterfaces shared_ptr constructor, which isn't technically
needed.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2023-01-04 08:49:57 -05:00
Shane Loretz
c63f9eae0f 18.0.0
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2022-12-29 10:30:32 -08:00
methylDragon
a73e0bd23b Implement Unified Node Interface (NodeInterfaces class) (#2041)
Co-authored-by: William Woodall <william+github@osrfoundation.org>
Co-authored-by: methylDragon <methylDragon@gmail.com>
2022-12-28 17:59:02 -08:00
jrutgeer
c5491a4e58 API url info fix (#2071)
* Fixed API url info.

Signed-off-by: jrutgeer <johan.rutgeerts@lancewood.eu>

* Fix api url info in README

Signed-off-by: jrutgeer <johan.rutgeerts@lancewood.eu>

Signed-off-by: jrutgeer <johan.rutgeerts@lancewood.eu>
2022-12-23 10:27:42 -03:00
Alberto Soragna
3fb012e2e9 do not throw exception if trying to dequeue an empty intra-process buffer (#2061)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2022-12-21 10:11:24 +00:00
methylDragon
9c1c9896a3 Move event callback binding to PublisherBase and SubscriptionBase (#2066) 2022-12-19 18:21:40 -08:00
methylDragon
c091fe1a45 Implement validity checks for rclcpp::Clock (#2040) 2022-12-19 18:18:54 -08:00
mauropasse
a20a295a3b Set explicitly callback type (#2059)
* Set explicitly callback type

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Revert "Set explicitly callback type"

This reverts commit dfb4c54adb.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Add type info to cpp_callback_trampoline

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
2022-12-16 09:44:59 +00:00
Mateusz Szczygielski
86335dd4ac Fix logging macros to build with msvc and cpp20 (#2063)
Signed-off-by: Mateusz Szczygielski <mateusz.szczygielski@robotec.ai>

Signed-off-by: Mateusz Szczygielski <mateusz.szczygielski@robotec.ai>
2022-12-13 15:40:38 -03:00
Jeffery Hsu
432bf21f02 Add clock type to node_options (#1982)
* add clock type to node_options and change node/lifecycle_node constructor accordingly

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

* Modify TimeSource::NodeState class to work with different clock types. Add test cases.

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

* Change on_parameter_event to output RCLCPP_ERROR and check
ros_time_active_ in ClocksState::attachClock()

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

* Remove a redundant include

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

* Disallow setting use_sim_time to true if a clock_type can't support it.
Following the reasoning in c54a6f1cd2, on_set_parameters doesn't try to enforce use_sim_time to be of boolean type explicitly.

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

* minior style change

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

* remove reason string for successful result

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

Signed-off-by: Jeffery Hsu <jefferyyjhsu@gmail.com>
2022-12-13 09:11:09 -08:00
andrei
1ac37b692c fix nullptr dereference in prune_requests_older_than (#2008)
* fix nullptr dereference in prune_requests_older_than

Signed-off-by: akela1101 <akela1101@gmail.com>

* add tests for prune_requests_older_than

Signed-off-by: akela1101 <akela1101@gmail.com>

* Update rclcpp/test/rclcpp/test_client.cpp

Co-authored-by: Chen Lihui <lihui.chen@sony.com>
Signed-off-by: akela1101 <akela1101@gmail.com>

Signed-off-by: akela1101 <akela1101@gmail.com>
Co-authored-by: Chen Lihui <lihui.chen@sony.com>
2022-12-13 10:31:14 -03:00
methylDragon
338eed0c06 Remove templating on to_rcl_subscription_options (#2056) 2022-12-06 11:19:40 -08:00
Lei Liu
66b19448b0 Fix SharedFuture from async_send_request never becomes valid (#2044)
Signed-off-by: Lei Liu <Lei.Liu.AP@sony.com>
2022-12-01 18:17:43 -08:00
Chris Lalancette
a00ef22d6d Add in a warning for a KeepLast depth of 0. (#2048)
* Add in a warning for a KeepLast depth of 0.

It really doesn't make much sense to have a KeepLast depth of 0;
no data could possibly be stored.  Indeed, the underlying DDS
implementations don't actually support this.  It currently "works"
because a KeepLast depth of 0 is assumed to be system default,
so the RMW layer typically chooses "1" instead.  But this isn't
something we should be encouraging users to do, so add a warning.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-11-30 08:31:53 -05:00
Shane Loretz
e5d20474da Mark rclcpp::Clock::now() as const (#2050)
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2022-11-23 08:44:34 -08:00
Chen Lihui
91bc312190 fix a case that not throw ParameterUninitializedException (#2036)
* fix a case that not throw ParameterUninitializedException

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

* catch ParameterUninitializedException exception while calling get_parameters in service callback

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

* update doc

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

* add one more test

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

* explicitly use this to call a method inside the class itself

Co-authored-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
Signed-off-by: Chen Lihui <lihui.chen@sony.com>

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
Co-authored-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2022-11-09 07:50:01 -08:00
Audrow Nash
82f1fbff0b [rolling] Update maintainers (#2043)
* Update maintainers to Ivan Paunovic, Michel Hidalgo, and William Woodall

Signed-off-by: Audrow Nash <audrow@openrobotics.org>
2022-11-08 08:21:43 -05:00
Chris Lalancette
7c6785176a 17.1.0 2022-11-02 18:06:21 +00:00
Chris Lalancette
586932ebf8 Changelog.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-11-02 18:06:12 +00:00
Tomoya Fujita
edbfe1404b LifecycleNode on_configure doc fix. (#2034)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2022-10-28 09:17:47 -07:00
Tomoya Fujita
6f22443513 MultiThreadExecutor number of threads is at least 2+ in default. (#2032)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2022-10-28 09:17:23 -07:00
Tomoya Fujita
e5d13a2478 Bugfix 20210810 get current state (#1756)
* protect state_machine_ with mutex lock.

protect state_handle_ with mutex lock.

reconsider mutex lock scope.

remove mutex lock from constructors.

lock just once during initialization of LifecycleNodeInterfaceImpl.

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

* Move updating of current_state to right after initialization.

This is slightly more correct in the case that registering one
of the services fails.

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

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
Co-authored-by: Chris Lalancette <clalancette@openrobotics.org>
2022-10-28 09:15:14 -07:00
Chen Lihui
d119157948 Fix bug that a callback not reached (#1640)
* Add a test case

a subscriber on a new executor with a callback group triggered to receive a message

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

* fix flaky and not to use spin_some

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

* update comment

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

* update for not using anti-pattern source code

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

* add a notify guard condition for callback group

Co-authored-by: William Woodall <william@osrfoundation.org>
Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* Notify guard condition of Node not to be used in Executor

it is only for the waitset of GraphListener

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

* put code in the try catch

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

* defer to create guard condition

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

* use context directly for the create function

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

* cpplint

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

* fix that some case might call add_node after shutdown

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

* nitpick and fix some potential bug

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

* add sanity check as some case might not create notify guard condition after shutdown

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

* Cleanup includes.

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

* remove destroy method

make a callback group can only create one guard condition

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

* remove limitation that guard condition can not be re-created in callback group

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

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
Co-authored-by: William Woodall <william@osrfoundation.org>
Co-authored-by: Chris Lalancette <clalancette@openrobotics.org>
2022-10-26 14:18:21 -07:00
Alexis Paques
85c0af4fa0 Set the minimum number of threads of the Multithreaded executor to 2 (#2030)
* Set the minimum number of threads of the Multithreaded executor to 2

Signed-off-by: Alexis Paques <paa1ti@bosch.com>
2022-10-25 13:33:14 -04:00
Chris Lalancette
2d32d03ba3 Make lifecycle impl get_current_state() const. (#2031)
We should only need to update the current state when it changes,
so we do that in the change_state method (which is not const).
Then we can just return the current_state_ object in
get_current_state, and then mark it as const.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-10-24 17:14:12 -04:00
Chris Lalancette
28890bf126 Cleanup the lifecycle implementation (#2027)
* Split lifecycle_node_interface_impl into header and implementation.

There is no reason it should all be in the header file.  No
functional change.

* Mark LifecycleNodeInterfaceImpl as final.

* Update documentation about return codes.

* Mark a bunch of LifecycleNodeInterfaceImpl methods as const.

* Make most of LifecycleNodeInterfaceImpl private.

* Mark some LifecycleNode methods as const.

* Disable copies on LifecycleNodeInterfaceImpl.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-10-24 08:43:17 -04:00
uupks
b9b1468d15 check thread whether joinable before join (#2019)
Signed-off-by: uupks <uupks0325@gmail.com>
2022-10-13 08:30:02 -04:00
Cristóbal Arroyo
3aca271ef5 Set cpplint test timeout to 3 minutes (#2022)
Signed-off-by: Crola1702 <cristobal.arroyo@ekumenlabs.com>
2022-10-10 08:17:24 -05:00
Chris Lalancette
dec766228d Cleanup the rclcpp_lifecycle dependencies. (#2021)
That is, make sure they are all listed in package.xml, found
in the CMakeLists.txt, and properly included where they are
used.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-10-04 14:04:12 -04:00
Chris Lalancette
95837d34f1 Make sure to include-what-you-use in the node_interfaces. (#2018)
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-09-29 07:54:41 -04:00
mauropasse
145933b037 Do not clear entities callbacks on destruction (#2002)
* Do not clear entities callbacks on destruction

Removing these clearings since they were not necessary,
since the objects are being destroyed anyway.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Fix CI

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Restore clear_on_ready_callback on ~QOSEventHandlerBase

Needed since QOSEventHandlerBase does not own
the pub/sub listeners. So the QOSEventHandler
can be destroyed while the corresponding listeners
are still alive, so we need to clear these callbacks.

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Add coment on clearing callback for QoS event

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
2022-09-23 00:30:59 +01:00
Chen Lihui
6a8c61c026 use unique ptr and remove unuseful container (#2013)
Signed-off-by: Chen Lihui <lihui.chen@sony.com>

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2022-09-19 10:08:25 -07:00
Chen Lihui
978439191f fix mismatched issue if using zero_allocate (#1995)
* fix mismatched issue if uzing zero_allocated

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2022-09-15 08:17:28 -04:00
Chris Lalancette
7bc05da5d1 17.0.0 2022-09-13 20:29:01 +00:00
Chris Lalancette
4744fb6f50 Changelog.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-09-13 20:28:54 +00:00
Barry Xu
d0e1e837b5 Make ParameterService and Sync/AsyncParameterClient accept rclcpp::QoS (#1978)
* Make ParameterService and Sync/AsyncParameterClient accept rclcpp::QoS

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

* Avoid deprecated function using another deprecated function

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

Signed-off-by: Barry Xu <barry.xu@sony.com>
2022-09-09 15:06:33 -07:00
Chen Lihui
92d4f3e347 support regex match for parameter client (#1992)
* support regex match for parameter client

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

* address review that change behavior of a public method

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

* remove an unnecessary comment

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

* update gmock compilation setting

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

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2022-09-08 12:55:41 -07:00
Tyler Weaver
ea8daa3784 operator+= and operator-= for Duration (#1988)
* operator+= and operator-= for Duration

Signed-off-by: Tyler Weaver <maybe@tylerjw.dev>
2022-09-02 13:58:56 -04:00
Ivan Santiago Paunovic
df994e435d Revert "Revert "Add a create_timer method to Node and LifecycleNode classes (#1975)" (#2009) (#2010)
Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2022-08-31 10:21:13 -03:00
schrodinbug
93222cc2cd force compiler warning if callback handles not captured (#2000)
clarify documentation to show that not capturing returned handles
will result in callbacks immediately being unregistered

Signed-off-by: Jason Beach <jason.m.beach@gmail.com>
2022-08-31 08:40:11 -04:00
Shane Loretz
11b5f8db21 Revert "Add a create_timer method to Node and LifecycleNode classes (#1975)" (#2009)
This reverts commit 6167a575b3.

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

Signed-off-by: Shane Loretz <sloretz@openrobotics.org>
2022-08-30 13:42:14 -07:00
Andrew Symington
6167a575b3 Add a create_timer method to Node and LifecycleNode classes (#1975)
Signed-off-by: Andrew Symington <andrew.c.symington@gmail.com>
2022-08-30 10:10:11 -03:00
Brian
dab9f5e0a3 [docs] add note about callback lifetime for {on, post}_set_parameter_callback (#1981)
[docs] add note about {on, post, pre}_set_parameter_callback lifetime

Signed-off-by: Brian Chen <brian.chen@openrobotics.org>
2022-08-29 15:14:09 -04:00
Chen Lihui
3d69031d2a fix memory leak (#1994)
Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2022-08-17 21:30:51 -07:00
Deepanshu Bansal
b953bdddf8 Support pre-set and post-set parameter callbacks in addition to on-set-parameter-callback. (#1947)
* Support add_pre_set_parameter and add_post_set_parameter callbacks in addition to add_on_set_parameter_callback in Node API

Signed-off-by: deepanshu <deepanshubansal01@gmail.com>
2022-07-14 18:22:48 -04:00
Shane Loretz
64e4c72791 Make create_service accept rclcpp::QoS (#1969)
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2022-07-12 09:41:40 +09:00
Shane Loretz
f6056beaa0 Make create_client accept rclcpp::QoS (#1964)
* Make create_client accept rclcpp::QoS

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

* Deprecate passing rmw_qos_profile_t to create_client

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2022-07-11 10:09:44 -07:00
Chris Lalancette
37b589dc85 Fix the documentation for rclcpp::ok to be accurate. (#1965)
That is, it returns false if shutdown has been called, and
true in all other circumstances.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-07-07 09:07:12 -07:00
Chen Lihui
6dd3a0377b use regex for wildcard matching (#1839)
* use regex for wildcard matching

Co-authored-by: Aaron Lipinski <aaron.lipinski@roboticsplus.co.nz>
Signed-off-by: Chen Lihui <lihui.chen@sony.com>

* use map to process the content of parameter file by order

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

* add more test cases

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

* try to not decrease the performance and make the param win last

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

* update node name

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

* update document comment

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

* add more test for parameter_map_from

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

Co-authored-by: Aaron Lipinski <aaron.lipinski@roboticsplus.co.nz>
2022-07-06 09:41:16 -07:00
Audrow Nash
33dae5d679 Mirror rolling to master 2022-06-28 09:20:48 -05:00
William Woodall
f43a9198eb Revert "Introduce executors new spin_for method, replace spin_until_future_complete with spin_until_complete. (#1821) (#1874)" (#1956)
This reverts commit 3c8e89d17c.
2022-06-24 11:21:24 -07:00
Hubert Liberacki
3c8e89d17c Introduce executors new spin_for method, replace spin_until_future_complete with spin_until_complete. (#1821) (#1874) 2022-06-24 00:33:29 -07:00
Tomoya Fujita
546ddf87fe test adjustment for LoanedMessage. (#1951)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2022-06-22 15:01:43 -07:00
William Woodall
dbded5c0d6 fix virtual dispatch issues identified by clang-tidy (#1816)
* fix virtual dispatch issues identified by clang-tidy

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

* fix up to reduce code duplication

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

* uncrustify

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

* avoid returning temporaries

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

* uncrustify

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

* add docs about overriding methods used in constructors

Signed-off-by: William Woodall <william@osrfoundation.org>
2022-06-21 18:59:25 -07:00
Nikolai Morin
86a9d5882e Remove unused on_parameters_set_callback_ (#1945)
Signed-off-by: Nikolai Morin <nikolai.morin@apex.ai>
2022-06-14 13:22:53 -03:00
Ivan Santiago Paunovic
8e6a6fb32d Fix subscription.is_serialized() for callbacks with message info (#1950)
* Fix subscription.is_serialized() for callbacks with message info argument

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

* Add tests + please linters

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2022-06-10 17:18:25 -03:00
Tomoya Fujita
b3f57033a9 wait for subscriptions on another thread. (#1940)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2022-06-09 12:14:12 -07:00
Daniel Reuter
38581cc860 Fix documentation of RCLCPP_[INFO,WARN,...] (#1943)
In the pull request https://github.com/ros2/rclcpp/pull/1442 the ability
to use `std::string` as the first argument to the logging functions was
(rightfully) removed.

This commit just corrects the documentation of the macro, since it
confused me a bit ;)

Signed-off-by: Daniel Reuter <daniel.robin.reuter@googlemail.com>
2022-06-07 11:56:57 -04:00
Alberto Soragna
790e529ba3 Always trigger guard condition waitset (#1923)
* trigger guard condition waitset regardless of whether a trigger callback is present

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

* restore mutex in guard_condition.cpp

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

* remove whitespace

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

* add unit-test

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

* add documentation for trigger and set_on_trigger_callback

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2022-06-02 18:58:19 +01:00
Barry Xu
5c688303b3 Add statistics for handle_loaned_message (#1927)
* Add statistics for handle_loaned_message

Signed-off-by: Barry Xu <barry.xu@sony.com>
2022-05-19 08:10:17 -07:00
Jochen Sprickerhof
02802bcc38 Drop wrong template specialization (#1926)
This fails with g++ -std=gnu++20.

Signed-off-by: Jochen Sprickerhof <git@jochen.sprickerhof.de>
2022-05-12 19:08:34 -07:00
Jacob Perron
491475f232 16.2.0 2022-05-03 12:14:26 -07:00
Barry Xu
b8b64b4c08 Update get_parameter_from_event to follow the function description (#1922)
Signed-off-by: Barry Xu <barry.xu@sony.com>
2022-05-03 11:21:20 -07:00
Jacob Perron
ee67c211c5 Add 'best available' QoS enum values and methods (#1920)
If users set a policy as 'best available', then the middleware will pick a policy
that is most compatible with the current set of discovered endpoints while maintaining
the highest level of service possible.

For details about the expected behavior, see connected changes:

- https://github.com/ros2/rmw/pull/320
- https://github.com/ros2/rmw_dds_common/pull/60

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2022-05-03 10:35:10 -07:00
Audrow Nash
82ddd44140 16.1.0
Signed-off-by: Audrow Nash <audrow@hey.com>
2022-04-29 17:40:56 -07:00
Tomoya Fujita
a1980678ae use reinterpret_cast for function pointer conversion. (#1919)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2022-04-26 13:39:00 -07:00
Audrow Nash
c24e485084 16.0.1
Signed-off-by: Audrow Nash <audrow@hey.com>
2022-04-13 18:17:29 -07:00
Chen Lihui
d99157d731 remove DEFINE_CONTENT_FILTER cmake option (#1914)
* remove DEFINE_CONTENT_FILTER cmake option

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

* remove the macro CONTENT_FILTER_ENABLE as well

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2022-04-11 10:26:09 -03:00
William Woodall
248d911ea5 16.0.0 2022-04-08 16:27:59 -07:00
William Woodall
76aae4f799 changelogs
Signed-off-by: William Woodall <william@osrfoundation.org>
2022-04-08 16:25:08 -07:00
William Woodall
6815022909 remove things that were deprecated during galactic (#1913)
Signed-off-by: William Woodall <william@osrfoundation.org>
2022-04-08 16:23:43 -07:00
Audrow Nash
85a7046ac3 15.4.0
Signed-off-by: Audrow Nash <audrow@hey.com>
2022-04-05 14:17:00 -07:00
Alberto Soragna
6c06a29050 add take_data_by_entity_id API to waitable (#1892)
* add take_data_by_entity_id API to waitable

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

* use size_t for entity id

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

Co-authored-by: William Woodall <william@osrfoundation.org>
2022-04-04 22:25:30 -07:00
Chen Lihui
03fa731d23 add content-filtered-topic interfaces (#1561)
* to support a feature of content filtered topic

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

* update for checking memory allocated

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

* set expression parameter only if filter is valid

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

* add test

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

* use a default empty value for expresion parameters

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

* remove std_msgs dependency

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

* use new rcl interface

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

* remove unused include header files and fix unscrutify

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

* update comment

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

* update comment

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

* rename

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

* refactor test

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

* relate to `rcutils_string_array_t expression_parameters` changed in rmw

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

* remove the implementation temporary, add them with fallback in the feature

1. add DEFINE_CONTENT_FILTER with default(ON) to build content filter interfaces
2. user need a compile option(CONTENT_FILTER_ENABLE) to turn on the feature
to set the filter or call the interfaces

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

* update comments and check the option to build content filter test

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

* add DDS content filter implementation

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

* address review

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

* rcl api changed

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

* increase the filter propagation time

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

* add rclcpp::ContentFilterOptions and use it as the return type of get_content_filter

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

* increase the maximun time to wait message event and content filter propagation

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

* cft member should be defined in the structure.

to check the macro for interfaces is enough.

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

* set test timeout to 120

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

* update doc

Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2022-04-04 11:50:50 +08:00
Chris Lalancette
0699aeb851 15.3.0 2022-03-30 18:54:18 +00:00
Chris Lalancette
06e6da414a Changelog.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-03-30 18:54:09 +00:00
Abrar Rahman Protyasha
12de518956 [NodeParameters] Set name in param info pre-check (#1908)
* [NodeParameters] Set name if user didn't populate

If the user provided parameter descriptor already contains a name, then
we should not override said name, under the good faith assumption that
the user knows what they are doing.

Signed-off-by: Abrar Rahman Protyasha <aprotyas@u.rochester.edu>
2022-03-30 09:57:22 -07:00
Gaël Écorchard
eac0063176 Add test-dep ament_cmake_google_benchmark (#1904)
Signed-off-by: Gaël Écorchard <gael.ecorchard@cvut.cz>
2022-03-28 09:23:11 -04:00
Barry Xu
492770c12f Add publish by loaned message in GenericPublisher (#1856)
* Add publish by loaned message in GenericPublisher

Signed-off-by: Barry Xu <barry.xu@sony.com>
Co-authored-by: Alban Tamisier <alban.tamisier@apex.ai>
2022-03-27 22:06:17 -07:00
Jacob Perron
d302e3c628 15.2.0 2022-03-24 17:37:17 -07:00
Scott K Logan
011ea39e99 Add missing ament dependency on rcl_interfaces (#1903)
This package uses rcl_interfaces directly, and we've been getting away
with the missing dependency because `rcl` has the entirety of
`rcl_interfaces` as part of its link interface even tough it doesn't
need to.

If (when) `rcl` scopes its dependency to only the c generator and drops
the cpp generator from its link interface, this package will fail to
find the cpp symbols at link time. This change remedies that.

Signed-off-by: Scott K Logan <logans@cottsay.net>
2022-03-22 15:53:08 -07:00
Andrea Sorbini
49c2dd4813 Update data callback tests to account for all published samples. (#1900)
* Update data callback tests to account for all published samples.

Signed-off-by: Andrea Sorbini <asorbini@rti.com>
2022-03-21 13:50:04 -07:00
Andrea Sorbini
6afec4805c Increase timeout for acknowledgments to account for slower Connext settings. (#1901)
Signed-off-by: Andrea Sorbini <asorbini@rti.com>
2022-03-21 10:29:08 -04:00
Hirokazu Ishida
9ae35e347e Select executor in node registration (#1898)
* Select executor

Signed-off-by: Hirokazu Ishida <h-ishida@jsk.imi.i.u-tokyo.ac.jp>

* Fix indent

Signed-off-by: Hirokazu Ishida <h-ishida@jsk.imi.i.u-tokyo.ac.jp>
2022-03-18 20:42:06 +00:00
William Woodall
0f58bb8700 clang-tidy: explicit constructors (#1782)
* mark some single-argument constructors explicit that should have been

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

* suppress clang-tidy [google-explicit-constructor] in some cases where it is a false-positive

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

* Revert "suppress clang-tidy [google-explicit-constructor] in some cases where it is a false-positive"

This reverts commit eb6bf7e2df0dd27c945e97584ba9902ef9f61305.

Signed-off-by: William Woodall <william@osrfoundation.org>
2022-03-16 13:58:49 -07:00
mauropasse
8b9cabfb9d Add client/service QoS getters (#1784)
Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
2022-03-16 18:05:58 +00:00
Chris Lalancette
2e39e09803 Fix a bunch more rosdoc2 issues in rclcpp. (#1897)
* Fix a bunch more rosdoc2 issues in rclcpp.

There are a smattering of problems in here:

1.  We weren't properly using PREDEFINE for all of our macros.
2.  The Doxyfiles were referencing tag files that may not exist
(this will be handled by rosdoc2 automatically).
3.  The C++ parser in doxygen can't handle "friend classname",
but can handle "friend class classname"; it shouldn't matter
one way or the other to the compiler.
4.  There were a couple of parameters that were not documented.
5.  The doxygen C++ parser can't handle decltype in all situations.
6.  There was a structure using a C-style declaration.

This patch fixes all of the above issues.  We still aren't totally
clean on a rosdoc2 build, but we are a lot closer.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-03-11 14:46:07 -05:00
mauropasse
91f0b64493 time_until_trigger returns max time if timer is cancelled (#1893)
* time_until_trigger returns max time if timer is cancelled

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>
2022-03-10 20:36:16 -08:00
Chris Lalancette
3bd6900f98 Micro-optimizations in rclcpp (#1896)
* Use a single call to collect all CallbackGroup pointers.

Believe it or not, the taking and releasing of mutex_ within
the CallbackGroup class shows up in profiles.  However, when
collecting entities we really don't need to take it and drop
it between each of subscriptions, services, clients, timers,
and waitables.  Just take it once at the beginning, collect
everything, and then return, which removes a lot of unnecessary
work with that mutex.

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

* Pass shared_ptr as constref in the MemoryStrategy class.

That way, in the case that the callee doesn't need to take
a reference, we avoid creating and destroying a shared_ptr
class.  This reduces the overhead in using these functions,
and seems to work fine in the default case.  If a user wants
to subclass the MemoryStrategy class, then they can explicitly
create a shared_ptr copy by using the copy constructor.

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

* Use constref shared_ptr when iterating.

Believe it or not, the creation and destruction of the
shared_ptr class itself shows up in profiles in these loops.
Since we don't need to hold onto a reference while iterating
(the class is already doing that), just use constref wherever
we can which drastically reduces the overhead.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-03-09 11:13:36 -08:00
Alberto Soragna
f2f7ffdf53 fix bugprone-exception-escape in node_main.cpp.in (#1895)
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2022-03-08 19:55:06 +00:00
Chris Lalancette
16914e31a1 15.1.0 2022-03-01 19:33:31 +00:00
Chris Lalancette
4e3c6be76a Changelog.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-03-01 19:33:22 +00:00
Tomoya Fujita
79241a3cdc spin_all with a zero timeout. (#1878)
* spin_all with a zero timeout.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2022-02-25 09:20:00 -08:00
Alberto Soragna
9c5ad79b63 Merge pull request #1889 from ros2/asoragna/improve-components-main
small improvements to node_main.cpp.in
2022-02-24 17:30:40 +00:00
iRobot ROS
d8e1aed520 Add RMW listener APIs (#1579)
* Add RMW listener APIs

Signed-off-by: Alberto Soragna <asoragna@irobot.com>

* split long log into two lines

Signed-off-by: Alberto Soragna <asoragna@irobot.com>

* Remove use_previous_event

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Do not set gc listener callback

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* significant refactor to make callbacks type safe

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

* Add on_ready callback to waitables, add clear_* functions, add unit-tests

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

* add mutex to set and clear listener APIs

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

* allow to set ipc sub callback from regular subscription

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

* fix minor failures

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

* fix typos and errors in comments

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

* fix comments

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

* expose qos listener APIs from pub and sub; add unit-tests

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

* add qos event unit-test for invalid callbacks

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

* Use QoS depth to limit callbacks count

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* fix ipc-subscription

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

* Rename CallbackMessageT -> ROSMessageType

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Set callbacks to Actions

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* changes from upstream

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

* Unset callback on entities destruction

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* fix rebase errors

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

* fix unit-tests

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

* Add GuardCondition on trigger callback

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Add tests for new GuardCondition APIs

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Fix windows CI

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* Action unset callbacks only if were set

Signed-off-by: Mauro Passerino <mpasserino@irobot.com>

* add missing include rcl event callback include directive

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

* typos

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

* remove listener reference

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

* remove references to listener and move a method closer to impl

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

* cpplint

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

* Fix qos history check in subscription_intra_process.hpp

Co-authored-by: Mauro Passerino <mpasserino@irobot.com>
Co-authored-by: William Woodall <william@osrfoundation.org>
Co-authored-by: Alberto Soragna <alberto.soragna@gmail.com>
2022-02-24 00:32:17 -08:00
Miguel Company
c5a16dce11 Remove fastrtps customization on tests (#1887)
Signed-off-by: Miguel Company <MiguelCompany@eprosima.com>
2022-02-22 08:49:37 -05:00
Alberto Soragna
32c03dde2d small improvements to node_main.cpp.in
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2022-02-19 11:57:10 +00:00
Shane Loretz
4f778199b4 Install headers to include/${PROJECT_NAME} (#1888)
* initial attempt, problems with ament_gen_version_h

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

* remove blank line

Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2022-02-16 09:41:21 -08:00
Shane Loretz
025cd5ccc8 Use ament_generate_version_header (#1886)
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2022-02-15 13:02:23 -08:00
Tomoya Fujita
4c8cfa39f8 use universal reference to support rvalue. (#1883)
* use universal reference to support rvalue.

Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2022-02-10 09:58:36 -08:00
Chen Lihui
891fff0153 fix one subscription can wait_for_message twice (#1870)
Signed-off-by: Chen Lihui <lihui.chen@sony.com>
2022-02-10 08:44:14 -05:00
Alberto Soragna
43db06dad7 Use spin() in component_manager_isolated.hpp (#1881)
* replace spin_until_future_complete with spin in component_manager_isolated.hpp

spin_until_future_complete() is more inefficient as it needs to check the state of the future and the timeout after every work iteration

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

* fix uncrustify error

Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2022-02-09 11:45:39 -03:00
Tomoya Fujita
0d1747e066 LifecycleNode::on_deactivate deactivate all managed entities. (#1885)
Signed-off-by: Tomoya Fujita <Tomoya.Fujita@sony.com>
2022-02-07 09:32:44 -08:00
Kenji Miyake
2520d398c7 Add return value version of get_parameter_or (#1813)
* Add return value version of get_parameter_or

* Add test case

Signed-off-by: Kenji Miyake <kenji.miyake@tier4.jp>
Co-authored-by: Abrar Rahman Protyasha <aprotyas@u.rochester.edu>
2022-02-04 09:23:17 -05:00
Alberto Soragna
d3c0049b24 Merge pull request #1871 from ros2/asoragna/executor-is-spinning
add is_spinning() method to executor base class
2022-01-21 08:43:04 +00:00
Chris Lalancette
c54a6f1cd2 Cleanup time source object lifetimes (#1867)
* Small cleanups in TimeSource.

Simplify some code, and also make sure to throw an exception
when use_sim_time is not of type bool.  Also add a test for
the latter.

* Remove the sim_time_cb_handler.

It was originally used to make sure that someone didn't change
the 'use_sim_time' type from boolean to something else.  But
since the parameters interface now does that automatically for
us, we don't need the explicit check here.

I can think of one situation that this allows that wasn't allowed
before.  If the user defined 'use_sim_time' as a parameter override
when constructing a node, and the type is bool, and they also
defined the parameters as 'dynamic_typing', then this callback
will still have effect.  But presumably if the user went out of
their way to change the parameter to dynamic_typing, they are
trying to do something esoteric and so we should just let them.

* ClocksState does not need to be a pointer.

Instead, make it a regular member variable.  That lets us get
rid of all of the special handling for when it is a weak_ptr
or not.  It's lifetime is now just that of NodeState.

* Stop using NodeState as a weak_ptr in the captures.

This ended up causing the lifetime of the object to be weird.
Instead, just capture 'this' which is sufficient.

* Make sure destroy_clock_sub is first.

* Switch to using just a regular object.

Windows objects to trying to do "make_shared" in the RCLCPP
macro, so just switch to a normal object here.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-01-20 16:30:55 -05:00
Alberto Soragna
9ec0393e63 add is_spinning() method to executor base class
Signed-off-by: Alberto Soragna <alberto.soragna@gmail.com>
2022-01-20 12:55:07 +00:00
gezp
bca3fd7da1 add use_global_arguments for node options of component nodes (#1776)
* add use_global_arguments for node options of component nodes

Signed-off-by: zhenpeng ge <zhenpeng.ge@qq.com>

keep use_global_arguments false default

Signed-off-by: zhenpeng ge <zhenpeng.ge@qq.com>

update warning message

Signed-off-by: zhenpeng ge <zhenpeng.ge@qq.com>

update warnning message

Signed-off-by: zhenpeng ge <zhenpeng.ge@qq.com>

add test

Signed-off-by: zhenpeng ge <zhenpeng.ge@qq.com>

* use forward_global_arguments instead of use_global_arguments

Signed-off-by: zhenpeng ge <zhenpeng.ge@qq.com>
2022-01-19 10:29:33 -03:00
Jacob Perron
8afef51cfd 15.0.0 2022-01-14 17:49:37 -08:00
Ivan Santiago Paunovic
3123f5a643 Automatically transition lifecycle entities when node transitions (#1863)
* Automatically transition LifecyclePublisher(s) between activated and inactive

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

* Fix: Add created publishers to the managed entities vector

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

* enabled_ -> activated_

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

* Continue setting should_log_ as before

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

* Fix visibility attributes so it works on Windows

Signed-off-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2022-01-14 18:00:06 -03:00
Chris Lalancette
aa18ef51cc Cleanup the TypeAdapt tests (#1858)
* Cleanup test_publisher_with_type_adapter.

It had a lot of infrastructure that it didn't need, so remove
most of that.  Also move the creation of the node to open-coded,
since we weren't really saving anything.  Finally make sure
to reset the node pointers as appropriate, which cleans up
errant error messages.

* Cleanup test_subscription_with_type_adapter.

It had a lot of infrastructure that it didn't need, so remove
most of that.  Also move the creation of the node to open-coded,
since we weren't really saving anything.  Finally make sure
to reset the node pointers as appropriate, which cleans up
errant error messages.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-01-14 14:38:13 -05:00
Chris Lalancette
1688f05aea Cleanup includes. (#1857)
Remove ones that aren't needed, and add in ones that are
actually needed in the respective files.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-01-14 08:23:16 -05:00
Jacob Perron
80f93d1dbb Fix include order and relative paths for cpplint (#1859)
* Fix include order and relative paths for cpplint

Relates to https://github.com/ament/ament_lint/pull/324

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

* Use double-quotes for other includes

This is backwards compatible with older versions of cpplint.

Signed-off-by: Jacob Perron <jacob@openrobotics.org>
2022-01-12 14:24:49 -08:00
Doug Smith
5e314c253e Rename stringstream in macros to a more unique name (#1862)
* Rename stringstream in macros to a more unique name

Signed-off-by: Doug Smith <douglas.smith@swri.org>
2022-01-11 17:52:52 -05:00
Gonzo
6b321edb4f Add non transform capabilities for intra-process (#1849)
That is, make it so that if both a publisher and a subscriber using intra-process
and using TypeAdaptation are the same type, we don't need to convert to
a ROS Message type on the publisher and from a ROS Message type before
delivering to the subscriber.  Instead, we store the original message type
in the subscription buffer, and deliver it straight through to the subscriber.

This has two main benefits:
1. It is better for performance; we are eliding unnecessary work.
2. We can use TypeAdaptation to pass custom handles (like hardware accelerator
    handles) between a publisher and subscriber.  That means we can avoid
    unnecessary overhead when using a hardware accelerator, like synchronizing
    between the hardware accelerator and the main CPU.

The main way this is accomplished is by causing the SubscriptionIntraProcessBuffer
to store messages of the Subscribed Type (which in the case of a ROS message type
subscriber is a ROS message type, but in the case of a TypeAdapter type is the
custom type).  When a publish with a Type Adapted type happens, it delays conversion
of the message, and passes the message type from the user down into the IntraProcessManager.

The IntraProcessManager checks to see if it can cast the SubscriptionBuffer
to the custom type being published first.  If it can, then it knows it can deliver the message directly
into the SubscriptionBuffer with no conversion necessary.  If it can't, it then sees if
the SubscriptionBuffer is of a ROS message type.  If it is, then it performs the conversion
as necessary, and then stores it.  In all cases, the Subscription is then triggered underneath
so that the message will eventually be delivered by an executor.

We also add tests to ensure that in the case where the publisher and subscriber
and using the same type adapter, no conversion happens when delivering the
message.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
Signed-off-by: Gonzalo de Pedro <gonzalo@depedro.com.ar>
2022-01-07 14:45:06 -05:00
Michel Hidalgo
8093648ee3 Fix rclcpp documentation build (#1779)
* Update Doxyfile
* Update docblocks with warnings
* Use leading * instead of trailing [] for pointer types
* Help Doxygen parse std::enable_if<> condition
* Add documentation-only SFINAE functions' declarations
* Avoid function pointer type syntax in function arguments.
* Add documentation-only SFINAE function traits.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
Signed-off-by: Michel Hidalgo <michel@ekumenlabs.com>
2022-01-07 09:36:11 -03:00
Shane Loretz
9583ec7855 Add rclcpp_components::component (#1855)
Signed-off-by: Shane Loretz <sloretz@osrfoundation.org>
2022-01-06 14:31:17 -08:00
Chris Lalancette
2d6e6364cd 14.1.0 2022-01-05 10:40:02 -05:00
Chris Lalancette
5613d613cf Changelog.
Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2022-01-05 10:39:54 -05:00
M. Mostafa Farzan
7a2ee23281 Use UninitializedStaticallyTypedParameterException (#1689)
* Use UninitializedStaticallyTypedParameterException

Signed-off-by: Mohammad Farzan <m2_farzan@yahoo.com>
Co-authored-by: Ivan Santiago Paunovic <ivanpauno@ekumenlabs.com>
2021-12-23 22:28:46 -08:00
Barry Xu
802bfc2c74 Add wait_for_all_acked support (#1662)
* Add wait_for_all_acked support

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

* Update the description of wait_for_all_acked

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

* Use rcpputils::convert_to_nanoseconds() for time conversion

Signed-off-by: Barry Xu <barry.xu@sony.com>
2021-12-20 20:36:01 -08:00
Bi0T1N
152dbc6e38 Add tests for function templates of declare_parameter (#1747)
* Add function template tests for all defined types of declare_parameter

Signed-off-by: Nico Neumann <nico.neumann@iosb.fraunhofer.de>
2021-12-20 11:56:46 -08:00
278 changed files with 21193 additions and 4898 deletions

View File

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

2
CODEOWNERS Normal file
View File

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

View File

@@ -8,7 +8,8 @@ rclcpp provides the standard C++ API for interacting with ROS 2.
`#include "rclcpp/rclcpp.hpp"` allows use of the most common elements of the ROS 2 system.
Visit the [rclcpp API documentation](http://docs.ros2.org/latest/api/rclcpp/) for a complete list of its main components.
The link to the latest API documentation can be found on the rclcpp package info page, at the [ROS Index](https://index.ros.org/p/rclcpp/).
### Examples

View File

@@ -2,6 +2,193 @@
Changelog for package rclcpp
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
20.0.0 (2023-04-13)
-------------------
* applied tracepoints for ring_buffer (`#2091 <https://github.com/ros2/rclcpp/issues/2091>`_)
* Dynamic Subscription (REP-2011 Subset): Stubs for rclcpp (`#2165 <https://github.com/ros2/rclcpp/issues/2165>`_)
* Add type_hash to cpp TopicEndpointInfo (`#2137 <https://github.com/ros2/rclcpp/issues/2137>`_)
* Trigger the intraprocess guard condition with data (`#2164 <https://github.com/ros2/rclcpp/issues/2164>`_)
* Minor grammar fix (`#2149 <https://github.com/ros2/rclcpp/issues/2149>`_)
* Fix unnecessary allocations in executor.cpp (`#2135 <https://github.com/ros2/rclcpp/issues/2135>`_)
* add Logger::get_effective_level(). (`#2141 <https://github.com/ros2/rclcpp/issues/2141>`_)
* Remove deprecated header (`#2139 <https://github.com/ros2/rclcpp/issues/2139>`_)
* Implement matched event (`#2105 <https://github.com/ros2/rclcpp/issues/2105>`_)
* use allocator via init_options argument. (`#2129 <https://github.com/ros2/rclcpp/issues/2129>`_)
* Fixes to silence some clang warnings. (`#2127 <https://github.com/ros2/rclcpp/issues/2127>`_)
* Documentation improvements on the executor (`#2125 <https://github.com/ros2/rclcpp/issues/2125>`_)
* Avoid losing waitable handles while using MultiThreadedExecutor (`#2109 <https://github.com/ros2/rclcpp/issues/2109>`_)
* Hook up the incompatible type event inside of rclcpp (`#2069 <https://github.com/ros2/rclcpp/issues/2069>`_)
* Update all rclcpp packages to C++17. (`#2121 <https://github.com/ros2/rclcpp/issues/2121>`_)
* Fix clang warning: bugprone-use-after-move (`#2116 <https://github.com/ros2/rclcpp/issues/2116>`_)
* Contributors: Barry Xu, Chris Lalancette, Christopher Wecht, Emerson Knapp, Michael Carroll, Tomoya Fujita, Yadu, mauropasse, methylDragon, ymski
19.3.0 (2023-03-01)
-------------------
* Fix memory leak in tracetools::get_symbol() (`#2104 <https://github.com/ros2/rclcpp/issues/2104>`_)
* Service introspection (`#1985 <https://github.com/ros2/rclcpp/issues/1985>`_)
* Allow publishing borrowed messages with intra-process enabled (`#2108 <https://github.com/ros2/rclcpp/issues/2108>`_)
* to fix flaky test about TestTimeSource.callbacks (`#2111 <https://github.com/ros2/rclcpp/issues/2111>`_)
* Contributors: Brian, Chen Lihui, Christophe Bedard, Miguel Company
19.2.0 (2023-02-24)
-------------------
* to create a sublogger while getting child of Logger (`#1717 <https://github.com/ros2/rclcpp/issues/1717>`_)
* Fix documentation of Context class (`#2107 <https://github.com/ros2/rclcpp/issues/2107>`_)
* fixes for rmw callbacks in qos_event class (`#2102 <https://github.com/ros2/rclcpp/issues/2102>`_)
* Contributors: Alberto Soragna, Chen Lihui, Silvio Traversaro
19.1.0 (2023-02-14)
-------------------
* Add support for timers on reset callback (`#1979 <https://github.com/ros2/rclcpp/issues/1979>`_)
* Topic node guard condition in executor (`#2074 <https://github.com/ros2/rclcpp/issues/2074>`_)
* Fix bug on the disorder of calling shutdown callback (`#2097 <https://github.com/ros2/rclcpp/issues/2097>`_)
* Contributors: Barry Xu, Chen Lihui, mauropasse
19.0.0 (2023-01-30)
-------------------
* Add default constructor to NodeInterfaces (`#2094 <https://github.com/ros2/rclcpp/issues/2094>`_)
* Fix clock state cached time to be a copy, not a reference. (`#2092 <https://github.com/ros2/rclcpp/issues/2092>`_)
* Fix -Wmaybe-uninitialized warning (`#2081 <https://github.com/ros2/rclcpp/issues/2081>`_)
* Fix the keep_last warning when using system defaults. (`#2082 <https://github.com/ros2/rclcpp/issues/2082>`_)
* Add in a fix for older compilers. (`#2075 <https://github.com/ros2/rclcpp/issues/2075>`_)
* Contributors: Alexander Hans, Chris Lalancette, Shane Loretz
18.0.0 (2022-12-29)
-------------------
* Implement Unified Node Interface (NodeInterfaces class) (`#2041 <https://github.com/ros2/rclcpp/issues/2041>`_)
* Do not throw exception if trying to dequeue an empty intra-process buffer (`#2061 <https://github.com/ros2/rclcpp/issues/2061>`_)
* Move event callback binding to PublisherBase and SubscriptionBase (`#2066 <https://github.com/ros2/rclcpp/issues/2066>`_)
* Implement validity checks for rclcpp::Clock (`#2040 <https://github.com/ros2/rclcpp/issues/2040>`_)
* Explicitly set callback type (`#2059 <https://github.com/ros2/rclcpp/issues/2059>`_)
* Fix logging macros to build with msvc and cpp20 (`#2063 <https://github.com/ros2/rclcpp/issues/2063>`_)
* Add clock type to node_options (`#1982 <https://github.com/ros2/rclcpp/issues/1982>`_)
* Fix nullptr dereference in prune_requests_older_than (`#2008 <https://github.com/ros2/rclcpp/issues/2008>`_)
* Remove templating on to_rcl_subscription_options (`#2056 <https://github.com/ros2/rclcpp/issues/2056>`_)
* Fix SharedFuture from async_send_request never becoming valid (`#2044 <https://github.com/ros2/rclcpp/issues/2044>`_)
* Add in a warning for a KeepLast depth of 0. (`#2048 <https://github.com/ros2/rclcpp/issues/2048>`_)
* Mark rclcpp::Clock::now() as const (`#2050 <https://github.com/ros2/rclcpp/issues/2050>`_)
* Fix a case that did not throw ParameterUninitializedException (`#2036 <https://github.com/ros2/rclcpp/issues/2036>`_)
* Update maintainers (`#2043 <https://github.com/ros2/rclcpp/issues/2043>`_)
* Contributors: Alberto Soragna, Audrow Nash, Chen Lihui, Chris Lalancette, Jeffery Hsu, Lei Liu, Mateusz Szczygielski, Shane Loretz, andrei, mauropasse, methylDragon
17.1.0 (2022-11-02)
-------------------
* MultiThreadExecutor number of threads is at least 2+ in default. (`#2032 <https://github.com/ros2/rclcpp/issues/2032>`_)
* Fix bug that a callback not reached (`#1640 <https://github.com/ros2/rclcpp/issues/1640>`_)
* Set the minimum number of threads of the Multithreaded executor to 2 (`#2030 <https://github.com/ros2/rclcpp/issues/2030>`_)
* check thread whether joinable before join (`#2019 <https://github.com/ros2/rclcpp/issues/2019>`_)
* Set cpplint test timeout to 3 minutes (`#2022 <https://github.com/ros2/rclcpp/issues/2022>`_)
* Make sure to include-what-you-use in the node_interfaces. (`#2018 <https://github.com/ros2/rclcpp/issues/2018>`_)
* Do not clear entities callbacks on destruction (`#2002 <https://github.com/ros2/rclcpp/issues/2002>`_)
* fix mismatched issue if using zero_allocate (`#1995 <https://github.com/ros2/rclcpp/issues/1995>`_)
* Contributors: Alexis Paques, Chen Lihui, Chris Lalancette, Cristóbal Arroyo, Tomoya Fujita, mauropasse, uupks
17.0.0 (2022-09-13)
-------------------
* Make ParameterService and Sync/AsyncParameterClient accept rclcpp::QoS (`#1978 <https://github.com/ros2/rclcpp/issues/1978>`_)
* support regex match for parameter client (`#1992 <https://github.com/ros2/rclcpp/issues/1992>`_)
* operator+= and operator-= for Duration (`#1988 <https://github.com/ros2/rclcpp/issues/1988>`_)
* Revert "Revert "Add a create_timer method to Node and `LifecycleNode` classes (`#1975 <https://github.com/ros2/rclcpp/issues/1975>`_)" (`#2009 <https://github.com/ros2/rclcpp/issues/2009>`_) (`#2010 <https://github.com/ros2/rclcpp/issues/2010>`_)
* force compiler warning if callback handles not captured (`#2000 <https://github.com/ros2/rclcpp/issues/2000>`_)
* Revert "Add a `create_timer` method to `Node` and `LifecycleNode` classes (`#1975 <https://github.com/ros2/rclcpp/issues/1975>`_)" (`#2009 <https://github.com/ros2/rclcpp/issues/2009>`_)
* Add a `create_timer` method to `Node` and `LifecycleNode` classes (`#1975 <https://github.com/ros2/rclcpp/issues/1975>`_)
* [docs] add note about callback lifetime for {on, post}_set_parameter_callback (`#1981 <https://github.com/ros2/rclcpp/issues/1981>`_)
* fix memory leak (`#1994 <https://github.com/ros2/rclcpp/issues/1994>`_)
* Support pre-set and post-set parameter callbacks in addition to on-set-parameter-callback. (`#1947 <https://github.com/ros2/rclcpp/issues/1947>`_)
* Make create_service accept rclcpp::QoS (`#1969 <https://github.com/ros2/rclcpp/issues/1969>`_)
* Make create_client accept rclcpp::QoS (`#1964 <https://github.com/ros2/rclcpp/issues/1964>`_)
* Fix the documentation for rclcpp::ok to be accurate. (`#1965 <https://github.com/ros2/rclcpp/issues/1965>`_)
* use regex for wildcard matching (`#1839 <https://github.com/ros2/rclcpp/issues/1839>`_)
* Revert "Introduce executors new spin_for method, replace spin_until_future_complete with spin_until_complete. (`#1821 <https://github.com/ros2/rclcpp/issues/1821>`_) (`#1874 <https://github.com/ros2/rclcpp/issues/1874>`_)" (`#1956 <https://github.com/ros2/rclcpp/issues/1956>`_)
* Introduce executors new spin_for method, replace spin_until_future_complete with spin_until_complete. (`#1821 <https://github.com/ros2/rclcpp/issues/1821>`_) (`#1874 <https://github.com/ros2/rclcpp/issues/1874>`_)
* test adjustment for LoanedMessage. (`#1951 <https://github.com/ros2/rclcpp/issues/1951>`_)
* fix virtual dispatch issues identified by clang-tidy (`#1816 <https://github.com/ros2/rclcpp/issues/1816>`_)
* Remove unused on_parameters_set_callback\_ (`#1945 <https://github.com/ros2/rclcpp/issues/1945>`_)
* Fix subscription.is_serialized() for callbacks with message info (`#1950 <https://github.com/ros2/rclcpp/issues/1950>`_)
* wait for subscriptions on another thread. (`#1940 <https://github.com/ros2/rclcpp/issues/1940>`_)
* Fix documentation of `RCLCPP\_[INFO,WARN,...]` (`#1943 <https://github.com/ros2/rclcpp/issues/1943>`_)
* Always trigger guard condition waitset (`#1923 <https://github.com/ros2/rclcpp/issues/1923>`_)
* Add statistics for handle_loaned_message (`#1927 <https://github.com/ros2/rclcpp/issues/1927>`_)
* Drop wrong template specialization (`#1926 <https://github.com/ros2/rclcpp/issues/1926>`_)
* Contributors: Alberto Soragna, Andrew Symington, Barry Xu, Brian, Chen Lihui, Chris Lalancette, Daniel Reuter, Deepanshu Bansal, Hubert Liberacki, Ivan Santiago Paunovic, Jochen Sprickerhof, Nikolai Morin, Shane Loretz, Tomoya Fujita, Tyler Weaver, William Woodall, schrodinbug
16.2.0 (2022-05-03)
-------------------
* Update get_parameter_from_event to follow the function description (`#1922 <https://github.com/ros2/rclcpp/issues/1922>`_)
* Add 'best available' QoS enum values and methods (`#1920 <https://github.com/ros2/rclcpp/issues/1920>`_)
* Contributors: Barry Xu, Jacob Perron
16.1.0 (2022-04-29)
-------------------
* use reinterpret_cast for function pointer conversion. (`#1919 <https://github.com/ros2/rclcpp/issues/1919>`_)
* Contributors: Tomoya Fujita
16.0.1 (2022-04-13)
-------------------
* remove DEFINE_CONTENT_FILTER cmake option (`#1914 <https://github.com/ros2/rclcpp/issues/1914>`_)
* Contributors: Chen Lihui
16.0.0 (2022-04-08)
-------------------
* remove things that were deprecated during galactic (`#1913 <https://github.com/ros2/rclcpp/issues/1913>`_)
* Contributors: William Woodall
15.4.0 (2022-04-05)
-------------------
* add take_data_by_entity_id API to waitable (`#1892 <https://github.com/ros2/rclcpp/issues/1892>`_)
* add content-filtered-topic interfaces (`#1561 <https://github.com/ros2/rclcpp/issues/1561>`_)
* Contributors: Alberto Soragna, Chen Lihui
15.3.0 (2022-03-30)
-------------------
* [NodeParameters] Set name in param info pre-check (`#1908 <https://github.com/ros2/rclcpp/issues/1908>`_)
* Add test-dep ament_cmake_google_benchmark (`#1904 <https://github.com/ros2/rclcpp/issues/1904>`_)
* Add publish by loaned message in GenericPublisher (`#1856 <https://github.com/ros2/rclcpp/issues/1856>`_)
* Contributors: Abrar Rahman Protyasha, Barry Xu, Gaël Écorchard
15.2.0 (2022-03-24)
-------------------
* Add missing ament dependency on rcl_interfaces (`#1903 <https://github.com/ros2/rclcpp/issues/1903>`_)
* Update data callback tests to account for all published samples (`#1900 <https://github.com/ros2/rclcpp/issues/1900>`_)
* Increase timeout for acknowledgments to account for slower Connext settings (`#1901 <https://github.com/ros2/rclcpp/issues/1901>`_)
* clang-tidy: explicit constructors (`#1782 <https://github.com/ros2/rclcpp/issues/1782>`_)
* Add client/service QoS getters (`#1784 <https://github.com/ros2/rclcpp/issues/1784>`_)
* Fix a bunch more rosdoc2 issues in rclcpp. (`#1897 <https://github.com/ros2/rclcpp/issues/1897>`_)
* time_until_trigger returns max time if timer is cancelled (`#1893 <https://github.com/ros2/rclcpp/issues/1893>`_)
* Micro-optimizations in rclcpp (`#1896 <https://github.com/ros2/rclcpp/issues/1896>`_)
* Contributors: Andrea Sorbini, Chris Lalancette, Mauro Passerino, Scott K Logan, William Woodall
15.1.0 (2022-03-01)
-------------------
* spin_all with a zero timeout. (`#1878 <https://github.com/ros2/rclcpp/issues/1878>`_)
* Add RMW listener APIs (`#1579 <https://github.com/ros2/rclcpp/issues/1579>`_)
* Remove fastrtps customization on tests (`#1887 <https://github.com/ros2/rclcpp/issues/1887>`_)
* Install headers to include/${PROJECT_NAME} (`#1888 <https://github.com/ros2/rclcpp/issues/1888>`_)
* Use ament_generate_version_header (`#1886 <https://github.com/ros2/rclcpp/issues/1886>`_)
* use universal reference to support rvalue. (`#1883 <https://github.com/ros2/rclcpp/issues/1883>`_)
* fix one subscription can wait_for_message twice (`#1870 <https://github.com/ros2/rclcpp/issues/1870>`_)
* Add return value version of get_parameter_or (`#1813 <https://github.com/ros2/rclcpp/issues/1813>`_)
* Cleanup time source object lifetimes (`#1867 <https://github.com/ros2/rclcpp/issues/1867>`_)
* add is_spinning() method to executor base class
* Contributors: Alberto Soragna, Chen Lihui, Chris Lalancette, Kenji Miyake, Miguel Company, Shane Loretz, Tomoya Fujita, iRobot ROS
15.0.0 (2022-01-14)
-------------------
* Cleanup the TypeAdapt tests (`#1858 <https://github.com/ros2/rclcpp/issues/1858>`_)
* Cleanup includes (`#1857 <https://github.com/ros2/rclcpp/issues/1857>`_)
* Fix include order and relative paths for cpplint (`#1859 <https://github.com/ros2/rclcpp/issues/1859>`_)
* Rename stringstream in macros to a more unique name (`#1862 <https://github.com/ros2/rclcpp/issues/1862>`_)
* Add non transform capabilities for intra-process (`#1849 <https://github.com/ros2/rclcpp/issues/1849>`_)
* Fix rclcpp documentation build (`#1779 <https://github.com/ros2/rclcpp/issues/1779>`_)
* Contributors: Chris Lalancette, Doug Smith, Gonzo, Jacob Perron, Michel Hidalgo
14.1.0 (2022-01-05)
-------------------
* Use UninitializedStaticallyTypedParameterException (`#1689 <https://github.com/ros2/rclcpp/issues/1689>`_)
* Add wait_for_all_acked support (`#1662 <https://github.com/ros2/rclcpp/issues/1662>`_)
* Add tests for function templates of declare_parameter (`#1747 <https://github.com/ros2/rclcpp/issues/1747>`_)
* Contributors: Barry Xu, Bi0T1N, M. Mostafa Farzan
14.0.0 (2021-12-17)
-------------------
* Fixes for uncrustify 0.72 (`#1844 <https://github.com/ros2/rclcpp/issues/1844>`_)

View File

@@ -25,6 +25,7 @@ find_package(tracetools REQUIRED)
# Default to C++17
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# About -Wno-sign-conversion: With Clang, -Wconversion implies -Wsign-conversion. There are a number of
@@ -48,16 +49,25 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/detail/rmw_implementation_specific_subscription_payload.cpp
src/rclcpp/detail/utilities.cpp
src/rclcpp/duration.cpp
src/rclcpp/dynamic_typesupport/dynamic_message.cpp
src/rclcpp/dynamic_typesupport/dynamic_message_type.cpp
src/rclcpp/dynamic_typesupport/dynamic_message_type_builder.cpp
src/rclcpp/dynamic_typesupport/dynamic_message_type_support.cpp
src/rclcpp/dynamic_typesupport/dynamic_serialization_support.cpp
src/rclcpp/event.cpp
src/rclcpp/exceptions/exceptions.cpp
src/rclcpp/executable_list.cpp
src/rclcpp/executor.cpp
src/rclcpp/executors.cpp
src/rclcpp/executors/executor_entities_collection.cpp
src/rclcpp/executors/executor_entities_collector.cpp
src/rclcpp/executors/executor_notify_waitable.cpp
src/rclcpp/executors/multi_threaded_executor.cpp
src/rclcpp/executors/single_threaded_executor.cpp
src/rclcpp/executors/static_executor_entities_collector.cpp
src/rclcpp/executors/static_single_threaded_executor.cpp
src/rclcpp/expand_topic_or_service_name.cpp
src/rclcpp/experimental/executors/events_executor/events_executor.cpp
src/rclcpp/experimental/timers_manager.cpp
src/rclcpp/future_return_code.cpp
src/rclcpp/generic_publisher.cpp
src/rclcpp/generic_subscription.cpp
@@ -92,7 +102,7 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/parameter_value.cpp
src/rclcpp/publisher_base.cpp
src/rclcpp/qos.cpp
src/rclcpp/qos_event.cpp
src/rclcpp/event_handler.cpp
src/rclcpp/qos_overriding_options.cpp
src/rclcpp/serialization.cpp
src/rclcpp/serialized_message.cpp
@@ -187,13 +197,14 @@ endif()
target_include_directories(${PROJECT_NAME} PUBLIC
"$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>"
"$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}/include>"
"$<INSTALL_INTERFACE:include>")
"$<INSTALL_INTERFACE:include/${PROJECT_NAME}>")
target_link_libraries(${PROJECT_NAME} ${CMAKE_THREAD_LIBS_INIT})
# specific order: dependents before dependencies
ament_target_dependencies(${PROJECT_NAME}
"ament_index_cpp"
"libstatistics_collector"
"rcl"
"rcl_interfaces"
"rcl_yaml_param_parser"
"rcpputils"
"rcutils"
@@ -217,11 +228,14 @@ install(
RUNTIME DESTINATION bin
)
# specific order: dependents before dependencies
ament_export_include_directories(include)
# Export old-style CMake variables
ament_export_include_directories("include/${PROJECT_NAME}")
ament_export_libraries(${PROJECT_NAME})
# Export modern CMake targets
ament_export_targets(${PROJECT_NAME})
# specific order: dependents before dependencies
ament_export_dependencies(ament_index_cpp)
ament_export_dependencies(libstatistics_collector)
ament_export_dependencies(rcl)
@@ -247,7 +261,7 @@ ament_package()
install(
DIRECTORY include/ ${CMAKE_CURRENT_BINARY_DIR}/include/
DESTINATION include
DESTINATION include/${PROJECT_NAME}
)
if(TEST cppcheck)
@@ -255,4 +269,8 @@ if(TEST cppcheck)
set_tests_properties(cppcheck PROPERTIES TIMEOUT 500)
endif()
ament_cmake_gen_version_h()
if(TEST cpplint)
set_tests_properties(cpplint PROPERTIES TIMEOUT 180)
endif()
ament_generate_version_header(${PROJECT_NAME})

View File

@@ -21,13 +21,22 @@ GENERATE_LATEX = NO
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = RCLCPP_PUBLIC=
PREDEFINED += DOXYGEN_ONLY
PREDEFINED += RCLCPP_LOCAL=
PREDEFINED += RCLCPP_PUBLIC=
PREDEFINED += RCLCPP_PUBLIC_TYPE=
PREDEFINED += RCUTILS_WARN_UNUSED=
PREDEFINED += RCPPUTILS_TSA_GUARDED_BY(x)=
PREDEFINED += RCPPUTILS_TSA_PT_GUARDED_BY(x)=
PREDEFINED += RCPPUTILS_TSA_REQUIRES(x)=
DOT_GRAPH_MAX_NODES = 101
# Tag files that do not exist will produce a warning and cross-project linking will not work.
TAGFILES += "../../../../doxygen_tag_files/cppreference-doxygen-web.tag.xml=http://en.cppreference.com/w/"
#TAGFILES += "../../../../doxygen_tag_files/cppreference-doxygen-web.tag.xml=http://en.cppreference.com/w/"
# Consider changing "latest" to the version you want to reference (e.g. beta1 or 1.0.0)
TAGFILES += "../../../../doxygen_tag_files/rcl.tag=http://docs.ros2.org/latest/api/rcl/"
TAGFILES += "../../../../doxygen_tag_files/rmw.tag=http://docs.ros2.org/latest/api/rmw/"
TAGFILES += "../../../../doxygen_tag_files/rcutils.tag=http://docs.ros2.org/latest/api/rcutils/"
#TAGFILES += "../../../../doxygen_tag_files/rcl.tag=http://docs.ros2.org/latest/api/rcl/"
#TAGFILES += "../../../../doxygen_tag_files/rmw.tag=http://docs.ros2.org/latest/api/rmw/"
#TAGFILES += "../../../../doxygen_tag_files/rcutils.tag=http://docs.ros2.org/latest/api/rcutils/"
# Uncomment to generate tag files for cross-project linking.
#GENERATE_TAGFILE = "../../../../doxygen_tag_files/rclcpp.tag"

Binary file not shown.

After

Width:  |  Height:  |  Size: 164 KiB

View File

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

View File

@@ -15,6 +15,7 @@
#ifndef RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_
#define RCLCPP__ALLOCATOR__ALLOCATOR_COMMON_HPP_
#include <cstring>
#include <memory>
#include "rcl/allocator.h"
@@ -39,6 +40,22 @@ void * retyped_allocate(size_t size, void * untyped_allocator)
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
}
template<typename Alloc>
void * retyped_zero_allocate(size_t number_of_elem, size_t size_of_elem, void * untyped_allocator)
{
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
if (!typed_allocator) {
throw std::runtime_error("Received incorrect allocator type");
}
size_t size = number_of_elem * size_of_elem;
void * allocated_memory =
std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
if (allocated_memory) {
std::memset(allocated_memory, 0, size);
}
return allocated_memory;
}
template<typename T, typename Alloc>
void retyped_deallocate(void * untyped_pointer, void * untyped_allocator)
{
@@ -73,6 +90,7 @@ rcl_allocator_t get_rcl_allocator(Alloc & allocator)
rcl_allocator_t rcl_allocator = rcl_get_default_allocator();
#ifndef _WIN32
rcl_allocator.allocate = &retyped_allocate<Alloc>;
rcl_allocator.zero_allocate = &retyped_zero_allocate<Alloc>;
rcl_allocator.deallocate = &retyped_deallocate<T, Alloc>;
rcl_allocator.reallocate = &retyped_reallocate<T, Alloc>;
rcl_allocator.state = &allocator;

View File

@@ -41,7 +41,7 @@ public:
}
template<typename T>
AllocatorDeleter(const AllocatorDeleter<T> & a)
explicit AllocatorDeleter(const AllocatorDeleter<T> & a)
{
allocator_ = a.get_allocator();
}

View File

@@ -191,10 +191,14 @@ public:
#ifndef TRACETOOLS_DISABLED
std::visit(
[this](auto && arg) {
TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
tracetools::get_symbol(arg));
if (TRACEPOINT_ENABLED(rclcpp_callback_register)) {
char * symbol = tracetools::get_symbol(arg);
DO_TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
symbol);
std::free(symbol);
}
}, callback_);
#endif // TRACETOOLS_DISABLED
}

View File

@@ -354,12 +354,6 @@ public:
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
}
[[deprecated("use AnySubscriptionCallback(const AllocatorT & allocator) instead")]]
explicit
AnySubscriptionCallback(std::shared_ptr<AllocatorT> allocator) // NOLINT[runtime/explicit]
: AnySubscriptionCallback(*NotNull<AllocatorT>(allocator.get(), "invalid allocator").pointer)
{}
AnySubscriptionCallback(const AnySubscriptionCallback &) = default;
/// Generic function for setting the callback.
@@ -476,6 +470,22 @@ public:
}
}
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
convert_custom_type_to_ros_message_unique_ptr(const SubscribedType & msg)
{
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
auto ptr = ROSMessageTypeAllocatorTraits::allocate(ros_message_type_allocator_, 1);
ROSMessageTypeAllocatorTraits::construct(ros_message_type_allocator_, ptr);
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(msg, *ptr);
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
} else {
static_assert(
!sizeof(MessageT *),
"convert_custom_type_to_ros_message_unique_ptr() "
"unexpectedly called without specialized TypeAdapter");
}
}
// Dispatch when input is a ros message and the output could be anything.
void
dispatch(
@@ -658,7 +668,7 @@ public:
void
dispatch_intra_process(
std::shared_ptr<const ROSMessageType> message,
std::shared_ptr<const SubscribedType> message,
const rclcpp::MessageInfo & message_info)
{
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
@@ -677,65 +687,89 @@ public:
// conditions for custom type
if constexpr (is_ta && std::is_same_v<T, ConstRefCallback>) {
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
callback(*local_message);
callback(*message);
} else if constexpr (is_ta && std::is_same_v<T, ConstRefWithInfoCallback>) { // NOLINT
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
callback(*local_message, message_info);
callback(*message, message_info);
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, UniquePtrCallback>||
std::is_same_v<T, SharedPtrCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
callback(create_custom_unique_ptr_from_custom_shared_ptr_message(message));
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, UniquePtrWithInfoCallback>||
std::is_same_v<T, SharedPtrWithInfoCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
callback(create_custom_unique_ptr_from_custom_shared_ptr_message(message), message_info);
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, SharedConstPtrCallback>||
std::is_same_v<T, ConstRefSharedConstPtrCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
callback(message);
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
callback(message, message_info);
}
// conditions for ros message type
else if constexpr (std::is_same_v<T, ConstRefROSMessageCallback>) { // NOLINT
callback(*message);
} else if constexpr (std::is_same_v<T, ConstRefWithInfoROSMessageCallback>) {
callback(*message, message_info);
else if constexpr (std::is_same_v<T, ConstRefROSMessageCallback>) { // NOLINT[readability/braces]
if constexpr (is_ta) {
auto local = convert_custom_type_to_ros_message_unique_ptr(*message);
callback(*local);
} else {
callback(*message);
}
} else if constexpr (std::is_same_v<T, ConstRefWithInfoROSMessageCallback>) { // NOLINT[readability/braces]
if constexpr (is_ta) {
auto local = convert_custom_type_to_ros_message_unique_ptr(*message);
callback(*local, message_info);
} else {
callback(*message, message_info);
}
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, UniquePtrROSMessageCallback>||
std::is_same_v<T, SharedPtrROSMessageCallback>)
{
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message));
if constexpr (is_ta) {
callback(convert_custom_type_to_ros_message_unique_ptr(*message));
} else {
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message));
}
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, UniquePtrWithInfoROSMessageCallback>||
std::is_same_v<T, SharedPtrWithInfoROSMessageCallback>)
{
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message), message_info);
if constexpr (is_ta) {
callback(convert_custom_type_to_ros_message_unique_ptr(*message), message_info);
} else {
callback(create_ros_unique_ptr_from_ros_shared_ptr_message(message), message_info);
}
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrROSMessageCallback>)
{
callback(message);
if constexpr (is_ta) {
callback(convert_custom_type_to_ros_message_unique_ptr(*message));
} else {
callback(message);
}
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrWithInfoROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoROSMessageCallback>)
{
callback(message, message_info);
if constexpr (is_ta) {
callback(convert_custom_type_to_ros_message_unique_ptr(*message), message_info);
} else {
callback(message, message_info);
}
}
// condition to catch SerializedMessage types
else if constexpr ( // NOLINT[readability/braces]
@@ -764,7 +798,7 @@ public:
void
dispatch_intra_process(
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> message,
std::unique_ptr<SubscribedType, SubscribedTypeDeleter> message,
const rclcpp::MessageInfo & message_info)
{
TRACEPOINT(callback_start, static_cast<const void *>(this), true);
@@ -778,70 +812,98 @@ public:
// Dispatch.
std::visit(
[&message, &message_info, this](auto && callback) {
// clang complains that 'this' lambda capture is unused, which is true
// in *some* specializations of this template, but not others. Just
// quiet it down.
(void)this;
using T = std::decay_t<decltype(callback)>;
static constexpr bool is_ta = rclcpp::TypeAdapter<MessageT>::is_specialized::value;
// conditions for custom type
if constexpr (is_ta && std::is_same_v<T, ConstRefCallback>) {
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
callback(*local_message);
callback(*message);
} else if constexpr (is_ta && std::is_same_v<T, ConstRefWithInfoCallback>) { // NOLINT
auto local_message = convert_ros_message_to_custom_type_unique_ptr(*message);
callback(*local_message, message_info);
callback(*message, message_info);
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, UniquePtrCallback>||
std::is_same_v<T, SharedPtrCallback>
))
std::is_same_v<T, SharedPtrCallback>))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
callback(std::move(message));
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, UniquePtrWithInfoCallback>||
std::is_same_v<T, SharedPtrWithInfoCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
callback(std::move(message), message_info);
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, SharedConstPtrCallback>||
std::is_same_v<T, ConstRefSharedConstPtrCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message));
callback(std::move(message));
} else if constexpr ( // NOLINT[readability/braces]
is_ta && (
std::is_same_v<T, SharedConstPtrWithInfoCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoCallback>
))
{
callback(convert_ros_message_to_custom_type_unique_ptr(*message), message_info);
callback(std::move(message), message_info);
}
// conditions for ros message type
else if constexpr (std::is_same_v<T, ConstRefROSMessageCallback>) { // NOLINT
callback(*message);
} else if constexpr (std::is_same_v<T, ConstRefWithInfoROSMessageCallback>) {
callback(*message, message_info);
else if constexpr (std::is_same_v<T, ConstRefROSMessageCallback>) { // NOLINT[readability/braces]
if constexpr (is_ta) {
auto local = convert_custom_type_to_ros_message_unique_ptr(*message);
callback(*local);
} else {
callback(*message);
}
} else if constexpr (std::is_same_v<T, ConstRefWithInfoROSMessageCallback>) { // NOLINT[readability/braces]
if constexpr (is_ta) {
auto local = convert_custom_type_to_ros_message_unique_ptr(*message);
callback(*local, message_info);
} else {
callback(*message, message_info);
}
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, UniquePtrROSMessageCallback>||
std::is_same_v<T, SharedPtrROSMessageCallback>)
{
callback(std::move(message));
if constexpr (is_ta) {
callback(convert_custom_type_to_ros_message_unique_ptr(*message));
} else {
callback(std::move(message));
}
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, UniquePtrWithInfoROSMessageCallback>||
std::is_same_v<T, SharedPtrWithInfoROSMessageCallback>)
{
callback(std::move(message), message_info);
if constexpr (is_ta) {
callback(convert_custom_type_to_ros_message_unique_ptr(*message), message_info);
} else {
callback(std::move(message), message_info);
}
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrROSMessageCallback>)
{
callback(std::move(message));
if constexpr (is_ta) {
callback(convert_custom_type_to_ros_message_unique_ptr(*message));
} else {
callback(std::move(message));
}
} else if constexpr ( // NOLINT[readability/braces]
std::is_same_v<T, SharedConstPtrWithInfoROSMessageCallback>||
std::is_same_v<T, ConstRefSharedConstPtrWithInfoROSMessageCallback>)
{
callback(std::move(message), message_info);
if constexpr (is_ta) {
callback(convert_custom_type_to_ros_message_unique_ptr(*message), message_info);
} else {
callback(std::move(message), message_info);
}
}
// condition to catch SerializedMessage types
else if constexpr ( // NOLINT[readability/braces]
@@ -888,7 +950,13 @@ public:
std::holds_alternative<UniquePtrSerializedMessageCallback>(callback_variant_) ||
std::holds_alternative<SharedConstPtrSerializedMessageCallback>(callback_variant_) ||
std::holds_alternative<ConstRefSharedConstPtrSerializedMessageCallback>(callback_variant_) ||
std::holds_alternative<SharedPtrSerializedMessageCallback>(callback_variant_);
std::holds_alternative<SharedPtrSerializedMessageCallback>(callback_variant_) ||
std::holds_alternative<ConstRefSerializedMessageWithInfoCallback>(callback_variant_) ||
std::holds_alternative<UniquePtrSerializedMessageWithInfoCallback>(callback_variant_) ||
std::holds_alternative<SharedConstPtrSerializedMessageWithInfoCallback>(callback_variant_) ||
std::holds_alternative<ConstRefSharedConstPtrSerializedMessageWithInfoCallback>(
callback_variant_) ||
std::holds_alternative<SharedPtrSerializedMessageWithInfoCallback>(callback_variant_);
}
void
@@ -897,10 +965,14 @@ public:
#ifndef TRACETOOLS_DISABLED
std::visit(
[this](auto && callback) {
TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
tracetools::get_symbol(callback));
if (TRACEPOINT_ENABLED(rclcpp_callback_register)) {
char * symbol = tracetools::get_symbol(callback);
DO_TRACEPOINT(
rclcpp_callback_register,
static_cast<const void *>(this),
symbol);
std::free(symbol);
}
}, callback_variant_);
#endif // TRACETOOLS_DISABLED
}

View File

@@ -16,11 +16,14 @@
#define RCLCPP__CALLBACK_GROUP_HPP_
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "rclcpp/client.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription_base.hpp"
@@ -90,11 +93,58 @@ public:
* determines whether a callback group is automatically added to an executor
* with the node with which it is associated.
*/
[[deprecated("Use CallbackGroup constructor with context function argument")]]
RCLCPP_PUBLIC
explicit CallbackGroup(
CallbackGroupType group_type,
bool automatically_add_to_executor_with_node = true);
/// Constructor for CallbackGroup.
/**
* Callback Groups have a type, either 'Mutually Exclusive' or 'Reentrant'
* and when creating one the type must be specified.
*
* Callbacks in Reentrant Callback Groups must be able to:
* - run at the same time as themselves (reentrant)
* - run at the same time as other callbacks in their group
* - run at the same time as other callbacks in other groups
*
* Callbacks in Mutually Exclusive Callback Groups:
* - will not be run multiple times simultaneously (non-reentrant)
* - will not be run at the same time as other callbacks in their group
* - but must run at the same time as callbacks in other groups
*
* Additionally, callback groups have a property which determines whether or
* not they are added to an executor with their associated node automatically.
* When creating a callback group the automatically_add_to_executor_with_node
* argument determines this behavior, and if true it will cause the newly
* created callback group to be added to an executor with the node when the
* Executor::add_node method is used.
* If false, this callback group will not be added automatically and would
* have to be added to an executor manually using the
* Executor::add_callback_group method.
*
* Whether the node was added to the executor before creating the callback
* group, or after, is irrelevant; the callback group will be automatically
* added to the executor in either case.
*
* \param[in] group_type The type of the callback group.
* \param[in] get_node_context Lambda to retrieve the node context when
* checking that the creating node is valid and using the guard condition.
* \param[in] automatically_add_to_executor_with_node A boolean that
* determines whether a callback group is automatically added to an executor
* with the node with which it is associated.
*/
RCLCPP_PUBLIC
explicit CallbackGroup(
CallbackGroupType group_type,
std::function<rclcpp::Context::SharedPtr(void)> get_node_context,
bool automatically_add_to_executor_with_node = true);
/// Default destructor.
RCLCPP_PUBLIC
~CallbackGroup();
template<typename Function>
rclcpp::SubscriptionBase::SharedPtr
find_subscription_ptrs_if(Function func) const
@@ -130,14 +180,50 @@ public:
return _find_ptrs_if_impl<rclcpp::Waitable, Function>(func, waitable_ptrs_);
}
/// Get the total number of entities in this callback group.
/**
* \return the number of entities in the callback group.
*/
RCLCPP_PUBLIC
size_t size() const;
/// Return a reference to the 'can be taken' atomic boolean.
/**
* The resulting bool will be true in the case that no executor is currently
* using an executable entity from this group.
* The resulting bool will be false in the case that an executor is currently
* using an executable entity from this group, and the group policy doesn't
* allow a second take (eg mutual exclusion)
* \return a reference to the flag
*/
RCLCPP_PUBLIC
std::atomic_bool &
can_be_taken_from();
/// Get the group type.
/**
* \return the group type
*/
RCLCPP_PUBLIC
const CallbackGroupType &
type() const;
/// Collect all of the entity pointers contained in this callback group.
/**
* \param[in] sub_func Function to execute for each subscription
* \param[in] service_func Function to execute for each service
* \param[in] client_func Function to execute for each client
* \param[in] timer_func Function to execute for each timer
* \param[in] waitable_fuinc Function to execute for each waitable
*/
RCLCPP_PUBLIC
void collect_all_ptrs(
std::function<void(const rclcpp::SubscriptionBase::SharedPtr &)> sub_func,
std::function<void(const rclcpp::ServiceBase::SharedPtr &)> service_func,
std::function<void(const rclcpp::ClientBase::SharedPtr &)> client_func,
std::function<void(const rclcpp::TimerBase::SharedPtr &)> timer_func,
std::function<void(const rclcpp::Waitable::SharedPtr &)> waitable_func) const;
/// Return a reference to the 'associated with executor' atomic boolean.
/**
* When a callback group is added to an executor this boolean is checked
@@ -163,6 +249,29 @@ public:
bool
automatically_add_to_executor_with_node() const;
/// Retrieve the guard condition used to signal changes to this callback group.
/**
* \param[in] context_ptr context to use when creating the guard condition
* \return guard condition if it is valid, otherwise nullptr.
*/
[[deprecated("Use get_notify_guard_condition() without arguments")]]
RCLCPP_PUBLIC
rclcpp::GuardCondition::SharedPtr
get_notify_guard_condition(const rclcpp::Context::SharedPtr context_ptr);
/// Retrieve the guard condition used to signal changes to this callback group.
/**
* \return guard condition if it is valid, otherwise nullptr.
*/
RCLCPP_PUBLIC
rclcpp::GuardCondition::SharedPtr
get_notify_guard_condition();
/// Trigger the notify guard condition.
RCLCPP_PUBLIC
void
trigger_notify_guard_condition();
protected:
RCLCPP_DISABLE_COPY(CallbackGroup)
@@ -205,6 +314,11 @@ protected:
std::vector<rclcpp::Waitable::WeakPtr> waitable_ptrs_;
std::atomic_bool can_be_taken_from_;
const bool automatically_add_to_executor_with_node_;
// defer the creation of the guard condition
std::shared_ptr<rclcpp::GuardCondition> notify_guard_condition_ = nullptr;
std::recursive_mutex notify_guard_condition_mutex_;
std::function<rclcpp::Context::SharedPtr(void)> get_context_;
private:
template<typename TypeT, typename Function>

View File

@@ -16,33 +16,40 @@
#define RCLCPP__CLIENT_HPP_
#include <atomic>
#include <functional>
#include <future>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <optional> // NOLINT, cpplint doesn't think this is a cpp std header
#include <sstream>
#include <string>
#include <tuple>
#include <unordered_map>
#include <utility>
#include <variant> // NOLINT
#include <vector>
#include "rcl/client.h"
#include "rcl/error_handling.h"
#include "rcl/event_callback.h"
#include "rcl/service_introspection.h"
#include "rcl/wait.h"
#include "rclcpp/clock.hpp"
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcutils/logging_macros.h"
#include "rmw/error_handling.h"
#include "rmw/impl/cpp/demangle.hpp"
#include "rmw/rmw.h"
namespace rclcpp
@@ -126,7 +133,7 @@ public:
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph);
RCLCPP_PUBLIC
virtual ~ClientBase();
virtual ~ClientBase() = default;
/// Take the next response for this client as a type erased pointer.
/**
@@ -215,6 +222,123 @@ public:
bool
exchange_in_use_by_wait_set_state(bool in_use_state);
/// Get the actual request publsher 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 client, 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 request publsher qos settings.
* \throws std::runtime_error if failed to get qos settings
*/
RCLCPP_PUBLIC
rclcpp::QoS
get_request_publisher_actual_qos() const;
/// Get the actual response subscription 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 client, 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 response subscription qos settings.
* \throws std::runtime_error if failed to get qos settings
*/
RCLCPP_PUBLIC
rclcpp::QoS
get_response_subscription_actual_qos() const;
/// Set a callback to be called when each new response is received.
/**
* The callback receives a size_t which is the number of responses received
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if responses were received before any
* callback was set.
*
* Since this callback is called from the middleware, you should aim to make
* it fast and not blocking.
* If you need to do a lot of work or wait for some other event, you should
* spin it off to another thread, otherwise you risk blocking the middleware.
*
* Calling it again will clear any previously set callback.
*
* An exception will be thrown if the callback is not callable.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the client
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rmw_client_set_on_new_response_callback
* \sa rcl_client_set_on_new_response_callback
*
* \param[in] callback functor to be called when a new response is received
*/
void
set_on_new_response_callback(std::function<void(size_t)> callback)
{
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_new_response_callback "
"is not callable.");
}
auto new_callback =
[callback, this](size_t number_of_responses) {
try {
callback(number_of_responses);
} catch (const std::exception & exception) {
RCLCPP_ERROR_STREAM(
node_logger_,
"rclcpp::ClientBase@" << this <<
" caught " << rmw::impl::cpp::demangle(exception) <<
" exception in user-provided callback for the 'on new response' callback: " <<
exception.what());
} catch (...) {
RCLCPP_ERROR_STREAM(
node_logger_,
"rclcpp::ClientBase@" << this <<
" caught unhandled exception in user-provided callback " <<
"for the 'on new response' callback");
}
};
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
// been replaced but the middleware hasn't been told about the new one yet.
set_on_new_response_callback(
rclcpp::detail::cpp_callback_trampoline<decltype(new_callback), const void *, size_t>,
static_cast<const void *>(&new_callback));
// Store the std::function to keep it in scope, also overwrites the existing one.
on_new_response_callback_ = new_callback;
// Set it again, now using the permanent storage.
set_on_new_response_callback(
rclcpp::detail::cpp_callback_trampoline<
decltype(on_new_response_callback_), const void *, size_t>,
static_cast<const void *>(&on_new_response_callback_));
}
/// Unset the callback registered for new responses, if any.
void
clear_on_new_response_callback()
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
if (on_new_response_callback_) {
set_on_new_response_callback(nullptr, nullptr);
on_new_response_callback_ = nullptr;
}
}
protected:
RCLCPP_DISABLE_COPY(ClientBase)
@@ -230,13 +354,21 @@ protected:
const rcl_node_t *
get_rcl_node_handle() const;
RCLCPP_PUBLIC
void
set_on_new_response_callback(rcl_event_callback_t callback, const void * user_data);
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
std::shared_ptr<rcl_node_t> node_handle_;
std::shared_ptr<rclcpp::Context> context_;
rclcpp::Logger node_logger_;
std::shared_ptr<rcl_client_t> client_handle_;
std::atomic<bool> in_use_by_wait_set_{false};
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_response_callback_{nullptr};
};
template<typename ServiceT>
@@ -338,15 +470,13 @@ public:
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name,
rcl_client_options_t & client_options)
: ClientBase(node_base, node_graph)
: ClientBase(node_base, node_graph),
srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
{
using rosidl_typesupport_cpp::get_service_type_support_handle;
auto service_type_support_handle =
get_service_type_support_handle<ServiceT>();
rcl_ret_t ret = rcl_client_init(
this->get_client_handle().get(),
this->get_rcl_node_handle(),
service_type_support_handle,
srv_type_support_handle_,
service_name.c_str(),
&client_options);
if (ret != RCL_RET_OK) {
@@ -641,7 +771,9 @@ public:
auto old_size = pending_requests_.size();
for (auto it = pending_requests_.begin(), last = pending_requests_.end(); it != last; ) {
if (it->second.first < time_point) {
pruned_requests->push_back(it->first);
if (pruned_requests) {
pruned_requests->push_back(it->first);
}
it = pending_requests_.erase(it);
} else {
++it;
@@ -650,6 +782,33 @@ public:
return old_size - pending_requests_.size();
}
/// Configure client introspection.
/**
* \param[in] clock clock to use to generate introspection timestamps
* \param[in] qos_service_event_pub QoS settings to use when creating the introspection publisher
* \param[in] introspection_state the state to set introspection to
*/
void
configure_introspection(
Clock::SharedPtr clock, const QoS & qos_service_event_pub,
rcl_service_introspection_state_t introspection_state)
{
rcl_publisher_options_t pub_opts = rcl_publisher_get_default_options();
pub_opts.qos = qos_service_event_pub.get_rmw_qos_profile();
rcl_ret_t ret = rcl_client_configure_service_introspection(
client_handle_.get(),
node_handle_.get(),
clock->get_clock_handle(),
srv_type_support_handle_,
pub_opts,
introspection_state);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure client introspection");
}
}
protected:
using CallbackTypeValueVariant = std::tuple<CallbackType, SharedFuture, Promise>;
using CallbackWithRequestTypeValueVariant = std::tuple<
@@ -664,16 +823,14 @@ protected:
async_send_request_impl(const Request & request, CallbackInfoVariant value)
{
int64_t sequence_number;
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
rcl_ret_t ret = rcl_send_request(get_client_handle().get(), &request, &sequence_number);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
}
{
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
pending_requests_.try_emplace(
sequence_number,
std::make_pair(std::chrono::system_clock::now(), std::move(value)));
}
pending_requests_.try_emplace(
sequence_number,
std::make_pair(std::chrono::system_clock::now(), std::move(value)));
return sequence_number;
}
@@ -702,6 +859,9 @@ protected:
CallbackInfoVariant>>
pending_requests_;
std::mutex pending_requests_mutex_;
private:
const rosidl_service_type_support_t * srv_type_support_handle_;
};
} // namespace rclcpp

View File

@@ -77,7 +77,7 @@ public:
*/
RCLCPP_PUBLIC
Time
now();
now() const;
/**
* Sleep until a specified Time, according to clock type.
@@ -137,6 +137,51 @@ public:
Duration rel_time,
Context::SharedPtr context = contexts::get_global_default_context());
/**
* Check if the clock is started.
*
* A started clock is a clock that reflects non-zero time.
* Typically a clock will be unstarted if it is using RCL_ROS_TIME with ROS time and
* nothing has been published on the clock topic yet.
*
* \return true if clock is started
* \throws std::runtime_error if the clock is not rcl_clock_valid
*/
RCLCPP_PUBLIC
bool
started();
/**
* Wait until clock to start.
*
* \rclcpp::Clock::started
* \param context the context to wait in
* \return true if clock was already started or became started
* \throws std::runtime_error if the context is invalid or clock is not rcl_clock_valid
*/
RCLCPP_PUBLIC
bool
wait_until_started(Context::SharedPtr context = contexts::get_global_default_context());
/**
* Wait for clock to start, with timeout.
*
* The timeout is waited in steady time.
*
* \rclcpp::Clock::started
* \param timeout the maximum time to wait for.
* \param context the context to wait in.
* \param wait_tick_ns the time to wait between each iteration of the wait loop (in nanoseconds).
* \return true if clock was or became valid
* \throws std::runtime_error if the context is invalid or clock is not rcl_clock_valid
*/
RCLCPP_PUBLIC
bool
wait_until_started(
const rclcpp::Duration & timeout,
Context::SharedPtr context = contexts::get_global_default_context(),
const rclcpp::Duration & wait_tick_ns = rclcpp::Duration(0, static_cast<uint32_t>(1e7)));
/**
* Returns the clock of the type `RCL_ROS_TIME` is active.
*

View File

@@ -65,8 +65,11 @@ using PreShutdownCallbackHandle = ShutdownCallbackHandle;
/// Context which encapsulates shared state between nodes and other similar entities.
/**
* A context also represents the lifecycle between init and shutdown of rclcpp.
* It is often used in conjunction with rclcpp::init, or rclcpp::init_local,
* and rclcpp::shutdown.
* Nodes may be attached to a particular context by passing to the rclcpp::Node
* constructor a rclcpp::NodeOptions instance in which the Context is set via
* rclcpp::NodeOptions::context.
* Nodes will be automatically removed from the context when destructed.
* Contexts may be shutdown by calling rclcpp::shutdown.
*/
class Context : public std::enable_shared_from_this<Context>
{
@@ -127,7 +130,7 @@ public:
void
init(
int argc,
char const * const argv[],
char const * const * argv,
const rclcpp::InitOptions & init_options = rclcpp::InitOptions());
/// Return true if the context is valid, otherwise false.
@@ -183,6 +186,11 @@ public:
*
* This function is thread-safe.
*
* Note that if you override this method, but leave shutdown to be called in
* the destruction of this base class, it will not call the overridden
* version from your base class.
* So you need to ensure you call your class's shutdown() in its destructor.
*
* \param[in] reason the description of why shutdown happened
* \return true if shutdown was successful, false if context was already shutdown
* \throw various exceptions derived from rclcpp::exceptions::RCLError, if rcl_shutdown fails
@@ -318,7 +326,6 @@ public:
/// Interrupt any blocking sleep_for calls, causing them to return immediately and return true.
RCLCPP_PUBLIC
virtual
void
interrupt_all_sleep_for();
@@ -351,7 +358,6 @@ protected:
// Called by constructor and destructor to clean up by finalizing the
// shutdown rcl context and preparing for a new init cycle.
RCLCPP_PUBLIC
virtual
void
clean_up();
@@ -373,10 +379,10 @@ private:
// attempt to acquire another sub context.
std::recursive_mutex sub_contexts_mutex_;
std::unordered_set<std::shared_ptr<OnShutdownCallback>> on_shutdown_callbacks_;
std::vector<std::shared_ptr<OnShutdownCallback>> on_shutdown_callbacks_;
mutable std::mutex on_shutdown_callbacks_mutex_;
std::unordered_set<std::shared_ptr<PreShutdownCallback>> pre_shutdown_callbacks_;
std::vector<std::shared_ptr<PreShutdownCallback>> pre_shutdown_callbacks_;
mutable std::mutex pre_shutdown_callbacks_mutex_;
/// Condition variable for timed sleep (see sleep_for).
@@ -395,20 +401,22 @@ private:
using ShutdownCallback = ShutdownCallbackHandle::ShutdownCallbackType;
template<ShutdownType shutdown_type>
RCLCPP_LOCAL
ShutdownCallbackHandle
add_shutdown_callback(
ShutdownType shutdown_type,
ShutdownCallback callback);
template<ShutdownType shutdown_type>
RCLCPP_LOCAL
bool
remove_shutdown_callback(
ShutdownType shutdown_type,
const ShutdownCallbackHandle & callback_handle);
template<ShutdownType shutdown_type>
RCLCPP_LOCAL
std::vector<rclcpp::Context::ShutdownCallback>
get_shutdown_callback(ShutdownType shutdown_type) const;
get_shutdown_callback() const;
};
/// Return a copy of the list of context shared pointers.

View File

@@ -20,10 +20,40 @@
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/qos.hpp"
#include "rmw/rmw.h"
namespace rclcpp
{
/// Create a service client with a given type.
/**
* \param[in] node_base NodeBaseInterface implementation of the node on which
* to create the client.
* \param[in] node_graph NodeGraphInterface implementation of the node on which
* to create the client.
* \param[in] node_services NodeServicesInterface implementation of the node on
* which to create the client.
* \param[in] service_name The name on which the service is accessible.
* \param[in] qos Quality of service profile for client.
* \param[in] group Callback group to handle the reply to service calls.
* \return Shared pointer to the created client.
*/
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 rclcpp::QoS & qos = rclcpp::ServicesQoS(),
rclcpp::CallbackGroup::SharedPtr group = nullptr)
{
return create_client<ServiceT>(
node_base, node_graph, node_services,
service_name,
qos.get_rmw_qos_profile(),
group);
}
/// Create a service client with a given type.
/// \internal

View File

@@ -92,7 +92,7 @@ template<
typename NodeT>
std::shared_ptr<PublisherT>
create_publisher(
NodeT & node,
NodeT && node,
const std::string & topic_name,
const rclcpp::QoS & qos,
const rclcpp::PublisherOptionsWithAllocator<AllocatorT> & options = (

View File

@@ -26,6 +26,32 @@
namespace rclcpp
{
/// Create a service with a given type.
/**
* \param[in] node_base NodeBaseInterface implementation of the node on which
* to create the service.
* \param[in] node_services NodeServicesInterface implementation of the node on
* which to create the service.
* \param[in] service_name The name on which the service is accessible.
* \param[in] callback The callback to call when the service gets a request.
* \param[in] qos Quality of service profile for the service.
* \param[in] group Callback group to handle the reply to service calls.
* \return Shared pointer to the created service.
*/
template<typename ServiceT, typename CallbackT>
typename rclcpp::Service<ServiceT>::SharedPtr
create_service(
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
const std::string & service_name,
CallbackT && callback,
const rclcpp::QoS & qos,
rclcpp::CallbackGroup::SharedPtr group)
{
return create_service<ServiceT, CallbackT>(
node_base, node_services, service_name,
std::forward<CallbackT>(callback), qos.get_rmw_qos_profile(), group);
}
/// Create a service with a given type.
/// \internal

View File

@@ -23,85 +23,29 @@
#include "rclcpp/duration.hpp"
#include "rclcpp/node_interfaces/get_node_base_interface.hpp"
#include "rclcpp/node_interfaces/get_node_clock_interface.hpp"
#include "rclcpp/node_interfaces/get_node_timers_interface.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_clock_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(
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
std::shared_ptr<node_interfaces::NodeTimersInterface> node_timers,
rclcpp::Clock::SharedPtr clock,
rclcpp::Duration period,
CallbackT && callback,
rclcpp::CallbackGroup::SharedPtr group = nullptr)
namespace detail
{
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::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);
}
/// Convenience method to create a timer with node resources.
/// Perform a safe cast to a timer period in nanoseconds
/**
*
* \tparam DurationRepT
* \tparam DurationT
* \tparam CallbackT
* \param period period to execute callback. This duration must be 0 <= period < nanoseconds::max()
* \param callback callback to execute via the timer period
* \param group
* \param node_base
* \param node_timers
* \return
* \throws std::invalid argument if either node_base or node_timers
* are null, or period is negative or too large
* \return period, expressed as chrono::duration::nanoseconds
* \throws std::invalid_argument if period is negative or too large
*/
template<typename DurationRepT, typename DurationT, typename CallbackT>
typename rclcpp::WallTimer<CallbackT>::SharedPtr
create_wall_timer(
std::chrono::duration<DurationRepT, DurationT> period,
CallbackT callback,
rclcpp::CallbackGroup::SharedPtr group,
node_interfaces::NodeBaseInterface * node_base,
node_interfaces::NodeTimersInterface * node_timers)
template<typename DurationRepT, typename DurationT>
std::chrono::nanoseconds
safe_cast_to_period_in_ns(std::chrono::duration<DurationRepT, DurationT> period)
{
if (node_base == nullptr) {
throw std::invalid_argument{"input node_base cannot be null"};
}
if (node_timers == nullptr) {
throw std::invalid_argument{"input node_timers cannot be null"};
}
if (period < std::chrono::duration<DurationRepT, DurationT>::zero()) {
throw std::invalid_argument{"timer period cannot be negative"};
}
@@ -132,12 +76,135 @@ create_wall_timer(
"Casting timer period to nanoseconds resulted in integer overflow."};
}
return period_ns;
}
} // namespace detail
/// Create a timer with a given clock
/// \internal
template<typename CallbackT>
typename rclcpp::TimerBase::SharedPtr
create_timer(
std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
std::shared_ptr<node_interfaces::NodeTimersInterface> node_timers,
rclcpp::Clock::SharedPtr clock,
rclcpp::Duration period,
CallbackT && callback,
rclcpp::CallbackGroup::SharedPtr group = nullptr)
{
return create_timer(
clock,
period.to_chrono<std::chrono::nanoseconds>(),
std::forward<CallbackT>(callback),
group,
node_base.get(),
node_timers.get());
}
/// 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::CallbackGroup::SharedPtr group = nullptr)
{
return create_timer(
clock,
period.to_chrono<std::chrono::nanoseconds>(),
std::forward<CallbackT>(callback),
group,
rclcpp::node_interfaces::get_node_base_interface(node).get(),
rclcpp::node_interfaces::get_node_timers_interface(node).get());
}
/// Convenience method to create a general timer with node resources.
/**
*
* \tparam DurationRepT
* \tparam DurationT
* \tparam CallbackT
* \param clock clock to be used
* \param period period to execute callback. This duration must be 0 <= period < nanoseconds::max()
* \param callback callback to execute via the timer period
* \param group callback group
* \param node_base node base interface
* \param node_timers node timer interface
* \return shared pointer to a generic timer
* \throws std::invalid_argument if either clock, node_base or node_timers
* are nullptr, or period is negative or too large
*/
template<typename DurationRepT, typename DurationT, typename CallbackT>
typename rclcpp::GenericTimer<CallbackT>::SharedPtr
create_timer(
rclcpp::Clock::SharedPtr clock,
std::chrono::duration<DurationRepT, DurationT> period,
CallbackT callback,
rclcpp::CallbackGroup::SharedPtr group,
node_interfaces::NodeBaseInterface * node_base,
node_interfaces::NodeTimersInterface * node_timers)
{
if (clock == nullptr) {
throw std::invalid_argument{"clock cannot be null"};
}
if (node_base == nullptr) {
throw std::invalid_argument{"input node_base cannot be null"};
}
if (node_timers == nullptr) {
throw std::invalid_argument{"input node_timers cannot be null"};
}
const std::chrono::nanoseconds period_ns = detail::safe_cast_to_period_in_ns(period);
// Add a new generic timer.
auto timer = rclcpp::GenericTimer<CallbackT>::make_shared(
std::move(clock), period_ns, std::move(callback), node_base->get_context());
node_timers->add_timer(timer, group);
return timer;
}
/// Convenience method to create a wall timer with node resources.
/**
*
* \tparam DurationRepT
* \tparam DurationT
* \tparam CallbackT
* \param period period to execute callback. This duration must be 0 <= period < nanoseconds::max()
* \param callback callback to execute via the timer period
* \param group callback group
* \param node_base node base interface
* \param node_timers node timer interface
* \return shared pointer to a wall timer
* \throws std::invalid_argument if either node_base or node_timers
* are null, or period is negative or too large
*/
template<typename DurationRepT, typename DurationT, typename CallbackT>
typename rclcpp::WallTimer<CallbackT>::SharedPtr
create_wall_timer(
std::chrono::duration<DurationRepT, DurationT> period,
CallbackT callback,
rclcpp::CallbackGroup::SharedPtr group,
node_interfaces::NodeBaseInterface * node_base,
node_interfaces::NodeTimersInterface * node_timers)
{
if (node_base == nullptr) {
throw std::invalid_argument{"input node_base cannot be null"};
}
if (node_timers == nullptr) {
throw std::invalid_argument{"input node_timers cannot be null"};
}
const std::chrono::nanoseconds period_ns = detail::safe_cast_to_period_in_ns(period);
// Add a new wall timer.
auto timer = rclcpp::WallTimer<CallbackT>::make_shared(
period_ns, std::move(callback), node_base->get_context());
node_timers->add_timer(timer, group);
return timer;
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_TIMER_HPP_

View File

@@ -0,0 +1,70 @@
// Copyright 2021 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__CPP_CALLBACK_TRAMPOLINE_HPP_
#define RCLCPP__DETAIL__CPP_CALLBACK_TRAMPOLINE_HPP_
#include <functional>
namespace rclcpp
{
namespace detail
{
/// Trampoline pattern for wrapping std::function into C-style callbacks.
/**
* A common pattern in C is for a function to take a function pointer and a
* void pointer for "user data" which is passed to the function pointer when it
* is called from within C.
*
* It works by using the user data pointer to store a pointer to a
* std::function instance.
* So when called from C, this function will cast the user data to the right
* std::function type and call it.
*
* This should allow you to use free functions, lambdas with and without
* captures, and various kinds of std::bind instances.
*
* The interior of this function is likely to be executed within a C runtime,
* so no exceptions should be thrown at this point, and doing so results in
* undefined behavior.
*
* \tparam UserDataRealT Declared type of the passed function
* \tparam UserDataT Deduced type based on what is passed for user data,
* usually this type is either `void *` or `const void *`.
* \tparam Args the arguments being passed to the callback
* \tparam ReturnT the return type of this function and the callback, default void
* \param user_data the function pointer, possibly type erased
* \param args the arguments to be forwarded to the callback
* \returns whatever the callback returns, if anything
*/
template<
typename UserDataRealT,
typename UserDataT,
typename ... Args,
typename ReturnT = void
>
ReturnT
cpp_callback_trampoline(UserDataT user_data, Args ... args) noexcept
{
auto & actual_callback = *static_cast<const UserDataRealT *>(user_data);
return actual_callback(args ...);
}
} // namespace detail
} // namespace rclcpp
#endif // RCLCPP__DETAIL__CPP_CALLBACK_TRAMPOLINE_HPP_

View File

@@ -104,6 +104,7 @@ declare_parameter_or_get(
}
}
#ifdef DOXYGEN_ONLY
/// \internal Declare QoS parameters for the given entity.
/**
* \tparam NodeT Node pointer or reference type.
@@ -116,12 +117,23 @@ declare_parameter_or_get(
* \param default_qos User provided qos. It will be used as a default for the parameters declared.
* \return qos profile based on the user provided parameter overrides.
*/
template<typename NodeT, typename EntityQosParametersTraits>
rclcpp::QoS
declare_qos_parameters(
const ::rclcpp::QosOverridingOptions & options,
NodeT & node,
const std::string & topic_name,
const ::rclcpp::QoS & default_qos,
EntityQosParametersTraits);
#else
template<typename NodeT, typename EntityQosParametersTraits>
std::enable_if_t<
rclcpp::node_interfaces::has_node_parameters_interface<
(rclcpp::node_interfaces::has_node_parameters_interface<
decltype(std::declval<typename rcpputils::remove_pointer<NodeT>::type>())>::value ||
std::is_same<typename std::decay_t<NodeT>,
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr>::value,
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr>::value),
rclcpp::QoS>
declare_qos_parameters(
const ::rclcpp::QosOverridingOptions & options,
@@ -204,6 +216,8 @@ declare_qos_parameters(
return default_qos;
}
#endif
/// \internal Helper function to get a rmw qos policy value from a string.
#define RCLCPP_DETAIL_APPLY_QOS_OVERRIDE_FROM_PARAMETER_STRING( \
kind_lower, kind_upper, parameter_value, rclcpp_qos) \

View File

@@ -38,6 +38,7 @@ namespace detail
*
* This issue, with the lambda's, can be demonstrated with this minimal program:
*
* \code{.cpp}
* #include <functional>
* #include <memory>
*
@@ -52,6 +53,7 @@ namespace detail
* std::function<void (std::shared_ptr<int>)> cb = [](std::shared_ptr<int>){};
* f(cb);
* }
* \endcode
*
* If this program ever starts working in a future version of C++, this class
* may become redundant.

View File

@@ -0,0 +1,47 @@
// Copyright 2022 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_
#define RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_
#include <type_traits>
namespace rclcpp
{
namespace detail
{
/// Template meta-function that checks if a given T is contained in the list Us.
template<typename T, typename ... Us>
struct template_contains;
template<typename ... Args>
inline constexpr bool template_contains_v = template_contains<Args ...>::value;
template<typename T, typename NextT, typename ... Us>
struct template_contains<T, NextT, Us ...>
{
enum { value = (std::is_same_v<T, NextT>|| template_contains_v<T, Us ...>)};
};
template<typename T>
struct template_contains<T>
{
enum { value = false };
};
} // namespace detail
} // namespace rclcpp
#endif // RCLCPP__DETAIL__TEMPLATE_CONTAINS_HPP_

View File

@@ -0,0 +1,49 @@
// Copyright 2022 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_
#define RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_
#include <type_traits>
#include "rclcpp/detail/template_contains.hpp"
namespace rclcpp
{
namespace detail
{
/// Template meta-function that checks if a given list Ts contains unique types.
template<typename ... Ts>
struct template_unique;
template<typename ... Args>
inline constexpr bool template_unique_v = template_unique<Args ...>::value;
template<typename NextT, typename ... Ts>
struct template_unique<NextT, Ts ...>
{
enum { value = !template_contains_v<NextT, Ts ...>&& template_unique_v<Ts ...>};
};
template<typename T>
struct template_unique<T>
{
enum { value = true };
};
} // namespace detail
} // namespace rclcpp
#endif // RCLCPP__DETAIL__TEMPLATE_UNIQUE_HPP_

View File

@@ -30,7 +30,7 @@ namespace detail
std::vector<std::string>
get_unparsed_ros_arguments(
int argc, char const * const argv[],
int argc, char const * const * argv,
rcl_arguments_t * arguments,
rcl_allocator_t allocator);

View File

@@ -38,13 +38,6 @@ public:
*/
Duration(int32_t seconds, uint32_t nanoseconds);
/// Construct duration from the specified nanoseconds.
[[deprecated(
"Use Duration::from_nanoseconds instead or std::chrono_literals. For example:"
"rclcpp::Duration::from_nanoseconds(int64_variable);"
"rclcpp::Duration(0ns);")]]
explicit Duration(rcl_duration_value_t nanoseconds);
/// Construct duration from the specified std::chrono::nanoseconds.
explicit Duration(std::chrono::nanoseconds nanoseconds);
@@ -99,9 +92,13 @@ public:
Duration
operator+(const rclcpp::Duration & rhs) const;
Duration & operator+=(const rclcpp::Duration & rhs);
Duration
operator-(const rclcpp::Duration & rhs) const;
Duration & operator-=(const rclcpp::Duration & rhs);
/// Get the maximum representable value.
/**
* \return the maximum representable value
@@ -113,6 +110,9 @@ public:
Duration
operator*(double scale) const;
Duration &
operator*=(double scale);
/// Get duration in nanosecods
/**
* \return the duration in nanoseconds as a rcl_duration_value_t.

View File

@@ -0,0 +1,70 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_
#include <rcl/allocator.h>
#include <rcl/types.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <memory>
#include <string>
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace dynamic_typesupport
{
/// Utility wrapper class for rosidl_dynamic_typesupport_dynamic_data_t
/// STUBBED OUT
class DynamicMessage : public std::enable_shared_from_this<DynamicMessage>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessage)
RCLCPP_PUBLIC
virtual ~DynamicMessage();
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
// It isn't actually used by the builder since the builder should compose its own support
//
// ... Though ideally it should be the exact same support as the one stored in the
// DynamicSerializationSupport
DynamicSerializationSupport::SharedPtr serialization_support_;
rosidl_dynamic_typesupport_dynamic_data_t rosidl_dynamic_data_;
bool is_loaned_;
// Used for returning the loaned value, and lifetime management
DynamicMessage::SharedPtr parent_data_;
private:
RCLCPP_DISABLE_COPY(DynamicMessage)
RCLCPP_PUBLIC
DynamicMessage();
};
} // namespace dynamic_typesupport
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_HPP_

View File

@@ -0,0 +1,64 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_HPP_
#include <rcl/allocator.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <memory>
#include <string>
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace dynamic_typesupport
{
/// Utility wrapper class for `rosidl_dynamic_typesupport_dynamic_type_t`
/// STUBBED OUT
class DynamicMessageType : public std::enable_shared_from_this<DynamicMessageType>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageType)
RCLCPP_PUBLIC
virtual ~DynamicMessageType();
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
// It isn't actually used by the builder since the builder should compose its own support
//
// ... Though ideally it should be the exact same support as the one stored in the
// `DynamicSerializationSupport`
DynamicSerializationSupport::SharedPtr serialization_support_;
rosidl_dynamic_typesupport_dynamic_type_t rosidl_dynamic_type_;
private:
RCLCPP_DISABLE_COPY(DynamicMessageType)
RCLCPP_PUBLIC
DynamicMessageType();
};
} // namespace dynamic_typesupport
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_HPP_

View File

@@ -0,0 +1,65 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_BUILDER_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_BUILDER_HPP_
#include <rcl/allocator.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <memory>
#include <string>
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace dynamic_typesupport
{
/// Utility wrapper class for `rosidl_dynamic_typesupport_dynamic_type_builder_t *`
/// STUBBED OUT
class DynamicMessageTypeBuilder : public std::enable_shared_from_this<DynamicMessageTypeBuilder>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageTypeBuilder)
RCLCPP_PUBLIC
virtual ~DynamicMessageTypeBuilder();
protected:
// NOTE(methylDragon):
// This is just here to extend the lifetime of the serialization support
// It isn't actually used by the builder since the builder should compose its own support
//
// ... Though ideally it should be the exact same support as the one stored in the
// `DynamicSerializationSupport`
DynamicSerializationSupport::SharedPtr serialization_support_;
rosidl_dynamic_typesupport_dynamic_type_builder_t rosidl_dynamic_type_builder_;
private:
RCLCPP_DISABLE_COPY(DynamicMessageTypeBuilder)
RCLCPP_PUBLIC
DynamicMessageTypeBuilder();
};
} // namespace dynamic_typesupport
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_BUILDER_HPP_

View File

@@ -0,0 +1,67 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_SUPPORT_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_SUPPORT_HPP_
#include <rcl/allocator.h>
#include <rosidl_dynamic_typesupport/dynamic_message_type_support_struct.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <rosidl_runtime_c/message_type_support_struct.h>
#include <rosidl_runtime_c/type_description/type_description__struct.h>
#include <memory>
#include <string>
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace dynamic_typesupport
{
/// Utility wrapper class for `rosidl_message_type_support_t` containing managed
/// STUBBED OUT
class DynamicMessageTypeSupport : public std::enable_shared_from_this<DynamicMessageTypeSupport>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicMessageTypeSupport)
RCLCPP_PUBLIC
virtual ~DynamicMessageTypeSupport();
protected:
DynamicSerializationSupport::SharedPtr serialization_support_;
DynamicMessageType::SharedPtr dynamic_message_type_;
DynamicMessage::SharedPtr dynamic_message_;
rosidl_message_type_support_t rosidl_message_type_support_;
private:
RCLCPP_DISABLE_COPY(DynamicMessageTypeSupport)
RCLCPP_PUBLIC
DynamicMessageTypeSupport();
};
} // namespace dynamic_typesupport
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_MESSAGE_TYPE_SUPPORT_HPP_

View File

@@ -0,0 +1,60 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_SERIALIZATION_SUPPORT_HPP_
#define RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_SERIALIZATION_SUPPORT_HPP_
#include <rcl/allocator.h>
#include <rosidl_dynamic_typesupport/api/serialization_support.h>
#include <rosidl_dynamic_typesupport/types.h>
#include <memory>
#include <string>
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace dynamic_typesupport
{
/// Utility wrapper class for rosidl_dynamic_typesupport_serialization_support_t
class DynamicSerializationSupport : public std::enable_shared_from_this<DynamicSerializationSupport>
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(DynamicSerializationSupport)
RCLCPP_PUBLIC
explicit DynamicSerializationSupport(rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
DynamicSerializationSupport(
const std::string & serialization_library_name,
rcl_allocator_t allocator = rcl_get_default_allocator());
RCLCPP_PUBLIC
virtual ~DynamicSerializationSupport();
protected:
rosidl_dynamic_typesupport_serialization_support_t rosidl_serialization_support_;
private:
RCLCPP_DISABLE_COPY(DynamicSerializationSupport)
};
} // namespace dynamic_typesupport
} // namespace rclcpp
#endif // RCLCPP__DYNAMIC_TYPESUPPORT__DYNAMIC_SERIALIZATION_SUPPORT_HPP_

View File

@@ -0,0 +1,311 @@
// 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__EVENT_HANDLER_HPP_
#define RCLCPP__EVENT_HANDLER_HPP_
#include <functional>
#include <memory>
#include <mutex>
#include <stdexcept>
#include <string>
#include "rcl/error_handling.h"
#include "rcl/event_callback.h"
#include "rmw/impl/cpp/demangle.hpp"
#include "rmw/incompatible_qos_events_statuses.h"
#include "rmw/events_statuses/incompatible_type.h"
#include "rcutils/logging_macros.h"
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/logging.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 QOSMessageLostInfo = rmw_message_lost_status_t;
using QOSOfferedIncompatibleQoSInfo = rmw_offered_qos_incompatible_event_status_t;
using QOSRequestedIncompatibleQoSInfo = rmw_requested_qos_incompatible_event_status_t;
using IncompatibleTypeInfo = rmw_incompatible_type_status_t;
using MatchedInfo = rmw_matched_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 &)>;
using QOSMessageLostCallbackType = std::function<void (QOSMessageLostInfo &)>;
using QOSOfferedIncompatibleQoSCallbackType = std::function<void (QOSOfferedIncompatibleQoSInfo &)>;
using QOSRequestedIncompatibleQoSCallbackType =
std::function<void (QOSRequestedIncompatibleQoSInfo &)>;
using IncompatibleTypeCallbackType = std::function<void (IncompatibleTypeInfo &)>;
using PublisherMatchedCallbackType = std::function<void (MatchedInfo &)>;
using SubscriptionMatchedCallbackType = std::function<void (MatchedInfo &)>;
/// Contains callbacks for various types of events a Publisher can receive from the middleware.
struct PublisherEventCallbacks
{
QOSDeadlineOfferedCallbackType deadline_callback;
QOSLivelinessLostCallbackType liveliness_callback;
QOSOfferedIncompatibleQoSCallbackType incompatible_qos_callback;
IncompatibleTypeCallbackType incompatible_type_callback;
PublisherMatchedCallbackType matched_callback;
};
/// Contains callbacks for non-message events that a Subscription can receive from the middleware.
struct SubscriptionEventCallbacks
{
QOSDeadlineRequestedCallbackType deadline_callback;
QOSLivelinessChangedCallbackType liveliness_callback;
QOSRequestedIncompatibleQoSCallbackType incompatible_qos_callback;
QOSMessageLostCallbackType message_lost_callback;
IncompatibleTypeCallbackType incompatible_type_callback;
SubscriptionMatchedCallbackType matched_callback;
};
class UnsupportedEventTypeException : public exceptions::RCLErrorBase, public std::runtime_error
{
public:
RCLCPP_PUBLIC
UnsupportedEventTypeException(
rcl_ret_t ret,
const rcl_error_state_t * error_state,
const std::string & prefix);
RCLCPP_PUBLIC
UnsupportedEventTypeException(
const exceptions::RCLErrorBase & base_exc,
const std::string & prefix);
};
class EventHandlerBase : public Waitable
{
public:
enum class EntityType : std::size_t
{
Event,
};
RCLCPP_PUBLIC
virtual ~EventHandlerBase();
/// 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
void
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;
/// Set a callback to be called when each new event instance occurs.
/**
* The callback receives a size_t which is the number of events that occurred
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if events occurred before any
* callback was set.
*
* The callback also receives an int identifier argument.
* This is needed because a Waitable may be composed of several distinct entities,
* such as subscriptions, services, etc.
* The application should provide a generic callback function that will be then
* forwarded by the waitable to all of its entities.
* Before forwarding, a different value for the identifier argument will be
* bond to the function.
* This implies that the provided callback can use the identifier to behave
* differently depending on which entity triggered the waitable to become ready.
*
* Since this callback is called from the middleware, you should aim to make
* it fast and not blocking.
* If you need to do a lot of work or wait for some other event, you should
* spin it off to another thread, otherwise you risk blocking the middleware.
*
* Calling it again will clear any previously set callback.
*
* An exception will be thrown if the callback is not callable.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the qos event
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rmw_event_set_callback
* \sa rcl_event_set_callback
*
* \param[in] callback functor to be called when a new event occurs
*/
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override
{
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_ready_callback "
"is not callable.");
}
// Note: we bind the int identifier argument to this waitable's entity types
auto new_callback =
[callback, this](size_t number_of_events) {
try {
callback(number_of_events, static_cast<int>(EntityType::Event));
} catch (const std::exception & exception) {
RCLCPP_ERROR_STREAM(
// TODO(wjwwood): get this class access to the node logger it is associated with
rclcpp::get_logger("rclcpp"),
"rclcpp::EventHandlerBase@" << this <<
" caught " << rmw::impl::cpp::demangle(exception) <<
" exception in user-provided callback for the 'on ready' callback: " <<
exception.what());
} catch (...) {
RCLCPP_ERROR_STREAM(
rclcpp::get_logger("rclcpp"),
"rclcpp::EventHandlerBase@" << this <<
" caught unhandled exception in user-provided callback " <<
"for the 'on ready' callback");
}
};
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
// been replaced but the middleware hasn't been told about the new one yet.
set_on_new_event_callback(
rclcpp::detail::cpp_callback_trampoline<decltype(new_callback), const void *, size_t>,
static_cast<const void *>(&new_callback));
// Store the std::function to keep it in scope, also overwrites the existing one.
on_new_event_callback_ = new_callback;
// Set it again, now using the permanent storage.
set_on_new_event_callback(
rclcpp::detail::cpp_callback_trampoline<
decltype(on_new_event_callback_), const void *, size_t>,
static_cast<const void *>(&on_new_event_callback_));
}
/// Unset the callback registered for new events, if any.
void
clear_on_ready_callback() override
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
if (on_new_event_callback_) {
set_on_new_event_callback(nullptr, nullptr);
on_new_event_callback_ = nullptr;
}
}
protected:
RCLCPP_PUBLIC
void
set_on_new_event_callback(rcl_event_callback_t callback, const void * user_data);
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_event_callback_{nullptr};
rcl_event_t event_handle_;
size_t wait_set_event_index_;
};
using QOSEventHandlerBase [[deprecated("Use rclcpp::EventHandlerBase")]] = EventHandlerBase;
template<typename EventCallbackT, typename ParentHandleT>
class EventHandler : public EventHandlerBase
{
public:
template<typename InitFuncT, typename EventTypeEnum>
EventHandler(
const EventCallbackT & callback,
InitFuncT init_func,
ParentHandleT parent_handle,
EventTypeEnum event_type)
: parent_handle_(parent_handle), event_callback_(callback)
{
event_handle_ = rcl_get_zero_initialized_event();
rcl_ret_t ret = init_func(&event_handle_, parent_handle.get(), event_type);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_UNSUPPORTED) {
UnsupportedEventTypeException exc(ret, rcl_get_error_state(), "Failed to initialize event");
rcl_reset_error();
throw exc;
} else {
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to initialize event");
}
}
}
/// Take data so that the callback cannot be scheduled again
std::shared_ptr<void>
take_data() 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 nullptr;
}
return std::static_pointer_cast<void>(std::make_shared<EventCallbackInfoT>(callback_info));
}
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override
{
(void)id;
return take_data();
}
/// Execute any entities of the Waitable that are ready.
void
execute(std::shared_ptr<void> & data) override
{
if (!data) {
throw std::runtime_error("'data' is empty");
}
auto callback_ptr = std::static_pointer_cast<EventCallbackInfoT>(data);
event_callback_(*callback_ptr);
callback_ptr.reset();
}
private:
using EventCallbackInfoT = typename std::remove_reference<typename
rclcpp::function_traits::function_traits<EventCallbackT>::template argument_type<0>>::type;
ParentHandleT parent_handle_;
EventCallbackT event_callback_;
};
template<typename EventCallbackT, typename ParentHandleT>
using QOSEventHandler [[deprecated("Use rclcpp::EventHandler")]] = EventHandler<EventCallbackT,
ParentHandleT>;
} // namespace rclcpp
#endif // RCLCPP__EVENT_HANDLER_HPP_

View File

@@ -109,6 +109,8 @@ public:
: std::runtime_error(msg) {}
};
typedef void (* reset_error_function_t)();
/// Throw a C++ std::exception which was created based on an rcl error.
/**
* Passing nullptr for reset_error is safe and will avoid calling any function
@@ -129,7 +131,7 @@ 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);
reset_error_function_t reset_error = rcl_reset_error);
/* *INDENT-ON* */
class RCLErrorBase
@@ -254,6 +256,23 @@ public:
{}
};
/// Thrown if user attempts to create an uninitialized statically typed parameter
/**
* (see https://github.com/ros2/rclcpp/issues/1691)
*/
class UninitializedStaticallyTypedParameterException : public std::runtime_error
{
public:
/// Construct an instance.
/**
* \param[in] name the name of the parameter.
*/
RCLCPP_PUBLIC
explicit UninitializedStaticallyTypedParameterException(const std::string & name)
: std::runtime_error("Statically typed parameter '" + name + "' must be initialized.")
{}
};
/// Thrown if parameter is already declared.
class ParameterAlreadyDeclaredException : public std::runtime_error
{
@@ -289,7 +308,6 @@ public:
/// Construct an instance.
/**
* \param[in] name the name of the parameter.
* \param[in] message custom exception message.
*/
explicit ParameterUninitializedException(const std::string & name)
: std::runtime_error("parameter '" + name + "' is not initialized")

View File

@@ -19,6 +19,7 @@
#include <cassert>
#include <chrono>
#include <cstdlib>
#include <deque>
#include <iostream>
#include <list>
#include <map>
@@ -29,28 +30,27 @@
#include "rcl/guard_condition.h"
#include "rcl/wait.h"
#include "rclcpp/executors/executor_notify_waitable.hpp"
#include "rcpputils/scope_exit.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/executor_options.hpp"
#include "rclcpp/executors/executor_entities_collection.hpp"
#include "rclcpp/executors/executor_entities_collector.hpp"
#include "rclcpp/future_return_code.hpp"
#include "rclcpp/memory_strategies.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/wait_set.hpp"
namespace rclcpp
{
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
std::owner_less<rclcpp::CallbackGroup::WeakPtr>> WeakCallbackGroupsToNodesMap;
// Forward declaration is used in convenience method signature.
class Node;
class ExecutorImplementation;
/// Coordinate the order and timing of available communication tasks.
/**
@@ -305,7 +305,9 @@ public:
* If the time that waitables take to be executed is longer than the period on which new waitables
* become ready, this method will execute work repeatedly until `max_duration` has elapsed.
*
* \param[in] max_duration The maximum amount of time to spend executing work. Must be positive.
* \param[in] max_duration The maximum amount of time to spend executing work, must be >= 0.
* `0` is potentially block forever until no more work is available.
* \throw std::invalid_argument if max_duration is less than 0.
* Note that spin_all() may take longer than this time as it only returns once max_duration has
* been exceeded.
*/
@@ -313,6 +315,16 @@ public:
virtual void
spin_all(std::chrono::nanoseconds max_duration);
/// Collect work once and execute the next available work, optionally within a duration.
/**
* This function can be overridden. The default implementation is suitable for
* a single-thread model of execution.
* Adding subscriptions, timers, services, etc. with blocking callbacks will cause this function
* to block (which may have unintended consequences).
* \param[in] timeout The maximum amount of time to spend waiting for work.
* `-1` is potentially block forever waiting for work.
*/
RCLCPP_PUBLIC
virtual void
spin_once(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
@@ -390,24 +402,39 @@ public:
void
cancel();
/// Support dynamic switching of the memory strategy.
/// Returns true if the executor is currently spinning.
/**
* Switching the memory strategy while the executor is spinning in another threading could have
* unintended consequences.
* \param[in] memory_strategy Shared pointer to the memory strategy to set.
* \throws std::runtime_error if memory_strategy is null
* This function can be called asynchronously from any thread.
* \return True if the executor is currently spinning.
*/
RCLCPP_PUBLIC
void
set_memory_strategy(memory_strategy::MemoryStrategy::SharedPtr memory_strategy);
bool
is_spinning();
protected:
/// Add a node to executor, execute the next available unit of work, and remove the node.
/**
* Implementation of spin_node_once using std::chrono::nanoseconds
* \param[in] node Shared pointer to the node to add.
* \param[in] timeout How long to wait for work to become available. Negative values cause
* spin_node_once to block indefinitely (the default behavior). A timeout of 0 causes this
* function to be non-blocking.
*/
RCLCPP_PUBLIC
void
spin_node_once_nanoseconds(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::nanoseconds timeout);
/// Collect work and execute available work, optionally within a duration.
/**
* Implementation of spin_some and spin_all.
* The exhaustive flag controls if the function will re-collect available work within the duration.
*
* \param[in] max_duration The maximum amount of time to spend executing work, or 0 for no limit.
* \param[in] exhaustive when set to true, continue to collect work and execute (spin_all)
* when set to false, return when all collected work is executed (spin_some)
*/
RCLCPP_PUBLIC
void
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
@@ -422,123 +449,97 @@ protected:
void
execute_any_executable(AnyExecutable & any_exec);
/// Run subscription executable.
/**
* Do necessary setup and tear-down as well as executing the subscription.
* \param[in] subscription Subscription to execute
*/
RCLCPP_PUBLIC
static void
execute_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription);
/// Run timer executable.
/**
* Do necessary setup and tear-down as well as executing the timer callback.
* \param[in] timer Timer to execute
*/
RCLCPP_PUBLIC
static void
execute_timer(rclcpp::TimerBase::SharedPtr timer);
/// Run service server executable.
/**
* Do necessary setup and tear-down as well as executing the service server callback.
* \param[in] service Service to execute
*/
RCLCPP_PUBLIC
static void
execute_service(rclcpp::ServiceBase::SharedPtr service);
/// Run service client executable.
/**
* Do necessary setup and tear-down as well as executing the service client callback.
* \param[in] service Service to execute
*/
RCLCPP_PUBLIC
static void
execute_client(rclcpp::ClientBase::SharedPtr client);
/// Gather all of the waitable entities from associated nodes and callback groups.
RCLCPP_PUBLIC
void
collect_entities();
/// Block until more work becomes avilable or timeout is reached.
/**
* Builds a set of waitable entities, which are passed to the middleware.
* After building wait set, waits on middleware to notify.
* \param[in] timeout duration to wait for new work to become available.
* \throws std::runtime_error if the wait set can be cleared
*/
RCLCPP_PUBLIC
void
wait_for_work(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
rclcpp::CallbackGroup::SharedPtr group);
/// Return true if the node has been added to this executor.
/// Check for executable in ready state and populate union structure.
/**
* \param[in] node_ptr a shared pointer that points to a node base interface
* \return true if the node is associated with the executor, otherwise false
* \param[out] any_executable populated union structure of ready executable
* \return true if an executable was ready and any_executable was populated,
* otherwise false
*/
RCLCPP_PUBLIC
bool
has_node(
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) const;
RCLCPP_PUBLIC
rclcpp::CallbackGroup::SharedPtr
get_group_by_timer(rclcpp::TimerBase::SharedPtr timer);
/// Add a callback group to an executor
/**
* \see rclcpp::Executor::add_callback_group
*/
RCLCPP_PUBLIC
virtual void
add_callback_group_to_map(
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
bool notify = true) RCPPUTILS_TSA_REQUIRES(mutex_);
/// Remove a callback group from the executor.
/**
* \see rclcpp::Executor::remove_callback_group
*/
RCLCPP_PUBLIC
virtual void
remove_callback_group_from_map(
rclcpp::CallbackGroup::SharedPtr group_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes,
bool notify = true) RCPPUTILS_TSA_REQUIRES(mutex_);
RCLCPP_PUBLIC
bool
get_next_ready_executable(AnyExecutable & any_executable);
RCLCPP_PUBLIC
bool
get_next_ready_executable_from_map(
AnyExecutable & any_executable,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
/// Wait for executable in ready state and populate union structure.
/**
* If an executable is ready, it will return immediately, otherwise
* block based on the timeout for work to become ready.
*
* \param[out] any_executable populated union structure of ready executable
* \param[in] timeout duration of time to wait for work, a negative value
* (the defualt behavior), will make this function block indefinitely
* \return true if an executable was ready and any_executable was populated,
* otherwise false
*/
RCLCPP_PUBLIC
bool
get_next_executable(
AnyExecutable & any_executable,
std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
/// Add all callback groups that can be automatically added from associated nodes.
/**
* The executor, before collecting entities, verifies if any callback group from
* nodes associated with the executor, which is not already associated to an executor,
* can be automatically added to this executor.
* This takes care of any callback group that has been added to a node but not explicitly added
* to the executor.
* It is important to note that in order for the callback groups to be automatically added to an
* executor through this function, the node of the callback groups needs to have been added
* through the `add_node` method.
*/
RCLCPP_PUBLIC
virtual void
add_callback_groups_from_nodes_associated_to_executor() RCPPUTILS_TSA_REQUIRES(mutex_);
/// Spinning state, used to prevent multi threaded calls to spin and to cancel blocking spins.
std::atomic_bool spinning;
/// Guard condition for signaling the rmw layer to wake up for special events.
rclcpp::GuardCondition interrupt_guard_condition_;
std::shared_ptr<rclcpp::GuardCondition> interrupt_guard_condition_;
/// Guard condition for signaling the rmw layer to wake up for system shutdown.
std::shared_ptr<rclcpp::GuardCondition> shutdown_guard_condition_;
/// 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_.
mutable std::mutex mutex_;
/// The memory strategy: an interface for handling user-defined memory allocation strategies.
memory_strategy::MemoryStrategy::SharedPtr
memory_strategy_ RCPPUTILS_TSA_PT_GUARDED_BY(mutex_);
/// The context associated with this executor.
std::shared_ptr<rclcpp::Context> context_;
@@ -548,33 +549,39 @@ protected:
virtual void
spin_once_impl(std::chrono::nanoseconds timeout);
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
const rclcpp::GuardCondition *,
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
WeakNodesToGuardConditionsMap;
/// Waitable containing guard conditions controlling the executor flow.
/**
* This waitable contains the interrupt and shutdown guard condition, as well
* as the guard condition associated with each node and callback group.
* By default, if any change is detected in the monitored entities, the notify
* waitable will awake the executor and rebuild the collections.
*/
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> notify_waitable_;
/// maps nodes to guard conditions
WeakNodesToGuardConditionsMap
weak_nodes_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
std::atomic_bool entities_need_rebuild_;
/// maps callback groups associated to nodes
WeakCallbackGroupsToNodesMap
weak_groups_associated_with_executor_to_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Collector used to associate executable entities from nodes and guard conditions
rclcpp::executors::ExecutorEntitiesCollector collector_;
/// maps callback groups to nodes associated with executor
WeakCallbackGroupsToNodesMap
weak_groups_to_nodes_associated_with_executor_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Waitset to be waited on.
rclcpp::WaitSet wait_set_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// maps all callback groups to nodes
WeakCallbackGroupsToNodesMap
weak_groups_to_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Hold the current state of the collection being waited on by the waitset
rclcpp::executors::ExecutorEntitiesCollection current_collection_ RCPPUTILS_TSA_GUARDED_BY(
mutex_);
/// nodes that are associated with the executor
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>
weak_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Hold the current state of the notify waitable being waited on by the waitset
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> current_notify_waitable_
RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Hold the list of executables currently available to be executed.
std::deque<rclcpp::AnyExecutable> ready_executables_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// shutdown callback handle registered to Context
rclcpp::OnShutdownCallbackHandle shutdown_callback_handle_;
/// Pointer to implementation
std::unique_ptr<ExecutorImplementation> impl_;
};
} // namespace rclcpp

View File

@@ -21,6 +21,7 @@
#include "rclcpp/executors/multi_threaded_executor.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/executors/static_single_threaded_executor.hpp"
#include "rclcpp/experimental/executors/events_executor/events_executor.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"

View File

@@ -0,0 +1,213 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTION_HPP_
#define RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTION_HPP_
#include <deque>
#include <functional>
#include <unordered_map>
#include <vector>
#include <rclcpp/any_executable.hpp>
#include <rclcpp/node_interfaces/node_base.hpp>
#include <rclcpp/callback_group.hpp>
#include <rclcpp/executors/executor_notify_waitable.hpp>
#include <rclcpp/visibility_control.hpp>
#include <rclcpp/wait_result.hpp>
#include <rclcpp/wait_set.hpp>
namespace rclcpp
{
namespace executors
{
/// Structure to represent a single entity's entry in a collection
template<typename EntityValueType>
struct CollectionEntry
{
/// Weak pointer to entity type
using EntityWeakPtr = typename EntityValueType::WeakPtr;
/// Shared pointer to entity type
using EntitySharedPtr = typename EntityValueType::SharedPtr;
/// The entity
EntityWeakPtr entity;
/// If relevant, the entity's corresponding callback_group
rclcpp::CallbackGroup::WeakPtr callback_group;
};
/// Update a collection based on another collection
/*
* Iterates update_from and update_to to see which entities have been added/removed between
* the two collections.
*
* For each new entry (in update_from, but not in update_to),
* add the entity and fire the on_added callback
* For each removed entry (in update_to, but not in update_from),
* remove the entity and fire the on_removed callback.
*
* \param[in] update_from The collection representing the next iteration's state
* \param[inout] update_to The collection representing the current iteration's state
* \param[in] on_added Callback fired when a new entity is detected
* \param[in] on_removed Callback fired when an entity is removed
*/
template<typename CollectionType>
void update_entities(
const CollectionType & update_from,
CollectionType & update_to,
std::function<void(const typename CollectionType::EntitySharedPtr &)> on_added,
std::function<void(const typename CollectionType::EntitySharedPtr &)> on_removed
)
{
for (auto it = update_to.begin(); it != update_to.end(); ) {
if (update_from.count(it->first) == 0) {
auto entity = it->second.entity.lock();
if (entity) {
on_removed(entity);
}
it = update_to.erase(it);
} else {
++it;
}
}
for (auto it = update_from.begin(); it != update_from.end(); ++it) {
if (update_to.count(it->first) == 0) {
auto entity = it->second.entity.lock();
if (entity) {
on_added(entity);
}
update_to.insert(*it);
}
}
}
/// A collection of entities, indexed by their corresponding handles
template<typename EntityKeyType, typename EntityValueType>
class EntityCollection
: public std::unordered_map<const EntityKeyType *, CollectionEntry<EntityValueType>>
{
public:
/// Key type of the map
using Key = const EntityKeyType *;
/// Weak pointer to entity type
using EntityWeakPtr = typename EntityValueType::WeakPtr;
/// Shared pointer to entity type
using EntitySharedPtr = typename EntityValueType::SharedPtr;
/// Update this collection based on the contents of another collection
/**
* Update the internal state of this collection, firing callbacks when entities have been
* added or removed.
*
* \param[in] other Collection to compare to
* \param[in] on_added Callback for when entities have been added
* \param[in] on_removed Callback for when entities have been removed
*/
void update(
const EntityCollection<EntityKeyType, EntityValueType> & other,
std::function<void(const EntitySharedPtr &)> on_added,
std::function<void(const EntitySharedPtr &)> on_removed)
{
update_entities(other, *this, on_added, on_removed);
}
};
/// Represent the total set of entities for a single executor
/**
* This allows the entities to be stored from ExecutorEntitiesCollector.
* The structure also makes in convenient to re-evaluate when entities have been added or removed.
*/
struct ExecutorEntitiesCollection
{
/// Collection type for timer entities
using TimerCollection = EntityCollection<rcl_timer_t, rclcpp::TimerBase>;
/// Collection type for subscription entities
using SubscriptionCollection = EntityCollection<rcl_subscription_t, rclcpp::SubscriptionBase>;
/// Collection type for client entities
using ClientCollection = EntityCollection<rcl_client_t, rclcpp::ClientBase>;
/// Collection type for service entities
using ServiceCollection = EntityCollection<rcl_service_t, rclcpp::ServiceBase>;
/// Collection type for waitable entities
using WaitableCollection = EntityCollection<rclcpp::Waitable, rclcpp::Waitable>;
/// Collection type for guard condition entities
using GuardConditionCollection = EntityCollection<rcl_guard_condition_t, rclcpp::GuardCondition>;
/// Collection of timers currently in use by the executor.
TimerCollection timers;
/// Collection of subscriptions currently in use by the executor.
SubscriptionCollection subscriptions;
/// Collection of clients currently in use by the executor.
ClientCollection clients;
/// Collection of services currently in use by the executor.
ServiceCollection services;
/// Collection of guard conditions currently in use by the executor.
GuardConditionCollection guard_conditions;
/// Collection of waitables currently in use by the executor.
WaitableCollection waitables;
/// Check if the entities collection is empty
/**
* \return true if all member collections are empty, false otherwise
*/
bool empty() const;
/// Clear the entities collection
void clear();
};
/// Build an entities collection from callback groups
/**
* Iterates a list of callback groups and adds entities from each valid group
*
* \param[in] callback_groups List of callback groups to check for entities
* \param[inout] colletion Entities collection to populate with found entities
*/
void
build_entities_collection(
const std::vector<rclcpp::CallbackGroup::WeakPtr> & callback_groups,
ExecutorEntitiesCollection & collection);
/// Build a queue of executables ready to be executed
/**
* Iterates a list of entities and adds them to a queue if they are ready.
*
* \param[in] collection Collection of entities corresponding to the current wait set.
* \param[in] wait_result Result of rclcpp::WaitSet::wait corresponding to the collection.
* \param[inout] queue of executables to append new ready executables to
* \return number of new ready executables
*/
size_t
ready_executables(
const ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
std::deque<rclcpp::AnyExecutable> & executables
);
} // namespace executors
} // namespace rclcpp
#endif // RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTION_HPP_

View File

@@ -0,0 +1,270 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTOR_HPP_
#define RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTOR_HPP_
#include <map>
#include <memory>
#include <mutex>
#include <set>
#include <vector>
#include "rcpputils/thread_safety_annotations.hpp"
#include <rclcpp/any_executable.hpp>
#include <rclcpp/node_interfaces/node_base.hpp>
#include <rclcpp/callback_group.hpp>
#include <rclcpp/executors/executor_notify_waitable.hpp>
#include <rclcpp/visibility_control.hpp>
#include <rclcpp/wait_set.hpp>
#include <rclcpp/wait_result.hpp>
namespace rclcpp
{
namespace executors
{
/// Class to monitor a set of nodes and callback groups for changes in entity membership
/**
* This is to be used with an executor to track the membership of various nodes, groups,
* and entities (timers, subscriptions, clients, services, etc) and report status to the
* executor.
*
* In general, users will add either nodes or callback groups to an executor.
* Each node may have callback groups that are automatically associated with executors,
* or callback groups that must be manually associated with an executor.
*
* This object tracks both types of callback groups as well as nodes that have been
* previously added to the executor.
* When a new callback group is added/removed or new entities are added/removed, the
* corresponding node or callback group will signal this to the executor so that the
* entity collection may be rebuilt according to that executor's implementation.
*
*/
class ExecutorEntitiesCollector
{
public:
/// Constructor
/**
* \param[in] notify_waitable Waitable that is used to signal to the executor
* when nodes or callback groups have been added or removed.
*/
RCLCPP_PUBLIC
explicit ExecutorEntitiesCollector(
std::shared_ptr<ExecutorNotifyWaitable> notify_waitable);
/// Destructor
RCLCPP_PUBLIC
~ExecutorEntitiesCollector();
/// Indicate if the entities collector has pending additions or removals.
/**
* \return true if there are pending additions or removals
*/
bool has_pending() const;
/// Add a node to the entity collector
/**
* \param[in] node_ptr a shared pointer that points to a node base interface
* \throw std::runtime_error if the node is associated with an executor
*/
RCLCPP_PUBLIC
void
add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
/// Remove a node from the entity collector
/**
* \param[in] node_ptr a shared pointer that points to a node base interface
* \throw std::runtime_error if the node is associated with an executor
* \throw std::runtime_error if the node is associated with this executor
*/
RCLCPP_PUBLIC
void
remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
/// Add a callback group to the entity collector
/**
* \param[in] group_ptr a shared pointer that points to a callback group
* \throw std::runtime_error if the callback_group is associated with an executor
*/
RCLCPP_PUBLIC
void
add_callback_group(rclcpp::CallbackGroup::SharedPtr group_ptr);
/// Remove a callback group from the entity collector
/**
* \param[in] group_ptr a shared pointer that points to a callback group
* \throw std::runtime_error if the callback_group is not associated with an executor
* \throw std::runtime_error if the callback_group is not associated with this executor
*/
RCLCPP_PUBLIC
void
remove_callback_group(rclcpp::CallbackGroup::SharedPtr group_ptr);
/// Get all callback groups known to this entity collector
/**
* This will include manually added and automatically added (node associated) groups
* \return vector of all callback groups
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_all_callback_groups() const;
/// Get manually-added callback groups known to this entity collector
/**
* This will include callback groups that have been added via add_callback_group
* \return vector of manually-added callback groups
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_manually_added_callback_groups() const;
/// Get automatically-added callback groups known to this entity collector
/**
* This will include callback groups that are associated with nodes added via add_node
* \return vector of automatically-added callback groups
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_automatically_added_callback_groups() const;
/// Update the underlying collections
/**
* This will prune nodes and callback groups that are no longer valid as well
* as add new callback groups from any associated nodes.
*/
RCLCPP_PUBLIC
void
update_collections();
protected:
using NodeCollection = std::set<
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>;
using CallbackGroupCollection = std::set<
rclcpp::CallbackGroup::WeakPtr,
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>;
using WeakNodesToGuardConditionsMap = std::map<
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
rclcpp::GuardCondition::WeakPtr,
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>;
using WeakGroupsToGuardConditionsMap = std::map<
rclcpp::CallbackGroup::WeakPtr,
rclcpp::GuardCondition::WeakPtr,
std::owner_less<rclcpp::CallbackGroup::WeakPtr>>;
/// Implementation of removing a node from the collector.
/**
* This will disassociate the node from the collector and remove any
* automatically-added callback groups
*
* This takes and returns an iterator so it may be used as:
*
* it = remove_weak_node(it);
*
* \param[in] weak_node iterator to the weak node to be removed
* \return Valid updated iterator in the same collection
*/
RCLCPP_PUBLIC
NodeCollection::iterator
remove_weak_node(NodeCollection::iterator weak_node) RCPPUTILS_TSA_REQUIRES(mutex_);
/// Implementation of removing a callback group from the collector.
/**
* This will disassociate the callback group from the collector
*
* This takes and returns an iterator so it may be used as:
*
* it = remove_weak_callback_group(it);
*
* \param[in] weak_group_it iterator to the weak group to be removed
* \param[in] collection the collection to remove the group from
* (manually or automatically added)
* \return Valid updated iterator in the same collection
*/
RCLCPP_PUBLIC
CallbackGroupCollection::iterator
remove_weak_callback_group(
CallbackGroupCollection::iterator weak_group_it,
CallbackGroupCollection & collection) RCPPUTILS_TSA_REQUIRES(mutex_);
/// Implementation of adding a callback group
/**
* \param[in] group_ptr the group to add
* \param[in] collection the collection to add the group to
*/
RCLCPP_PUBLIC
void
add_callback_group_to_collection(
rclcpp::CallbackGroup::SharedPtr group_ptr,
CallbackGroupCollection & collection) RCPPUTILS_TSA_REQUIRES(mutex_);
/// Iterate over queued added/remove nodes and callback_groups
RCLCPP_PUBLIC
void
process_queues() RCPPUTILS_TSA_REQUIRES(mutex_);
/// Check a collection of nodes and add any new callback_groups that
/// are set to be automatically associated via the node.
RCLCPP_PUBLIC
void
add_automatically_associated_callback_groups(
const NodeCollection & nodes_to_check) RCPPUTILS_TSA_REQUIRES(mutex_);
/// Check all nodes and group for expired weak pointers and remove them.
RCLCPP_PUBLIC
void
prune_invalid_nodes_and_groups() RCPPUTILS_TSA_REQUIRES(mutex_);
/// mutex to protect collections and pending queues
mutable std::mutex mutex_;
/// Callback groups that were added via `add_callback_group`
CallbackGroupCollection manually_added_groups_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Callback groups that were added by their association with added nodes
CallbackGroupCollection automatically_added_groups_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// nodes that are associated with the executor
NodeCollection weak_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Track guard conditions associated with added nodes
WeakNodesToGuardConditionsMap weak_nodes_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Track guard conditions associated with added callback groups
WeakGroupsToGuardConditionsMap weak_groups_to_guard_conditions_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// nodes that have been added since the last update.
NodeCollection pending_added_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// nodes that have been removed since the last update.
NodeCollection pending_removed_nodes_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// callback groups that have been added since the last update.
CallbackGroupCollection pending_manually_added_groups_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// callback groups that have been removed since the last update.
CallbackGroupCollection pending_manually_removed_groups_ RCPPUTILS_TSA_GUARDED_BY(mutex_);
/// Waitable to add guard conditions to
std::shared_ptr<ExecutorNotifyWaitable> notify_waitable_;
};
} // namespace executors
} // namespace rclcpp
//
#endif // RCLCPP__EXECUTORS__EXECUTOR_ENTITIES_COLLECTOR_HPP_

View File

@@ -0,0 +1,158 @@
// Copyright 2023 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXECUTORS__EXECUTOR_NOTIFY_WAITABLE_HPP_
#define RCLCPP__EXECUTORS__EXECUTOR_NOTIFY_WAITABLE_HPP_
#include <functional>
#include <memory>
#include <mutex>
#include <set>
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/waitable.hpp"
namespace rclcpp
{
namespace executors
{
/// Maintain a collection of guard conditions from associated nodes and callback groups
/// to signal to the executor when associated entities have changed.
class ExecutorNotifyWaitable : public rclcpp::Waitable
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(ExecutorNotifyWaitable)
// Constructor
/**
* \param[in] on_execute_callback Callback to execute when one of the conditions
* of this waitable has signaled the wait_set.
*/
RCLCPP_PUBLIC
explicit ExecutorNotifyWaitable(std::function<void(void)> on_execute_callback = {});
// Destructor
RCLCPP_PUBLIC
~ExecutorNotifyWaitable() override = default;
RCLCPP_PUBLIC
ExecutorNotifyWaitable(const ExecutorNotifyWaitable & other);
RCLCPP_PUBLIC
ExecutorNotifyWaitable & operator=(const ExecutorNotifyWaitable & other);
/// Add conditions to the wait set
/**
* \param[inout] wait_set structure that conditions will be added to
*/
RCLCPP_PUBLIC
void
add_to_wait_set(rcl_wait_set_t * wait_set) override;
/// Check conditions against the wait set
/**
* \param[inout] wait_set structure that internal elements will be checked against.
* \return true if this waitable is ready to be executed, false otherwise.
*/
RCLCPP_PUBLIC
bool
is_ready(rcl_wait_set_t * wait_set) override;
/// Perform work associated with the waitable.
/**
* This will call the callback provided in the constructor.
* \param[in] data Data to be use for the execute, if available, else nullptr.
*/
RCLCPP_PUBLIC
void
execute(std::shared_ptr<void> & data) override;
/// Retrieve data to be used in the next execute call.
/**
* \return If available, data to be used, otherwise nullptr
*/
RCLCPP_PUBLIC
std::shared_ptr<void>
take_data() override;
/// Take the data from an entity ID so that it can be consumed with `execute`.
/**
* \param[in] id ID of the entity to take data from.
* \return If available, data to be used, otherwise nullptr
* \sa rclcpp::Waitable::take_data_by_entity_id
*/
RCLCPP_PUBLIC
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override;
/// Set a callback to be called whenever the waitable becomes ready.
/**
* \param[in] callback callback to set
* \sa rclcpp::Waitable::set_on_ready_callback
*/
RCLCPP_PUBLIC
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override;
/// Add a guard condition to be waited on.
/**
* \param[in] guard_condition The guard condition to add.
*/
RCLCPP_PUBLIC
void
add_guard_condition(rclcpp::GuardCondition::WeakPtr guard_condition);
/// Unset any callback registered via set_on_ready_callback.
/**
* \sa rclcpp::Waitable::clear_on_ready_callback
*/
RCLCPP_PUBLIC
void
clear_on_ready_callback() override;
/// Remove a guard condition from being waited on.
/**
* \param[in] weak_guard_condition The guard condition to remove.
*/
RCLCPP_PUBLIC
void
remove_guard_condition(rclcpp::GuardCondition::WeakPtr weak_guard_condition);
/// Get the number of ready guard_conditions
/**
* \return The number of guard_conditions associated with the Waitable.
*/
RCLCPP_PUBLIC
size_t
get_number_of_ready_guard_conditions() override;
private:
/// Callback to run when waitable executes
std::function<void(void)> execute_callback_;
std::mutex guard_condition_mutex_;
std::function<void(size_t)> on_ready_callback_;
/// The collection of guard conditions to be waited on.
std::set<rclcpp::GuardCondition::WeakPtr,
std::owner_less<rclcpp::GuardCondition::WeakPtr>> notify_guard_conditions_;
};
} // namespace executors
} // namespace rclcpp
#endif // RCLCPP__EXECUTORS__EXECUTOR_NOTIFY_WAITABLE_HPP_

View File

@@ -47,12 +47,12 @@ public:
*
* \param options common options for all executors
* \param number_of_threads number of threads to have in the thread pool,
* the default 0 will use the number of cpu cores found instead
* the default 0 will use the number of cpu cores found (minimum of 2)
* \param yield_before_execute if true std::this_thread::yield() is called
* \param timeout maximum time to wait
*/
RCLCPP_PUBLIC
MultiThreadedExecutor(
explicit MultiThreadedExecutor(
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions(),
size_t number_of_threads = 0,
bool yield_before_execute = false,

View File

@@ -1,356 +0,0 @@
// Copyright 2020 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXECUTORS__STATIC_EXECUTOR_ENTITIES_COLLECTOR_HPP_
#define RCLCPP__EXECUTORS__STATIC_EXECUTOR_ENTITIES_COLLECTOR_HPP_
#include <chrono>
#include <list>
#include <map>
#include <memory>
#include <vector>
#include "rcl/guard_condition.h"
#include "rcl/wait.h"
#include "rclcpp/experimental/executable_list.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/waitable.hpp"
namespace rclcpp
{
namespace executors
{
typedef std::map<rclcpp::CallbackGroup::WeakPtr,
rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
std::owner_less<rclcpp::CallbackGroup::WeakPtr>> WeakCallbackGroupsToNodesMap;
class StaticExecutorEntitiesCollector final
: public rclcpp::Waitable,
public std::enable_shared_from_this<StaticExecutorEntitiesCollector>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(StaticExecutorEntitiesCollector)
// Constructor
RCLCPP_PUBLIC
StaticExecutorEntitiesCollector() = default;
// Destructor
RCLCPP_PUBLIC
~StaticExecutorEntitiesCollector();
/// Initialize StaticExecutorEntitiesCollector
/**
* \param p_wait_set A reference to the wait set to be used in the executor
* \param memory_strategy Shared pointer to the memory strategy to set.
* \throws std::runtime_error if memory strategy is null
*/
RCLCPP_PUBLIC
void
init(
rcl_wait_set_t * p_wait_set,
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy);
/// Finalize StaticExecutorEntitiesCollector to clear resources
RCLCPP_PUBLIC
bool
is_init() {return initialized_;}
RCLCPP_PUBLIC
void
fini();
/// Execute the waitable.
RCLCPP_PUBLIC
void
execute(std::shared_ptr<void> & data) override;
/// Take the data so that it can be consumed with `execute`.
/**
* For `StaticExecutorEntitiesCollector`, this always return `nullptr`.
* \sa rclcpp::Waitable::take_data()
*/
RCLCPP_PUBLIC
std::shared_ptr<void>
take_data() override;
/// Function to add_handles_to_wait_set and wait for work and
/**
* block until the wait set is ready or until the timeout has been exceeded.
* \throws std::runtime_error if wait set couldn't be cleared or filled.
* \throws any rcl errors from rcl_wait, \see rclcpp::exceptions::throw_from_rcl_error()
*/
RCLCPP_PUBLIC
void
refresh_wait_set(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
/**
* \throws std::runtime_error if it couldn't add guard condition to wait set
*/
RCLCPP_PUBLIC
void
add_to_wait_set(rcl_wait_set_t * wait_set) override;
RCLCPP_PUBLIC
size_t
get_number_of_ready_guard_conditions() override;
/// Add a callback group to an executor.
/**
* \see rclcpp::Executor::add_callback_group
*/
RCLCPP_PUBLIC
bool
add_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
/// Add a callback group to an executor.
/**
* \see rclcpp::Executor::add_callback_group
* \return boolean whether the node from the callback group is new
*/
RCLCPP_PUBLIC
bool
add_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
/// Remove a callback group from the executor.
/**
* \see rclcpp::Executor::remove_callback_group
*/
RCLCPP_PUBLIC
bool
remove_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr);
/// Remove a callback group from the executor.
/**
* \see rclcpp::Executor::remove_callback_group_from_map
*/
RCLCPP_PUBLIC
bool
remove_callback_group_from_map(
rclcpp::CallbackGroup::SharedPtr group_ptr,
WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
/**
* \see rclcpp::Executor::add_node()
* \throw std::runtime_error if node was already added
*/
RCLCPP_PUBLIC
bool
add_node(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
/**
* \see rclcpp::Executor::remove_node()
* \throw std::runtime_error if no guard condition is associated with node.
*/
RCLCPP_PUBLIC
bool
remove_node(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_all_callback_groups();
/// Get callback groups that belong to executor.
/**
* \see rclcpp::Executor::get_manually_added_callback_groups()
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_manually_added_callback_groups();
/// Get callback groups that belong to executor.
/**
* \see rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_automatically_added_callback_groups_from_nodes();
/// Complete all available queued work without blocking.
/**
* This function checks if after the guard condition was triggered
* (or a spurious wakeup happened) we are really ready to execute
* i.e. re-collect entities
*/
RCLCPP_PUBLIC
bool
is_ready(rcl_wait_set_t * wait_set) override;
/// Return number of timers
/**
* \return number of timers
*/
RCLCPP_PUBLIC
size_t
get_number_of_timers() {return exec_list_.number_of_timers;}
/// Return number of subscriptions
/**
* \return number of subscriptions
*/
RCLCPP_PUBLIC
size_t
get_number_of_subscriptions() {return exec_list_.number_of_subscriptions;}
/// Return number of services
/**
* \return number of services
*/
RCLCPP_PUBLIC
size_t
get_number_of_services() {return exec_list_.number_of_services;}
/// Return number of clients
/**
* \return number of clients
*/
RCLCPP_PUBLIC
size_t
get_number_of_clients() {return exec_list_.number_of_clients;}
/// Return number of waitables
/**
* \return number of waitables
*/
RCLCPP_PUBLIC
size_t
get_number_of_waitables() {return exec_list_.number_of_waitables;}
/** Return a SubscritionBase Sharedptr by index.
* \param[in] i The index of the SubscritionBase
* \return a SubscritionBase shared pointer
* \throws std::out_of_range if the argument is higher than the size of the structrue.
*/
RCLCPP_PUBLIC
rclcpp::SubscriptionBase::SharedPtr
get_subscription(size_t i) {return exec_list_.subscription[i];}
/** Return a TimerBase Sharedptr by index.
* \param[in] i The index of the TimerBase
* \return a TimerBase shared pointer
* \throws std::out_of_range if the argument is higher than the size.
*/
RCLCPP_PUBLIC
rclcpp::TimerBase::SharedPtr
get_timer(size_t i) {return exec_list_.timer[i];}
/** Return a ServiceBase Sharedptr by index.
* \param[in] i The index of the ServiceBase
* \return a ServiceBase shared pointer
* \throws std::out_of_range if the argument is higher than the size.
*/
RCLCPP_PUBLIC
rclcpp::ServiceBase::SharedPtr
get_service(size_t i) {return exec_list_.service[i];}
/** Return a ClientBase Sharedptr by index
* \param[in] i The index of the ClientBase
* \return a ClientBase shared pointer
* \throws std::out_of_range if the argument is higher than the size.
*/
RCLCPP_PUBLIC
rclcpp::ClientBase::SharedPtr
get_client(size_t i) {return exec_list_.client[i];}
/** Return a Waitable Sharedptr by index
* \param[in] i The index of the Waitable
* \return a Waitable shared pointer
* \throws std::out_of_range if the argument is higher than the size.
*/
RCLCPP_PUBLIC
rclcpp::Waitable::SharedPtr
get_waitable(size_t i) {return exec_list_.waitable[i];}
private:
/// Function to reallocate space for entities in the wait set.
/**
* \throws std::runtime_error if wait set couldn't be cleared or resized.
*/
void
prepare_wait_set();
void
fill_executable_list();
void
fill_memory_strategy();
/// Return true if the node belongs to the collector
/**
* \param[in] group_ptr a node base interface shared pointer
* \return boolean whether a node belongs the collector
*/
bool
has_node(
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) const;
/// Add all callback groups that can be automatically added by any executor
/// and is not already associated with an executor from nodes
/// that are associated with executor
/**
* \see rclcpp::Executor::add_callback_groups_from_nodes_associated_to_executor()
*/
void
add_callback_groups_from_nodes_associated_to_executor();
void
fill_executable_list_from_map(const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
/// Memory strategy: an interface for handling user-defined memory allocation strategies.
rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;
// maps callback groups to nodes.
WeakCallbackGroupsToNodesMap weak_groups_associated_with_executor_to_nodes_;
// maps callback groups to nodes.
WeakCallbackGroupsToNodesMap weak_groups_to_nodes_associated_with_executor_;
typedef std::map<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr,
const rclcpp::GuardCondition *,
std::owner_less<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>>
WeakNodesToGuardConditionsMap;
WeakNodesToGuardConditionsMap weak_nodes_to_guard_conditions_;
/// List of weak nodes registered in the static executor
std::list<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
// Mutex to protect vector of new nodes.
std::mutex new_nodes_mutex_;
std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> new_nodes_;
/// Wait set for managing entities that the rmw layer waits on.
rcl_wait_set_t * p_wait_set_ = nullptr;
/// Executable list: timers, subscribers, clients, services and waitables
rclcpp::experimental::ExecutableList exec_list_;
/// Bool to check if the entities collector has been initialized
bool initialized_ = false;
};
} // namespace executors
} // namespace rclcpp
#endif // RCLCPP__EXECUTORS__STATIC_EXECUTOR_ENTITIES_COLLECTOR_HPP_

View File

@@ -15,24 +15,13 @@
#ifndef RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
#define RCLCPP__EXECUTORS__STATIC_SINGLE_THREADED_EXECUTOR_HPP_
#include <atomic>
#include <chrono>
#include <cassert>
#include <cstdlib>
#include <memory>
#include <vector>
#include <string>
#include "rmw/rmw.h"
#include "rclcpp/executor.hpp"
#include "rclcpp/executors/static_executor_entities_collector.hpp"
#include "rclcpp/experimental/executable_list.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/memory_strategies.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rate.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/executors/executor_entities_collection.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
namespace rclcpp
{
@@ -65,7 +54,7 @@ public:
explicit StaticSingleThreadedExecutor(
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
/// Default destrcutor.
/// Default destructor.
RCLCPP_PUBLIC
virtual ~StaticSingleThreadedExecutor();
@@ -99,9 +88,10 @@ public:
/// Static executor implementation of spin all
/**
* This non-blocking function will execute entities until
* timeout or no more work available. If new entities get ready
* while executing work available, they will be executed
* This non-blocking function will execute entities until timeout (must be >= 0)
* or no more work available.
* If timeout is `0`, potentially it blocks forever until no more work is available.
* If new entities get ready while executing work available, they will be executed
* as long as the timeout hasn't expired.
*
* Example:
@@ -115,92 +105,20 @@ public:
void
spin_all(std::chrono::nanoseconds max_duration) override;
/// Add a callback group to an executor.
/**
* \sa rclcpp::Executor::add_callback_group
*/
RCLCPP_PUBLIC
void
add_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
bool notify = true) override;
/// Remove callback group from the executor
/**
* \sa rclcpp::Executor::remove_callback_group
*/
RCLCPP_PUBLIC
void
remove_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
bool notify = true) override;
/// Add a node to the executor.
/**
* \sa rclcpp::Executor::add_node
*/
RCLCPP_PUBLIC
void
add_node(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
bool notify = true) override;
/// Convenience function which takes Node and forwards NodeBaseInterface.
/**
* \sa rclcpp::StaticSingleThreadedExecutor::add_node
*/
RCLCPP_PUBLIC
void
add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
/// Remove a node from the executor.
/**
* \sa rclcpp::Executor::remove_node
*/
RCLCPP_PUBLIC
void
remove_node(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
bool notify = true) override;
/// Convenience function which takes Node and forwards NodeBaseInterface.
/**
* \sa rclcpp::Executor::remove_node
*/
RCLCPP_PUBLIC
void
remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_all_callback_groups() override;
/// Get callback groups that belong to executor.
/**
* \sa rclcpp::Executor::get_manually_added_callback_groups()
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_manually_added_callback_groups() override;
/// Get callback groups that belong to executor.
/**
* \sa rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_automatically_added_callback_groups_from_nodes() override;
protected:
/**
* @brief Executes ready executables from wait set.
* @param collection entities to evaluate for ready executables.
* @param wait_result result to check for ready executables.
* @param spin_once if true executes only the first ready executable.
* @return true if any executable was ready.
*/
RCLCPP_PUBLIC
bool
execute_ready_executables(bool spin_once = false);
execute_ready_executables(
const rclcpp::executors::ExecutorEntitiesCollection & collection,
rclcpp::WaitResult<rclcpp::WaitSet> & wait_result,
bool spin_once);
RCLCPP_PUBLIC
void
@@ -212,8 +130,6 @@ protected:
private:
RCLCPP_DISABLE_COPY(StaticSingleThreadedExecutor)
StaticExecutorEntitiesCollector::SharedPtr entities_collector_;
};
} // namespace executors

View File

@@ -16,6 +16,7 @@
#define RCLCPP__EXPERIMENTAL__BUFFERS__INTRA_PROCESS_BUFFER_HPP_
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <utility>
@@ -23,6 +24,7 @@
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/experimental/buffers/buffer_implementation_base.hpp"
#include "rclcpp/macros.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
@@ -93,6 +95,10 @@ public:
buffer_ = std::move(buffer_impl);
TRACEPOINT(
rclcpp_buffer_to_ipb,
static_cast<const void *>(buffer_.get()),
static_cast<const void *>(this));
if (!allocator) {
message_allocator_ = std::make_shared<MessageAlloc>();
} else {

View File

@@ -15,10 +15,6 @@
#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>
@@ -29,6 +25,7 @@
#include "rclcpp/logging.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
@@ -55,6 +52,7 @@ public:
if (capacity == 0) {
throw std::invalid_argument("capacity must be a positive, non-zero value");
}
TRACEPOINT(rclcpp_construct_ring_buffer, static_cast<const void *>(this), capacity_);
}
virtual ~RingBufferImplementation() {}
@@ -71,6 +69,12 @@ public:
write_index_ = next_(write_index_);
ring_buffer_[write_index_] = std::move(request);
TRACEPOINT(
rclcpp_ring_buffer_enqueue,
static_cast<const void *>(this),
write_index_,
size_ + 1,
is_full_());
if (is_full_()) {
read_index_ = next_(read_index_);
@@ -90,11 +94,15 @@ public:
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");
return BufferT();
}
auto request = std::move(ring_buffer_[read_index_]);
TRACEPOINT(
rclcpp_ring_buffer_dequeue,
static_cast<const void *>(this),
read_index_,
size_ - 1);
read_index_ = next_(read_index_);
size_--;
@@ -140,7 +148,10 @@ public:
return is_full_();
}
void clear() {}
void clear()
{
TRACEPOINT(rclcpp_ring_buffer_clear, static_cast<const void *>(this));
}
private:
/// Get the next index value for the ring buffer

View File

@@ -16,11 +16,9 @@
#define RCLCPP__EXPERIMENTAL__CREATE_INTRA_PROCESS_BUFFER_HPP_
#include <memory>
#include <type_traits>
#include <stdexcept>
#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"

View File

@@ -15,7 +15,6 @@
#ifndef RCLCPP__EXPERIMENTAL__EXECUTABLE_LIST_HPP_
#define RCLCPP__EXPERIMENTAL__EXECUTABLE_LIST_HPP_
#include <memory>
#include <vector>
#include "rclcpp/client.hpp"

View File

@@ -0,0 +1,286 @@
// Copyright 2023 iRobot Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_
#include <atomic>
#include <chrono>
#include <memory>
#include <vector>
#include "rclcpp/executor.hpp"
#include "rclcpp/executors/executor_entities_collection.hpp"
#include "rclcpp/executors/executor_entities_collector.hpp"
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
#include "rclcpp/experimental/executors/events_executor/events_queue.hpp"
#include "rclcpp/experimental/executors/events_executor/simple_events_queue.hpp"
#include "rclcpp/experimental/timers_manager.hpp"
#include "rclcpp/node.hpp"
namespace rclcpp
{
namespace experimental
{
namespace executors
{
/// Events executor implementation
/**
* This executor uses an events queue and a timers manager to execute entities from its
* associated nodes and callback groups.
* ROS 2 entities allow to set callback functions that are invoked when the entity is triggered
* or has work to do. The events-executor sets these callbacks such that they push an
* event into its queue.
*
* This executor tries to reduce as much as possible the amount of maintenance operations.
* This allows to use customized `EventsQueue` classes to achieve different goals such
* as very low CPU usage, bounded memory requirement, determinism, etc.
*
* The executor uses a weak ownership model and it locks entities only while executing
* their related events.
*
* To run this executor:
* rclcpp::experimental::executors::EventsExecutor executor;
* executor.add_node(node);
* executor.spin();
* executor.remove_node(node);
*/
class EventsExecutor : public rclcpp::Executor
{
friend class EventsExecutorEntitiesCollector;
public:
RCLCPP_SMART_PTR_DEFINITIONS(EventsExecutor)
/// Default constructor. See the default constructor for Executor.
/**
* \param[in] events_queue The queue used to store events.
* \param[in] execute_timers_separate_thread If true, timers are executed in a separate
* thread. If false, timers are executed in the same thread as all other entities.
* \param[in] options Options used to configure the executor.
*/
RCLCPP_PUBLIC
explicit EventsExecutor(
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue = std::make_unique<
rclcpp::experimental::executors::SimpleEventsQueue>(),
bool execute_timers_separate_thread = false,
const rclcpp::ExecutorOptions & options = rclcpp::ExecutorOptions());
/// Default destructor.
RCLCPP_PUBLIC
virtual ~EventsExecutor();
/// Events executor implementation of spin.
/**
* This function will block until work comes in, execute it, and keep blocking.
* It will only be interrupted by a CTRL-C (managed by the global signal handler).
* \throws std::runtime_error when spin() called while already spinning
*/
RCLCPP_PUBLIC
void
spin() override;
/// Events executor implementation of spin some
/**
* This non-blocking function will execute the timers and events
* that were ready when this API was called, until timeout or no
* more work available. New ready-timers/events arrived while
* executing work, won't be taken into account here.
*
* Example:
* while(condition) {
* spin_some();
* sleep(); // User should have some sync work or
* // sleep to avoid a 100% CPU usage
* }
*/
RCLCPP_PUBLIC
void
spin_some(std::chrono::nanoseconds max_duration = std::chrono::nanoseconds(0)) override;
/// Events executor implementation of spin all
/**
* This non-blocking function will execute timers and events
* until timeout or no more work available. If new ready-timers/events
* arrive while executing work available, they will be executed
* as long as the timeout hasn't expired.
*
* Example:
* while(condition) {
* spin_all();
* sleep(); // User should have some sync work or
* // sleep to avoid a 100% CPU usage
* }
*/
RCLCPP_PUBLIC
void
spin_all(std::chrono::nanoseconds max_duration) override;
/// Add a node to the executor.
/**
* \sa rclcpp::Executor::add_node
*/
RCLCPP_PUBLIC
void
add_node(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
bool notify = true) override;
/// Convenience function which takes Node and forwards NodeBaseInterface.
/**
* \sa rclcpp::EventsExecutor::add_node
*/
RCLCPP_PUBLIC
void
add_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
/// Remove a node from the executor.
/**
* \sa rclcpp::Executor::remove_node
*/
RCLCPP_PUBLIC
void
remove_node(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
bool notify = true) override;
/// Convenience function which takes Node and forwards NodeBaseInterface.
/**
* \sa rclcpp::Executor::remove_node
*/
RCLCPP_PUBLIC
void
remove_node(std::shared_ptr<rclcpp::Node> node_ptr, bool notify = true) override;
/// Add a callback group to an executor.
/**
* \sa rclcpp::Executor::add_callback_group
*/
RCLCPP_PUBLIC
void
add_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
bool notify = true) override;
/// Remove callback group from the executor
/**
* \sa rclcpp::Executor::remove_callback_group
*/
RCLCPP_PUBLIC
void
remove_callback_group(
rclcpp::CallbackGroup::SharedPtr group_ptr,
bool notify = true) override;
/// Get callback groups that belong to executor.
/**
* \sa rclcpp::Executor::get_all_callback_groups()
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_all_callback_groups() override;
/// Get callback groups that belong to executor.
/**
* \sa rclcpp::Executor::get_manually_added_callback_groups()
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_manually_added_callback_groups() override;
/// Get callback groups that belong to executor.
/**
* \sa rclcpp::Executor::get_automatically_added_callback_groups_from_nodes()
*/
RCLCPP_PUBLIC
std::vector<rclcpp::CallbackGroup::WeakPtr>
get_automatically_added_callback_groups_from_nodes() override;
protected:
/// Internal implementation of spin_once
RCLCPP_PUBLIC
void
spin_once_impl(std::chrono::nanoseconds timeout) override;
/// Internal implementation of spin_some
RCLCPP_PUBLIC
void
spin_some_impl(std::chrono::nanoseconds max_duration, bool exhaustive);
private:
RCLCPP_DISABLE_COPY(EventsExecutor)
/// Execute a provided executor event if its associated entities are available
void
execute_event(const ExecutorEvent & event);
/// Collect entities from callback groups and refresh the current collection with them
void
refresh_current_collection_from_callback_groups();
/// Refresh the current collection using the provided new_collection
void
refresh_current_collection(const rclcpp::executors::ExecutorEntitiesCollection & new_collection);
/// Create a listener callback function for the provided entity
std::function<void(size_t)>
create_entity_callback(void * entity_key, ExecutorEventType type);
/// Create a listener callback function for the provided waitable entity
std::function<void(size_t, int)>
create_waitable_callback(const rclcpp::Waitable * waitable_id);
/// Searches for the provided entity_id in the collection and returns the entity if valid
template<typename CollectionType>
typename CollectionType::EntitySharedPtr
retrieve_entity(typename CollectionType::Key entity_id, CollectionType & collection)
{
// Check if the entity_id is in the collection
auto it = collection.find(entity_id);
if (it == collection.end()) {
return nullptr;
}
// Check if the entity associated with the entity_id is valid
// and remove it from the collection if it isn't
auto entity = it->second.entity.lock();
if (!entity) {
collection.erase(it);
}
// Return the retrieved entity (this can be a nullptr if the entity was not valid)
return entity;
}
/// Queue where entities can push events
rclcpp::experimental::executors::EventsQueue::UniquePtr events_queue_;
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollector> entities_collector_;
std::shared_ptr<rclcpp::executors::ExecutorEntitiesCollection> current_entities_collection_;
std::shared_ptr<rclcpp::executors::ExecutorNotifyWaitable> notify_waitable_;
/// Flag used to reduce the number of unnecessary waitable events
std::atomic<bool> notify_waitable_event_pushed_ {false};
/// Timers manager used to track and/or execute associated timers
std::shared_ptr<rclcpp::experimental::TimersManager> timers_manager_;
};
} // namespace executors
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_HPP_

View File

@@ -0,0 +1,46 @@
// Copyright 2023 iRobot Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_
namespace rclcpp
{
namespace experimental
{
namespace executors
{
enum ExecutorEventType
{
CLIENT_EVENT,
SUBSCRIPTION_EVENT,
SERVICE_EVENT,
TIMER_EVENT,
WAITABLE_EVENT
};
struct ExecutorEvent
{
const void * entity_key;
int waitable_data;
ExecutorEventType type;
size_t num_events;
};
} // namespace executors
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_EXECUTOR_EVENT_TYPES_HPP_

View File

@@ -0,0 +1,100 @@
// Copyright 2023 iRobot Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_QUEUE_HPP_
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_QUEUE_HPP_
#include <queue>
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/experimental/executors/events_executor/events_executor_event_types.hpp"
namespace rclcpp
{
namespace experimental
{
namespace executors
{
/**
* @brief This abstract class can be used to implement different types of queues
* where `ExecutorEvent` can be stored.
* The derived classes should choose which underlying container to use and
* the strategy for pushing and popping events.
* For example a queue implementation may be bounded or unbounded and have
* different pruning strategies.
* Implementations may or may not check the validity of events and decide how to handle
* the situation where an event is not valid anymore (e.g. a subscription history cache overruns)
*/
class EventsQueue
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(EventsQueue)
RCLCPP_PUBLIC
EventsQueue() = default;
/**
* @brief Destruct the object.
*/
RCLCPP_PUBLIC
virtual ~EventsQueue() = default;
/**
* @brief push event into the queue
* @param event The event to push into the queue
*/
RCLCPP_PUBLIC
virtual
void
enqueue(const rclcpp::experimental::executors::ExecutorEvent & event) = 0;
/**
* @brief Extracts an event from the queue, eventually waiting until timeout
* if none is available.
* @return true if event has been found, false if timeout
*/
RCLCPP_PUBLIC
virtual
bool
dequeue(
rclcpp::experimental::executors::ExecutorEvent & event,
std::chrono::nanoseconds timeout = std::chrono::nanoseconds::max()) = 0;
/**
* @brief Test whether queue is empty
* @return true if the queue's size is 0, false otherwise.
*/
RCLCPP_PUBLIC
virtual
bool
empty() const = 0;
/**
* @brief Returns the number of elements in the queue.
* @return the number of elements in the queue.
*/
RCLCPP_PUBLIC
virtual
size_t
size() const = 0;
};
} // namespace executors
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__EVENTS_QUEUE_HPP_

View File

@@ -0,0 +1,134 @@
// Copyright 2023 iRobot Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__SIMPLE_EVENTS_QUEUE_HPP_
#define RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__SIMPLE_EVENTS_QUEUE_HPP_
#include <condition_variable>
#include <mutex>
#include <queue>
#include <utility>
#include "rclcpp/experimental/executors/events_executor/events_queue.hpp"
namespace rclcpp
{
namespace experimental
{
namespace executors
{
/**
* @brief This class implements an EventsQueue as a simple wrapper around a std::queue.
* It does not perform any checks about the size of queue, which can grow
* unbounded without being pruned.
* The simplicity of this implementation makes it suitable for optimizing CPU usage.
*/
class SimpleEventsQueue : public EventsQueue
{
public:
RCLCPP_PUBLIC
~SimpleEventsQueue() override = default;
/**
* @brief enqueue event into the queue
* Thread safe
* @param event The event to enqueue into the queue
*/
RCLCPP_PUBLIC
void
enqueue(const rclcpp::experimental::executors::ExecutorEvent & event) override
{
rclcpp::experimental::executors::ExecutorEvent single_event = event;
single_event.num_events = 1;
{
std::unique_lock<std::mutex> lock(mutex_);
for (size_t ev = 0; ev < event.num_events; ev++) {
event_queue_.push(single_event);
}
}
events_queue_cv_.notify_one();
}
/**
* @brief waits for an event until timeout, gets a single event
* Thread safe
* @return true if event, false if timeout
*/
RCLCPP_PUBLIC
bool
dequeue(
rclcpp::experimental::executors::ExecutorEvent & event,
std::chrono::nanoseconds timeout = std::chrono::nanoseconds::max()) override
{
std::unique_lock<std::mutex> lock(mutex_);
// Initialize to true because it's only needed if we have a valid timeout
bool has_data = true;
if (timeout != std::chrono::nanoseconds::max()) {
has_data =
events_queue_cv_.wait_for(lock, timeout, [this]() {return !event_queue_.empty();});
} else {
events_queue_cv_.wait(lock, [this]() {return !event_queue_.empty();});
}
if (has_data) {
event = event_queue_.front();
event_queue_.pop();
return true;
}
return false;
}
/**
* @brief Test whether queue is empty
* Thread safe
* @return true if the queue's size is 0, false otherwise.
*/
RCLCPP_PUBLIC
bool
empty() const override
{
std::unique_lock<std::mutex> lock(mutex_);
return event_queue_.empty();
}
/**
* @brief Returns the number of elements in the queue.
* Thread safe
* @return the number of elements in the queue.
*/
RCLCPP_PUBLIC
size_t
size() const override
{
std::unique_lock<std::mutex> lock(mutex_);
return event_queue_.size();
}
private:
// The underlying queue implementation
std::queue<rclcpp::experimental::executors::ExecutorEvent> event_queue_;
// Mutex to protect read/write access to the queue
mutable std::mutex mutex_;
// Variable used to notify when an event is added to the queue
std::condition_variable events_queue_cv_;
};
} // namespace executors
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__EXECUTORS__EVENTS_EXECUTOR__SIMPLE_EVENTS_QUEUE_HPP_

View File

@@ -19,18 +19,16 @@
#include <shared_mutex>
#include <algorithm>
#include <atomic>
#include <cstdint>
#include <exception>
#include <map>
#include <iterator>
#include <memory>
#include <string>
#include <stdexcept>
#include <unordered_map>
#include <utility>
#include <vector>
#include <typeinfo>
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/experimental/ros_message_intra_process_buffer.hpp"
#include "rclcpp/experimental/subscription_intra_process.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/experimental/subscription_intra_process_buffer.hpp"
@@ -38,6 +36,7 @@
#include "rclcpp/logging.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/type_adapter.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -173,11 +172,14 @@ public:
*
* \param intra_process_publisher_id the id of the publisher of this message.
* \param message the message that is being stored.
* \param allocator for allocations when buffering messages.
*/
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
typename ROSMessageType,
typename Alloc,
typename Deleter = std::default_delete<MessageT>
>
void
do_intra_process_publish(
uint64_t intra_process_publisher_id,
@@ -203,7 +205,7 @@ public:
// 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, Alloc, Deleter>(
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
msg, sub_ids.take_shared_subscriptions);
} else if (!sub_ids.take_ownership_subscriptions.empty() && // NOLINT
sub_ids.take_shared_subscriptions.size() <= 1)
@@ -217,8 +219,7 @@ public:
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>(
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
std::move(message),
concatenated_vector,
allocator);
@@ -229,17 +230,19 @@ public:
// 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, Alloc, Deleter>(
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
shared_msg, sub_ids.take_shared_subscriptions);
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter>(
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
std::move(message), sub_ids.take_ownership_subscriptions, allocator);
}
}
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
typename ROSMessageType,
typename Alloc,
typename Deleter = std::default_delete<MessageT>
>
std::shared_ptr<const MessageT>
do_intra_process_publish_and_return_shared(
uint64_t intra_process_publisher_id,
@@ -265,7 +268,7 @@ public:
// 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, Alloc, Deleter>(
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
shared_msg, sub_ids.take_shared_subscriptions);
}
return shared_msg;
@@ -275,17 +278,16 @@ public:
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, Alloc, Deleter>(
this->template add_shared_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
shared_msg,
sub_ids.take_shared_subscriptions);
}
if (!sub_ids.take_ownership_subscriptions.empty()) {
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter>(
this->template add_owned_msg_to_buffers<MessageT, Alloc, Deleter, ROSMessageType>(
std::move(message),
sub_ids.take_ownership_subscriptions,
allocator);
}
return shared_msg;
}
}
@@ -338,41 +340,83 @@ private:
template<
typename MessageT,
typename Alloc,
typename Deleter>
typename Deleter,
typename ROSMessageType>
void
add_shared_msg_to_buffers(
std::shared_ptr<const MessageT> message,
std::vector<uint64_t> subscription_ids)
{
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
using PublishedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, Alloc>;
using PublishedTypeAllocator = typename PublishedTypeAllocatorTraits::allocator_type;
using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
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.lock();
if (subscription_base) {
auto subscription = std::dynamic_pointer_cast<
rclcpp::experimental::SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>
>(subscription_base);
if (nullptr == subscription) {
throw std::runtime_error(
"failed to dynamic cast SubscriptionIntraProcessBase to "
"SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, which "
"can happen when the publisher and subscription use different "
"allocator types, which is not supported");
}
subscription->provide_intra_process_message(message);
} else {
if (subscription_base == nullptr) {
subscriptions_.erase(id);
continue;
}
auto subscription = std::dynamic_pointer_cast<
rclcpp::experimental::SubscriptionIntraProcessBuffer<PublishedType,
PublishedTypeAllocator, PublishedTypeDeleter, ROSMessageType>
>(subscription_base);
if (subscription != nullptr) {
subscription->provide_intra_process_data(message);
continue;
}
auto ros_message_subscription = std::dynamic_pointer_cast<
rclcpp::experimental::SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,
ROSMessageTypeAllocator, ROSMessageTypeDeleter>
>(subscription_base);
if (nullptr == ros_message_subscription) {
throw std::runtime_error(
"failed to dynamic cast SubscriptionIntraProcessBase to "
"SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, or to "
"SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,ROSMessageTypeAllocator,"
"ROSMessageTypeDeleter> which can happen when the publisher and "
"subscription use different allocator types, which is not supported");
}
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
ROSMessageType ros_msg;
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*message, ros_msg);
ros_message_subscription->provide_intra_process_message(
std::make_shared<ROSMessageType>(ros_msg));
} else {
if constexpr (std::is_same<MessageT, ROSMessageType>::value) {
ros_message_subscription->provide_intra_process_message(message);
} else {
if constexpr (std::is_same<typename rclcpp::TypeAdapter<MessageT,
ROSMessageType>::ros_message_type, ROSMessageType>::value)
{
ROSMessageType ros_msg;
rclcpp::TypeAdapter<MessageT, ROSMessageType>::convert_to_ros_message(
*message, ros_msg);
ros_message_subscription->provide_intra_process_message(
std::make_shared<ROSMessageType>(ros_msg));
}
}
}
}
}
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>>
typename Alloc,
typename Deleter,
typename ROSMessageType>
void
add_owned_msg_to_buffers(
std::unique_ptr<MessageT, Deleter> message,
@@ -382,39 +426,88 @@ private:
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageUniquePtr = std::unique_ptr<MessageT, Deleter>;
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
using PublishedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, Alloc>;
using PublishedTypeAllocator = typename PublishedTypeAllocatorTraits::allocator_type;
using PublishedTypeDeleter = allocator::Deleter<PublishedTypeAllocator, PublishedType>;
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.lock();
if (subscription_base) {
auto subscription = std::dynamic_pointer_cast<
rclcpp::experimental::SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>
>(subscription_base);
if (nullptr == subscription) {
throw std::runtime_error(
"failed to dynamic cast SubscriptionIntraProcessBase to "
"SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, which "
"can happen when the publisher and subscription use different "
"allocator types, which is not supported");
}
if (subscription_base == nullptr) {
subscriptions_.erase(subscription_it);
continue;
}
auto subscription = std::dynamic_pointer_cast<
rclcpp::experimental::SubscriptionIntraProcessBuffer<PublishedType,
PublishedTypeAllocator, PublishedTypeDeleter, ROSMessageType>
>(subscription_base);
if (subscription != nullptr) {
if (std::next(it) == subscription_ids.end()) {
// If this is the last subscription, give up ownership
subscription->provide_intra_process_message(std::move(message));
subscription->provide_intra_process_data(std::move(message));
// Last message delivered, break from for loop
break;
} else {
// Copy the message since we have additional subscriptions to serve
MessageUniquePtr copy_message;
Deleter deleter = message.get_deleter();
auto ptr = MessageAllocTraits::allocate(allocator, 1);
MessageAllocTraits::construct(allocator, ptr, *message);
copy_message = MessageUniquePtr(ptr, deleter);
subscription->provide_intra_process_message(std::move(copy_message));
subscription->provide_intra_process_data(std::move(MessageUniquePtr(ptr, deleter)));
}
continue;
}
auto ros_message_subscription = std::dynamic_pointer_cast<
rclcpp::experimental::SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,
ROSMessageTypeAllocator, ROSMessageTypeDeleter>
>(subscription_base);
if (nullptr == ros_message_subscription) {
throw std::runtime_error(
"failed to dynamic cast SubscriptionIntraProcessBase to "
"SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>, or to "
"SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,ROSMessageTypeAllocator,"
"ROSMessageTypeDeleter> which can happen when the publisher and "
"subscription use different allocator types, which is not supported");
}
if constexpr (rclcpp::TypeAdapter<MessageT>::is_specialized::value) {
ROSMessageTypeAllocator ros_message_alloc(allocator);
auto ptr = ros_message_alloc.allocate(1);
ros_message_alloc.construct(ptr);
ROSMessageTypeDeleter deleter;
allocator::set_allocator_for_deleter(&deleter, &allocator);
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*message, *ptr);
auto ros_msg = std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, deleter);
ros_message_subscription->provide_intra_process_message(std::move(ros_msg));
} else {
subscriptions_.erase(subscription_it);
if constexpr (std::is_same<MessageT, ROSMessageType>::value) {
if (std::next(it) == subscription_ids.end()) {
// If this is the last subscription, give up ownership
ros_message_subscription->provide_intra_process_message(std::move(message));
// Last message delivered, break from for loop
break;
} else {
// Copy the message since we have additional subscriptions to serve
Deleter deleter = message.get_deleter();
allocator::set_allocator_for_deleter(&deleter, &allocator);
auto ptr = MessageAllocTraits::allocate(allocator, 1);
MessageAllocTraits::construct(allocator, ptr, *message);
ros_message_subscription->provide_intra_process_message(
std::move(MessageUniquePtr(ptr, deleter)));
}
}
}
}
}

View File

@@ -0,0 +1,68 @@
// Copyright 2021 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__ROS_MESSAGE_INTRA_PROCESS_BUFFER_HPP_
#define RCLCPP__EXPERIMENTAL__ROS_MESSAGE_INTRA_PROCESS_BUFFER_HPP_
#include <memory>
#include <string>
#include "rcl/error_handling.h"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
{
namespace experimental
{
template<
typename RosMessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<void>
>
class SubscriptionROSMsgIntraProcessBuffer : public SubscriptionIntraProcessBase
{
public:
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<RosMessageT, Alloc>;
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, RosMessageT>;
using ConstMessageSharedPtr = std::shared_ptr<const RosMessageT>;
using MessageUniquePtr = std::unique_ptr<RosMessageT, ROSMessageTypeDeleter>;
SubscriptionROSMsgIntraProcessBuffer(
rclcpp::Context::SharedPtr context,
const std::string & topic_name,
const rclcpp::QoS & qos_profile)
: SubscriptionIntraProcessBase(context, topic_name, qos_profile)
{}
virtual ~SubscriptionROSMsgIntraProcessBuffer()
{}
virtual void
provide_intra_process_message(ConstMessageSharedPtr message) = 0;
virtual void
provide_intra_process_message(MessageUniquePtr message) = 0;
};
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__ROS_MESSAGE_INTRA_PROCESS_BUFFER_HPP_

View File

@@ -15,25 +15,22 @@
#ifndef RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_HPP_
#include <rmw/rmw.h>
#include <rmw/types.h>
#include <functional>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <type_traits>
#include <utility>
#include "rcl/error_handling.h"
#include "rcl/types.h"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/context.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/experimental/subscription_intra_process_buffer.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/waitable.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
@@ -43,40 +40,47 @@ namespace experimental
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>,
typename CallbackMessageT = MessageT>
typename SubscribedType,
typename SubscribedTypeAlloc = std::allocator<SubscribedType>,
typename SubscribedTypeDeleter = std::default_delete<SubscribedType>,
typename ROSMessageType = SubscribedType,
typename Alloc = std::allocator<void>
>
class SubscriptionIntraProcess
: public SubscriptionIntraProcessBuffer<
MessageT,
Alloc,
Deleter
SubscribedType,
SubscribedTypeAlloc,
SubscribedTypeDeleter,
ROSMessageType
>
{
using SubscriptionIntraProcessBufferT = SubscriptionIntraProcessBuffer<
MessageT,
Alloc,
Deleter
SubscribedType,
SubscribedTypeAlloc,
SubscribedTypeDeleter,
ROSMessageType
>;
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcess)
using MessageAllocTraits = typename SubscriptionIntraProcessBufferT::MessageAllocTraits;
using MessageAlloc = typename SubscriptionIntraProcessBufferT::MessageAlloc;
using ConstMessageSharedPtr = typename SubscriptionIntraProcessBufferT::ConstMessageSharedPtr;
using MessageUniquePtr = typename SubscriptionIntraProcessBufferT::MessageUniquePtr;
using MessageAllocTraits =
typename SubscriptionIntraProcessBufferT::SubscribedTypeAllocatorTraits;
using MessageAlloc = typename SubscriptionIntraProcessBufferT::SubscribedTypeAllocator;
using ConstMessageSharedPtr = typename SubscriptionIntraProcessBufferT::ConstDataSharedPtr;
using MessageUniquePtr = typename SubscriptionIntraProcessBufferT::SubscribedTypeUniquePtr;
using BufferUniquePtr = typename SubscriptionIntraProcessBufferT::BufferUniquePtr;
SubscriptionIntraProcess(
AnySubscriptionCallback<CallbackMessageT, Alloc> callback,
AnySubscriptionCallback<MessageT, Alloc> callback,
std::shared_ptr<Alloc> allocator,
rclcpp::Context::SharedPtr context,
const std::string & topic_name,
const rclcpp::QoS & qos_profile,
rclcpp::IntraProcessBufferType buffer_type)
: SubscriptionIntraProcessBuffer<MessageT, Alloc, Deleter>(
allocator,
: SubscriptionIntraProcessBuffer<SubscribedType, SubscribedTypeAlloc,
SubscribedTypeDeleter, ROSMessageType>(
std::make_shared<SubscribedTypeAlloc>(*allocator),
context,
topic_name,
qos_profile,
@@ -98,16 +102,29 @@ public:
virtual ~SubscriptionIntraProcess() = default;
std::shared_ptr<void>
take_data()
take_data() override
{
ConstMessageSharedPtr shared_msg;
MessageUniquePtr unique_msg;
if (any_callback_.use_take_shared_method()) {
shared_msg = this->buffer_->consume_shared();
if (!shared_msg) {
return nullptr;
}
} else {
unique_msg = this->buffer_->consume_unique();
if (!unique_msg) {
return nullptr;
}
}
if (this->buffer_->has_data()) {
// If there is data still to be processed, indicate to the
// executor or waitset by triggering the guard condition.
this->trigger_guard_condition();
}
return std::static_pointer_cast<void>(
std::make_shared<std::pair<ConstMessageSharedPtr, MessageUniquePtr>>(
std::pair<ConstMessageSharedPtr, MessageUniquePtr>(
@@ -115,9 +132,9 @@ public:
);
}
void execute(std::shared_ptr<void> & data)
void execute(std::shared_ptr<void> & data) override
{
execute_impl<MessageT>(data);
execute_impl<SubscribedType>(data);
}
protected:
@@ -134,7 +151,7 @@ protected:
execute_impl(std::shared_ptr<void> & data)
{
if (!data) {
throw std::runtime_error("'data' is empty");
return;
}
rmw_message_info_t msg_info;
@@ -154,7 +171,7 @@ protected:
shared_ptr.reset();
}
AnySubscriptionCallback<CallbackMessageT, Alloc> any_callback_;
AnySubscriptionCallback<MessageT, Alloc> any_callback_;
};
} // namespace experimental

View File

@@ -15,19 +15,17 @@
#ifndef RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BASE_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BASE_HPP_
#include <rmw/rmw.h>
#include <functional>
#include <algorithm>
#include <memory>
#include <mutex>
#include <string>
#include <utility>
#include "rcl/error_handling.h"
#include "rcl/wait.h"
#include "rmw/impl/cpp/demangle.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/logging.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/waitable.hpp"
namespace rclcpp
@@ -40,6 +38,11 @@ class SubscriptionIntraProcessBase : public rclcpp::Waitable
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(SubscriptionIntraProcessBase)
enum class EntityType : std::size_t
{
Subscription,
};
RCLCPP_PUBLIC
SubscriptionIntraProcessBase(
rclcpp::Context::SharedPtr context,
@@ -48,27 +51,35 @@ public:
: gc_(context), topic_name_(topic_name), qos_profile_(qos_profile)
{}
RCLCPP_PUBLIC
virtual ~SubscriptionIntraProcessBase() = default;
RCLCPP_PUBLIC
size_t
get_number_of_ready_guard_conditions() {return 1;}
get_number_of_ready_guard_conditions() override {return 1;}
RCLCPP_PUBLIC
void
add_to_wait_set(rcl_wait_set_t * wait_set);
add_to_wait_set(rcl_wait_set_t * wait_set) override;
virtual bool
is_ready(rcl_wait_set_t * wait_set) = 0;
bool
is_ready(rcl_wait_set_t * wait_set) override = 0;
std::shared_ptr<void>
take_data() override = 0;
std::shared_ptr<void>
take_data_by_entity_id(size_t id) override
{
(void)id;
return take_data();
}
void
execute(std::shared_ptr<void> & data) override = 0;
virtual
std::shared_ptr<void>
take_data() = 0;
virtual void
execute(std::shared_ptr<void> & data) = 0;
virtual bool
bool
use_take_shared_method() const = 0;
RCLCPP_PUBLIC
@@ -79,14 +90,108 @@ public:
QoS
get_actual_qos() const;
/// Set a callback to be called when each new message arrives.
/**
* The callback receives a size_t which is the number of messages received
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if messages were received before any
* callback was set.
*
* The callback also receives an int identifier argument.
* This is needed because a Waitable may be composed of several distinct entities,
* such as subscriptions, services, etc.
* The application should provide a generic callback function that will be then
* forwarded by the waitable to all of its entities.
* Before forwarding, a different value for the identifier argument will be
* bound to the function.
* This implies that the provided callback can use the identifier to behave
* differently depending on which entity triggered the waitable to become ready.
*
* Calling it again will clear any previously set callback.
*
* An exception will be thrown if the callback is not callable.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the subscription
* or other information, you may use a lambda with captures or std::bind.
*
* \param[in] callback functor to be called when a new message is received.
*/
void
set_on_ready_callback(std::function<void(size_t, int)> callback) override
{
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_ready_callback "
"is not callable.");
}
// Note: we bind the int identifier argument to this waitable's entity types
auto new_callback =
[callback, this](size_t number_of_events) {
try {
callback(number_of_events, static_cast<int>(EntityType::Subscription));
} catch (const std::exception & exception) {
RCLCPP_ERROR_STREAM(
// TODO(wjwwood): get this class access to the node logger it is associated with
rclcpp::get_logger("rclcpp"),
"rclcpp::SubscriptionIntraProcessBase@" << this <<
" caught " << rmw::impl::cpp::demangle(exception) <<
" exception in user-provided callback for the 'on ready' callback: " <<
exception.what());
} catch (...) {
RCLCPP_ERROR_STREAM(
rclcpp::get_logger("rclcpp"),
"rclcpp::SubscriptionIntraProcessBase@" << this <<
" caught unhandled exception in user-provided callback " <<
"for the 'on ready' callback");
}
};
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
on_new_message_callback_ = new_callback;
if (unread_count_ > 0) {
if (qos_profile_.history() == HistoryPolicy::KeepAll) {
on_new_message_callback_(unread_count_);
} else {
// Use qos profile depth as upper bound for unread_count_
on_new_message_callback_(std::min(unread_count_, qos_profile_.depth()));
}
unread_count_ = 0;
}
}
/// Unset the callback registered for new messages, if any.
void
clear_on_ready_callback() override
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
on_new_message_callback_ = nullptr;
}
protected:
std::recursive_mutex reentrant_mutex_;
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_message_callback_ {nullptr};
size_t unread_count_{0};
rclcpp::GuardCondition gc_;
private:
virtual void
trigger_guard_condition() = 0;
void
invoke_on_new_message()
{
std::lock_guard<std::recursive_mutex> lock(this->callback_mutex_);
if (this->on_new_message_callback_) {
this->on_new_message_callback_(1);
} else {
this->unread_count_++;
}
}
private:
std::string topic_name_;
QoS qos_profile_;
};

View File

@@ -15,24 +15,22 @@
#ifndef RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
#define RCLCPP__EXPERIMENTAL__SUBSCRIPTION_INTRA_PROCESS_BUFFER_HPP_
#include <rmw/rmw.h>
#include <functional>
#include <map>
#include <memory>
#include <stdexcept>
#include <string>
#include <stdexcept>
#include <utility>
#include "rcl/error_handling.h"
#include "rcl/guard_condition.h"
#include "rcl/wait.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/experimental/ros_message_intra_process_buffer.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/waitable.hpp"
#include "tracetools/tracetools.h"
namespace rclcpp
@@ -41,24 +39,39 @@ namespace experimental
{
template<
typename MessageT,
typename Alloc = std::allocator<void>,
typename Deleter = std::default_delete<MessageT>
typename SubscribedType,
typename Alloc = std::allocator<SubscribedType>,
typename Deleter = std::default_delete<SubscribedType>,
/// MessageT::ros_message_type if MessageT is a TypeAdapter,
/// otherwise just MessageT.
typename ROSMessageType = SubscribedType
>
class SubscriptionIntraProcessBuffer : public SubscriptionIntraProcessBase
class SubscriptionIntraProcessBuffer : public SubscriptionROSMsgIntraProcessBuffer<ROSMessageType,
typename allocator::AllocRebind<ROSMessageType, Alloc>::allocator_type,
allocator::Deleter<typename allocator::AllocRebind<ROSMessageType, Alloc>::allocator_type,
ROSMessageType>>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionIntraProcessBuffer)
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 SubscribedTypeAllocatorTraits = allocator::AllocRebind<SubscribedType, Alloc>;
using SubscribedTypeAllocator = typename SubscribedTypeAllocatorTraits::allocator_type;
using SubscribedTypeDeleter = allocator::Deleter<SubscribedTypeAllocator, SubscribedType>;
using ROSMessageTypeAllocatorTraits = allocator::AllocRebind<ROSMessageType, Alloc>;
using ROSMessageTypeAllocator = typename ROSMessageTypeAllocatorTraits::allocator_type;
using ROSMessageTypeDeleter = allocator::Deleter<ROSMessageTypeAllocator, ROSMessageType>;
using ConstMessageSharedPtr = std::shared_ptr<const ROSMessageType>;
using MessageUniquePtr = std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>;
using ConstDataSharedPtr = std::shared_ptr<const SubscribedType>;
using SubscribedTypeUniquePtr = std::unique_ptr<SubscribedType, SubscribedTypeDeleter>;
using BufferUniquePtr = typename rclcpp::experimental::buffers::IntraProcessBuffer<
MessageT,
SubscribedType,
Alloc,
Deleter
SubscribedTypeDeleter
>::UniquePtr;
SubscriptionIntraProcessBuffer(
@@ -67,50 +80,105 @@ public:
const std::string & topic_name,
const rclcpp::QoS & qos_profile,
rclcpp::IntraProcessBufferType buffer_type)
: SubscriptionIntraProcessBase(context, topic_name, qos_profile)
: SubscriptionROSMsgIntraProcessBuffer<ROSMessageType, ROSMessageTypeAllocator,
ROSMessageTypeDeleter>(
context, topic_name, qos_profile),
subscribed_type_allocator_(*allocator)
{
allocator::set_allocator_for_deleter(&subscribed_type_deleter_, &subscribed_type_allocator_);
// Create the intra-process buffer.
buffer_ = rclcpp::experimental::create_intra_process_buffer<MessageT, Alloc, Deleter>(
buffer_ = rclcpp::experimental::create_intra_process_buffer<SubscribedType, Alloc,
SubscribedTypeDeleter>(
buffer_type,
qos_profile,
allocator);
std::make_shared<Alloc>(subscribed_type_allocator_));
TRACEPOINT(
rclcpp_ipb_to_subscription,
static_cast<const void *>(buffer_.get()),
static_cast<const void *>(this));
}
bool
is_ready(rcl_wait_set_t * wait_set)
is_ready(rcl_wait_set_t * wait_set) override
{
(void) wait_set;
return buffer_->has_data();
}
SubscribedTypeUniquePtr
convert_ros_message_to_subscribed_type_unique_ptr(const ROSMessageType & msg)
{
if constexpr (!std::is_same<SubscribedType, ROSMessageType>::value) {
auto ptr = SubscribedTypeAllocatorTraits::allocate(subscribed_type_allocator_, 1);
SubscribedTypeAllocatorTraits::construct(subscribed_type_allocator_, ptr);
rclcpp::TypeAdapter<SubscribedType, ROSMessageType>::convert_to_custom(msg, *ptr);
return SubscribedTypeUniquePtr(ptr, subscribed_type_deleter_);
} else {
throw std::runtime_error(
"convert_ros_message_to_subscribed_type_unique_ptr "
"unexpectedly called without TypeAdapter");
}
}
void
provide_intra_process_message(ConstMessageSharedPtr message)
provide_intra_process_message(ConstMessageSharedPtr message) override
{
if constexpr (std::is_same<SubscribedType, ROSMessageType>::value) {
buffer_->add_shared(std::move(message));
trigger_guard_condition();
} else {
buffer_->add_shared(convert_ros_message_to_subscribed_type_unique_ptr(*message));
trigger_guard_condition();
}
this->invoke_on_new_message();
}
void
provide_intra_process_message(MessageUniquePtr message) override
{
if constexpr (std::is_same<SubscribedType, ROSMessageType>::value) {
buffer_->add_unique(std::move(message));
trigger_guard_condition();
} else {
buffer_->add_unique(convert_ros_message_to_subscribed_type_unique_ptr(*message));
trigger_guard_condition();
}
this->invoke_on_new_message();
}
void
provide_intra_process_data(ConstDataSharedPtr message)
{
buffer_->add_shared(std::move(message));
trigger_guard_condition();
this->invoke_on_new_message();
}
void
provide_intra_process_message(MessageUniquePtr message)
provide_intra_process_data(SubscribedTypeUniquePtr message)
{
buffer_->add_unique(std::move(message));
trigger_guard_condition();
this->invoke_on_new_message();
}
bool
use_take_shared_method() const
use_take_shared_method() const override
{
return buffer_->use_take_shared_method();
}
protected:
void
trigger_guard_condition()
trigger_guard_condition() override
{
gc_.trigger();
this->gc_.trigger();
}
BufferUniquePtr buffer_;
SubscribedTypeAllocator subscribed_type_allocator_;
SubscribedTypeDeleter subscribed_type_deleter_;
};
} // namespace experimental

View File

@@ -0,0 +1,553 @@
// Copyright 2023 iRobot Corporation.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__EXPERIMENTAL__TIMERS_MANAGER_HPP_
#define RCLCPP__EXPERIMENTAL__TIMERS_MANAGER_HPP_
#include <algorithm>
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <utility>
#include <vector>
#include "rclcpp/context.hpp"
#include "rclcpp/timer.hpp"
namespace rclcpp
{
namespace experimental
{
/**
* @brief This class provides a way for storing and executing timer objects.
* It provides APIs to suit the needs of different applications and execution models.
* All public APIs provided by this class are thread-safe.
*
* Timers management
* This class provides APIs to add/remove timers to/from an internal storage.
* It keeps a list of weak pointers from added timers, and locks them only when
* they need to be executed or modified.
* Timers are kept ordered in a binary-heap priority queue.
* Calls to add/remove APIs will temporarily block the execution of the timers and
* will require to reorder the internal priority queue.
* Because of this, they have a not-negligible impact on the performance.
*
* Timers execution
* The most efficient use of this class consists in letting a TimersManager object
* to spawn a thread where timers are monitored and optionally executed.
* This can be controlled via the `start` and `stop` methods.
* Ready timers can either be executed or an on_ready_callback can be used to notify
* other entities that they are ready and need to be executed.
* Other APIs allow to directly execute a given timer.
*
* This class assumes that the `execute_callback()` API of the stored timers is never
* called by other entities, but it can only be called from here.
* If this assumption is not respected, the heap property may be invalidated,
* so timers may be executed out of order, without this object noticing it.
*
*/
class TimersManager
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimersManager)
/**
* @brief Construct a new TimersManager object
*
* @param context custom context to be used.
* Shared ownership of the context is held until destruction.
* @param on_ready_callback The timers on ready callback. The presence of this function
* indicates what to do when the TimersManager is running and a timer becomes ready.
* The TimersManager is considered "running" when the `start` method has been called.
* If it's callable, it will be invoked instead of the timer callback.
* If it's not callable, then the TimersManager will
* directly execute timers when they are ready.
* All the methods that execute a given timer (e.g. `execute_head_timer`
* or `execute_ready_timer`) without the TimersManager being `running`, i.e.
* without actually explicitly waiting for the timer to become ready, will ignore this
* callback.
*/
RCLCPP_PUBLIC
TimersManager(
std::shared_ptr<rclcpp::Context> context,
std::function<void(const rclcpp::TimerBase *)> on_ready_callback = nullptr);
/**
* @brief Destruct the TimersManager object making sure to stop thread and release memory.
*/
RCLCPP_PUBLIC
~TimersManager();
/**
* @brief Adds a new timer to the storage, maintaining weak ownership of it.
* Function is thread safe and it can be called regardless of the state of the timers thread.
*
* @param timer the timer to add.
* @throws std::invalid_argument if timer is a nullptr.
*/
RCLCPP_PUBLIC
void add_timer(rclcpp::TimerBase::SharedPtr timer);
/**
* @brief Remove a single timer from the object storage.
* Will do nothing if the timer was not being stored here.
* Function is thread safe and it can be called regardless of the state of the timers thread.
*
* @param timer the timer to remove.
*/
RCLCPP_PUBLIC
void remove_timer(rclcpp::TimerBase::SharedPtr timer);
/**
* @brief Remove all the timers stored in the object.
* Function is thread safe and it can be called regardless of the state of the timers thread.
*/
RCLCPP_PUBLIC
void clear();
/**
* @brief Starts a thread that takes care of executing the timers stored in this object.
* Function will throw an error if the timers thread was already running.
*/
RCLCPP_PUBLIC
void start();
/**
* @brief Stops the timers thread.
* Will do nothing if the timer thread was not running.
*/
RCLCPP_PUBLIC
void stop();
/**
* @brief Get the number of timers that are currently ready.
* This function is thread safe.
*
* @return size_t number of ready timers.
* @throws std::runtime_error if the timers thread was already running.
*/
RCLCPP_PUBLIC
size_t get_number_ready_timers();
/**
* @brief Executes head timer if ready.
* This function is thread safe.
* This function will try to execute the timer callback regardless of whether
* the TimersManager on_ready_callback was passed during construction.
*
* @return true if head timer was ready.
* @throws std::runtime_error if the timers thread was already running.
*/
RCLCPP_PUBLIC
bool execute_head_timer();
/**
* @brief Executes timer identified by its ID.
* This function is thread safe.
* This function will try to execute the timer callback regardless of whether
* the TimersManager on_ready_callback was passed during construction.
*
* @param timer_id the timer ID of the timer to execute
*/
RCLCPP_PUBLIC
void execute_ready_timer(const rclcpp::TimerBase * timer_id);
/**
* @brief Get the amount of time before the next timer triggers.
* This function is thread safe.
*
* @return std::chrono::nanoseconds to wait,
* the returned value could be negative if the timer is already expired
* or std::chrono::nanoseconds::max() if there are no timers stored in the object.
* @throws std::runtime_error if the timers thread was already running.
*/
RCLCPP_PUBLIC
std::chrono::nanoseconds get_head_timeout();
private:
RCLCPP_DISABLE_COPY(TimersManager)
using TimerPtr = rclcpp::TimerBase::SharedPtr;
using WeakTimerPtr = rclcpp::TimerBase::WeakPtr;
// Forward declaration
class TimersHeap;
/**
* @brief This class allows to store weak pointers to timers in a heap-like data structure.
* The root of the heap is the timer that triggers first.
* Since this class uses weak ownership, it is not guaranteed that it represents a valid heap
* at any point in time as timers could go out of scope, thus invalidating it.
* The "validate_and_lock" API allows to restore the heap property and also returns a locked version
* of the timers heap.
* This class is not thread safe and requires external mutexes to protect its usage.
*/
class WeakTimersHeap
{
public:
/**
* @brief Add a new timer to the heap. After the addition, the heap property is enforced.
*
* @param timer new timer to add.
* @return true if timer has been added, false if it was already there.
*/
bool add_timer(TimerPtr timer)
{
TimersHeap locked_heap = this->validate_and_lock();
bool added = locked_heap.add_timer(std::move(timer));
if (added) {
// Re-create the weak heap with the new timer added
this->store(locked_heap);
}
return added;
}
/**
* @brief Remove a timer from the heap. After the removal, the heap property is enforced.
*
* @param timer timer to remove.
* @return true if timer has been removed, false if it was not there.
*/
bool remove_timer(TimerPtr timer)
{
TimersHeap locked_heap = this->validate_and_lock();
bool removed = locked_heap.remove_timer(std::move(timer));
if (removed) {
// Re-create the weak heap with the timer removed
this->store(locked_heap);
}
return removed;
}
/**
* @brief Retrieve the timer identified by the key
* @param timer_id The ID of the timer to retrieve.
* @return TimerPtr if there's a timer associated with the ID, nullptr otherwise
*/
TimerPtr get_timer(const rclcpp::TimerBase * timer_id)
{
for (auto & weak_timer : weak_heap_) {
auto timer = weak_timer.lock();
if (timer.get() == timer_id) {
return timer;
}
}
return nullptr;
}
/**
* @brief Returns a const reference to the front element.
*/
const WeakTimerPtr & front() const
{
return weak_heap_.front();
}
/**
* @brief Returns whether the heap is empty or not.
*/
bool empty() const
{
return weak_heap_.empty();
}
/**
* @brief This function restores the current object as a valid heap
* and it returns a locked version of it.
* Timers that went out of scope are removed from the container.
* It is the only public API to access and manipulate the stored timers.
*
* @return TimersHeap owned timers corresponding to the current object
*/
TimersHeap validate_and_lock()
{
TimersHeap locked_heap;
bool any_timer_destroyed = false;
for (auto weak_timer : weak_heap_) {
auto timer = weak_timer.lock();
if (timer) {
// This timer is valid, so add it to the locked heap
// Note: we access friend private `owned_heap_` member field.
locked_heap.owned_heap_.push_back(std::move(timer));
} else {
// This timer went out of scope, so we don't add it to locked heap
// and we mark the corresponding flag.
// It's not needed to erase it from weak heap, as we are going to re-heapify.
// Note: we can't exit from the loop here, as we need to find all valid timers.
any_timer_destroyed = true;
}
}
// If a timer has gone out of scope, then the remaining elements do not represent
// a valid heap anymore. We need to re-heapify the timers heap.
if (any_timer_destroyed) {
locked_heap.heapify();
// Re-create the weak heap now that elements have been heapified again
this->store(locked_heap);
}
return locked_heap;
}
/**
* @brief This function allows to recreate the heap of weak pointers
* from an heap of owned pointers.
* It is required to be called after a locked TimersHeap generated from this object
* has been modified in any way (e.g. timers triggered, added, removed).
*
* @param heap timers heap to store as weak pointers
*/
void store(const TimersHeap & heap)
{
weak_heap_.clear();
// Note: we access friend private `owned_heap_` member field.
for (auto t : heap.owned_heap_) {
weak_heap_.push_back(t);
}
}
/**
* @brief Remove all timers from the heap.
*/
void clear()
{
weak_heap_.clear();
}
private:
std::vector<WeakTimerPtr> weak_heap_;
};
/**
* @brief This class is the equivalent of WeakTimersHeap but with ownership of the timers.
* It can be generated by locking the weak version.
* It provides operations to manipulate the heap.
* This class is not thread safe and requires external mutexes to protect its usage.
*/
class TimersHeap
{
public:
/**
* @brief Try to add a new timer to the heap.
* After the addition, the heap property is preserved.
* @param timer new timer to add.
* @return true if timer has been added, false if it was already there.
*/
bool add_timer(TimerPtr timer)
{
// Nothing to do if the timer is already stored here
auto it = std::find(owned_heap_.begin(), owned_heap_.end(), timer);
if (it != owned_heap_.end()) {
return false;
}
owned_heap_.push_back(std::move(timer));
std::push_heap(owned_heap_.begin(), owned_heap_.end(), timer_greater);
return true;
}
/**
* @brief Try to remove a timer from the heap.
* After the removal, the heap property is preserved.
* @param timer timer to remove.
* @return true if timer has been removed, false if it was not there.
*/
bool remove_timer(TimerPtr timer)
{
// Nothing to do if the timer is not stored here
auto it = std::find(owned_heap_.begin(), owned_heap_.end(), timer);
if (it == owned_heap_.end()) {
return false;
}
owned_heap_.erase(it);
this->heapify();
return true;
}
/**
* @brief Returns a reference to the front element.
* @return reference to front element.
*/
TimerPtr & front()
{
return owned_heap_.front();
}
/**
* @brief Returns a const reference to the front element.
* @return const reference to front element.
*/
const TimerPtr & front() const
{
return owned_heap_.front();
}
/**
* @brief Returns whether the heap is empty or not.
* @return true if the heap is empty.
*/
bool empty() const
{
return owned_heap_.empty();
}
/**
* @brief Returns the size of the heap.
* @return the number of valid timers in the heap.
*/
size_t size() const
{
return owned_heap_.size();
}
/**
* @brief Get the number of timers that are currently ready.
* @return size_t number of ready timers.
*/
size_t get_number_ready_timers() const
{
size_t ready_timers = 0;
for (TimerPtr t : owned_heap_) {
if (t->is_ready()) {
ready_timers++;
}
}
return ready_timers;
}
/**
* @brief Restore a valid heap after the root value has been replaced (e.g. timer triggered).
*/
void heapify_root()
{
// The following code is a more efficient version than doing
// pop_heap, pop_back, push_back, push_heap
// as it removes the need for the last push_heap
// Push the modified element (i.e. the current root) at the bottom of the heap
owned_heap_.push_back(owned_heap_[0]);
// Exchange first and last-1 elements and reheapify
std::pop_heap(owned_heap_.begin(), owned_heap_.end(), timer_greater);
// Remove last element
owned_heap_.pop_back();
}
/**
* @brief Completely restores the structure to a valid heap
*/
void heapify()
{
std::make_heap(owned_heap_.begin(), owned_heap_.end(), timer_greater);
}
/**
* @brief Helper function to clear the "on_reset_callback" on all associated timers.
*/
void clear_timers_on_reset_callbacks()
{
for (TimerPtr & t : owned_heap_) {
t->clear_on_reset_callback();
}
}
/**
* @brief Friend declaration to allow the `validate_and_lock()` function to access the
* underlying heap container
*/
friend TimersHeap WeakTimersHeap::validate_and_lock();
/**
* @brief Friend declaration to allow the `store()` function to access the
* underlying heap container
*/
friend void WeakTimersHeap::store(const TimersHeap & heap);
private:
/**
* @brief Comparison function between timers.
* @return true if `a` triggers after `b`.
*/
static bool timer_greater(TimerPtr a, TimerPtr b)
{
// TODO(alsora): this can cause an error if timers are using different clocks
return a->time_until_trigger() > b->time_until_trigger();
}
std::vector<TimerPtr> owned_heap_;
};
/**
* @brief Implements a loop that keeps executing ready timers.
* This function is executed in the timers thread.
*/
void run_timers();
/**
* @brief Get the amount of time before the next timer triggers.
* This function is not thread safe, acquire a mutex before calling it.
*
* @return std::chrono::nanoseconds to wait,
* the returned value could be negative if the timer is already expired
* or std::chrono::nanoseconds::max() if the heap is empty.
* This function is not thread safe, acquire the timers_mutex_ before calling it.
*/
std::chrono::nanoseconds get_head_timeout_unsafe();
/**
* @brief Executes all the timers currently ready when the function is invoked
* while keeping the heap correctly sorted.
* This function is not thread safe, acquire the timers_mutex_ before calling it.
*/
void execute_ready_timers_unsafe();
// Callback to be called when timer is ready
std::function<void(const rclcpp::TimerBase *)> on_ready_callback_ = nullptr;
// Thread used to run the timers execution task
std::thread timers_thread_;
// Protects access to timers
std::mutex timers_mutex_;
// Protects access to stop()
std::mutex stop_mutex_;
// Notifies the timers thread whenever timers are added/removed
std::condition_variable timers_cv_;
// Flag used as predicate by timers_cv_ that denotes one or more timers being added/removed
bool timers_updated_ {false};
// Indicates whether the timers thread is currently running or not
std::atomic<bool> running_ {false};
// Parent context used to understand if ROS is still active
std::shared_ptr<rclcpp::Context> context_;
// Timers heap storage with weak ownership
WeakTimersHeap weak_timers_heap_;
};
} // namespace experimental
} // namespace rclcpp
#endif // RCLCPP__EXPERIMENTAL__TIMERS_MANAGER_HPP_

View File

@@ -80,7 +80,9 @@ struct function_traits<ReturnTypeT (*)(Args ...)>: function_traits<ReturnTypeT(A
// std::bind for object methods
template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ... FArgs>
#if defined _LIBCPP_VERSION // libc++ (Clang)
#if defined DOXYGEN_ONLY
struct function_traits<std::bind<ReturnTypeT (ClassT::*)(Args ...), FArgs ...>>
#elif defined _LIBCPP_VERSION // libc++ (Clang)
struct function_traits<std::__bind<ReturnTypeT (ClassT::*)(Args ...), FArgs ...>>
#elif defined _GLIBCXX_RELEASE // glibc++ (GNU C++ >= 7.1)
struct function_traits<std::_Bind<ReturnTypeT(ClassT::* (FArgs ...))(Args ...)>>
@@ -97,7 +99,9 @@ struct function_traits<
// std::bind for object const methods
template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ... FArgs>
#if defined _LIBCPP_VERSION // libc++ (Clang)
#if defined DOXYGEN_ONLY
struct function_traits<std::bind<ReturnTypeT (ClassT::*)(Args ...) const, FArgs ...>>
#elif 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>>
@@ -114,7 +118,9 @@ struct function_traits<
// std::bind for free functions
template<typename ReturnTypeT, typename ... Args, typename ... FArgs>
#if defined _LIBCPP_VERSION // libc++ (Clang)
#if defined DOXYGEN_ONLY
struct function_traits<std::bind<ReturnTypeT( &)(Args ...), FArgs ...>>
#elif defined _LIBCPP_VERSION // libc++ (Clang)
struct function_traits<std::__bind<ReturnTypeT( &)(Args ...), FArgs ...>>
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
struct function_traits<std::_Bind<ReturnTypeT(*(FArgs ...))(Args ...)>>

View File

@@ -31,6 +31,8 @@
#include "rclcpp/typesupport_helpers.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/rmw.h"
namespace rclcpp
{
@@ -60,7 +62,6 @@ public:
* \param topic_name Topic name
* \param topic_type Topic type
* \param qos %QoS settings
* \param callback Callback for new messages of serialized form
* \param options %Publisher options.
* Not all publisher options are currently respected, the only relevant options for this
* publisher are `event_callbacks`, `use_default_callbacks`, and `%callback_group`.
@@ -77,38 +78,12 @@ public:
node_base,
topic_name,
*rclcpp::get_typesupport_handle(topic_type, "rosidl_typesupport_cpp", *ts_lib),
options.template to_rcl_publisher_options<rclcpp::SerializedMessage>(qos)),
options.template to_rcl_publisher_options<rclcpp::SerializedMessage>(qos),
// NOTE(methylDragon): Passing these args separately is necessary for event binding
options.event_callbacks,
options.use_default_callbacks),
ts_lib_(ts_lib)
{
// This is unfortunately duplicated with the code in publisher.hpp.
// TODO(nnmm): Deduplicate by moving this into PublisherBase.
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);
}
if (options.event_callbacks.incompatible_qos_callback) {
this->add_event_handler(
options.event_callbacks.incompatible_qos_callback,
RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
} else if (options.use_default_callbacks) {
// Register default callback when not specified
try {
this->add_event_handler(
[this](QOSOfferedIncompatibleQoSInfo & info) {
this->default_incompatible_qos_callback(info);
},
RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
} catch (UnsupportedEventTypeException & /*exc*/) {
// pass
}
}
}
{}
RCLCPP_PUBLIC
virtual ~GenericPublisher() = default;
@@ -117,9 +92,24 @@ public:
RCLCPP_PUBLIC
void publish(const rclcpp::SerializedMessage & message);
/**
* Publish a rclcpp::SerializedMessage via loaned message after de-serialization.
*
* \param message a serialized message
* \throws anything rclcpp::exceptions::throw_from_rcl_error can show
*/
RCLCPP_PUBLIC
void publish_as_loaned_msg(const rclcpp::SerializedMessage & message);
private:
// The type support library should stay loaded, so it is stored in the GenericPublisher
std::shared_ptr<rcpputils::SharedLibrary> ts_lib_;
void * borrow_loaned_message();
void deserialize_message(
const rmw_serialized_message_t & serialized_message,
void * deserialized_msg);
void publish_loaned_message(void * loaned_message);
};
} // namespace rclcpp

View File

@@ -81,45 +81,13 @@ public:
node_base,
*rclcpp::get_typesupport_handle(topic_type, "rosidl_typesupport_cpp", *ts_lib),
topic_name,
options.template to_rcl_subscription_options<rclcpp::SerializedMessage>(qos),
true),
options.to_rcl_subscription_options(qos),
options.event_callbacks,
options.use_default_callbacks,
DeliveredMessageKind::SERIALIZED_MESSAGE),
callback_(callback),
ts_lib_(ts_lib)
{
// This is unfortunately duplicated with the code in subscription.hpp.
// TODO(nnmm): Deduplicate by moving this into SubscriptionBase.
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);
}
if (options.event_callbacks.incompatible_qos_callback) {
this->add_event_handler(
options.event_callbacks.incompatible_qos_callback,
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
} else if (options.use_default_callbacks) {
// Register default callback when not specified
try {
this->add_event_handler(
[this](QOSRequestedIncompatibleQoSInfo & info) {
this->default_incompatible_qos_callback(info);
},
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
} catch (UnsupportedEventTypeException & /*exc*/) {
// pass
}
}
if (options.event_callbacks.message_lost_callback) {
this->add_event_handler(
options.event_callbacks.message_lost_callback,
RCL_SUBSCRIPTION_MESSAGE_LOST);
}
}
{}
RCLCPP_PUBLIC
virtual ~GenericSubscription() = default;
@@ -155,6 +123,31 @@ public:
RCLCPP_PUBLIC
void return_serialized_message(std::shared_ptr<rclcpp::SerializedMessage> & message) override;
// DYNAMIC TYPE ==================================================================================
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr get_shared_dynamic_message_type()
override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr get_shared_dynamic_message() override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support() override;
RCLCPP_PUBLIC
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr create_dynamic_message() override;
RCLCPP_PUBLIC
void return_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) override;
RCLCPP_PUBLIC
void handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info) override;
private:
RCLCPP_DISABLE_COPY(GenericSubscription)

View File

@@ -28,7 +28,17 @@
namespace rclcpp
{
/// Specialization for when MessageT is an actual ROS message type.
#ifdef DOXYGEN_ONLY
/// Returns the message type support for the given `MessageT` type.
/**
* \tparam MessageT an actual ROS message type or an adapted type using `rclcpp::TypeAdapter`
*/
template<typename MessageT>
constexpr const rosidl_message_type_support_t & get_message_type_support_handle();
#else
template<typename MessageT>
constexpr
typename std::enable_if_t<
@@ -44,7 +54,6 @@ get_message_type_support_handle()
return *handle;
}
/// Specialization for when MessageT is an adapted type using rclcpp::TypeAdapter.
template<typename AdaptedType>
constexpr
typename std::enable_if_t<
@@ -63,12 +72,9 @@ get_message_type_support_handle()
return *handle;
}
/// Specialization for when MessageT is not a ROS message nor an adapted type.
/**
* This specialization is a pass through runtime check, which allows a better
* static_assert to catch this issue further down the line.
* This should never get to be called in practice, and is purely defensive.
*/
// This specialization is a pass through runtime check, which allows a better
// static_assert to catch this issue further down the line.
// This should never get to be called in practice, and is purely defensive.
template<
typename AdaptedType
>
@@ -85,6 +91,8 @@ get_message_type_support_handle()
"should never be called");
}
#endif
} // namespace rclcpp
#endif // RCLCPP__GET_MESSAGE_TYPE_SUPPORT_HANDLE_HPP_

View File

@@ -128,6 +128,11 @@ public:
* If start_if_not_started() was never called, this function still succeeds,
* but start_if_not_started() still cannot be called after this function.
*
* Note that if you override this method, but leave shutdown to be called in
* the destruction of this base class, it will not call the overridden
* version from your base class.
* So you need to ensure you call your class's shutdown() in its destructor.
*
* \throws rclcpp::execptions::RCLError from rcl_guard_condition_fini()
* \throws rclcpp::execptions::RCLError from rcl_wait_set_fini()
* \throws std::system_error anything std::mutex::lock() throws

View File

@@ -40,6 +40,8 @@ public:
* Defaults to using the global default context singleton.
* Shared ownership of the context is held with the guard condition until
* destruction.
* \param[in] guard_condition_options Optional guard condition options to be used.
* Defaults to using the default guard condition options.
* \throws std::invalid_argument if the context is nullptr.
* \throws rclcpp::exceptions::RCLError based exceptions when underlying
* rcl functions fail.
@@ -70,7 +72,7 @@ public:
const rcl_guard_condition_t &
get_rcl_guard_condition() const;
/// Notify the wait set waiting on this condition, if any, that the condition had been met.
/// Signal that the condition has been met, notifying both the wait set and listeners, if any.
/**
* This function is thread-safe, and may be called concurrently with waiting
* on this guard condition in a wait set.
@@ -96,10 +98,43 @@ public:
bool
exchange_in_use_by_wait_set_state(bool in_use_state);
/// Adds the guard condition to a waitset
/**
* This function is thread-safe.
* \param[in] wait_set pointer to a wait set where to add the guard condition
*/
RCLCPP_PUBLIC
void
add_to_wait_set(rcl_wait_set_t * wait_set);
/// Set a callback to be called whenever the guard condition is triggered.
/**
* The callback receives a size_t which is the number of times the guard condition was triggered
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if the guard condition was triggered before any
* callback was set.
*
* Calling it again will clear any previously set callback.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the guard condition
* or other information, you may use a lambda with captures or std::bind.
*
* \param[in] callback functor to be called when the guard condition is triggered
*/
RCLCPP_PUBLIC
void
set_on_trigger_callback(std::function<void(size_t)> callback);
protected:
rclcpp::Context::SharedPtr context_;
rcl_guard_condition_t rcl_guard_condition_;
std::atomic<bool> in_use_by_wait_set_{false};
std::recursive_mutex reentrant_mutex_;
std::function<void(size_t)> on_trigger_callback_{nullptr};
size_t unread_count_{0};
rcl_wait_set_t * wait_set_{nullptr};
};
} // namespace rclcpp

View File

@@ -17,6 +17,7 @@
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/visibility_control.hpp"
@@ -79,7 +80,7 @@ get_node_logger(const rcl_node_t * node);
/// Get the current logging directory.
/**
* For more details of how the logging directory is determined,
* see \ref rcl_logging_get_logging_directory.
* see rcl_logging_get_logging_directory().
*
* \returns the logging directory being used.
* \throws rclcpp::exceptions::RCLError if an unexpected error occurs.
@@ -122,6 +123,7 @@ private:
: name_(new std::string(name)) {}
std::shared_ptr<const std::string> name_;
std::shared_ptr<std::pair<std::string, std::string>> logger_sublogger_pairname_ = nullptr;
public:
RCLCPP_PUBLIC
@@ -157,13 +159,7 @@ public:
*/
RCLCPP_PUBLIC
Logger
get_child(const std::string & suffix)
{
if (!name_) {
return Logger();
}
return Logger(*name_ + "." + suffix);
}
get_child(const std::string & suffix);
/// Set level for current logger.
/**
@@ -174,6 +170,24 @@ public:
RCLCPP_PUBLIC
void
set_level(Level level);
/// Get effective level for current logger.
/**
* The effective level is determined as the severity level of
* the logger if it is set, otherwise it is the first specified severity
* level of the logger's ancestors, starting with its closest ancestor.
* The ancestor hierarchy is signified by logger names being separated by dots:
* a logger named `x` is an ancestor of `x.y`, and both `x` and `x.y` are
* ancestors of `x.y.z`, etc.
* If the level has not been set for the logger nor any of its
* ancestors, the default level is used.
*
* \throws rclcpp::exceptions::RCLError if any error happens.
* \return Level for the current logger.
*/
RCLCPP_PUBLIC
Level
get_effective_level() const;
};
} // namespace rclcpp

View File

@@ -100,52 +100,52 @@ public:
static rclcpp::SubscriptionBase::SharedPtr
get_subscription_by_handle(
std::shared_ptr<const rcl_subscription_t> subscriber_handle,
const std::shared_ptr<const rcl_subscription_t> & subscriber_handle,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
static rclcpp::ServiceBase::SharedPtr
get_service_by_handle(
std::shared_ptr<const rcl_service_t> service_handle,
const std::shared_ptr<const rcl_service_t> & service_handle,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
static rclcpp::ClientBase::SharedPtr
get_client_by_handle(
std::shared_ptr<const rcl_client_t> client_handle,
const std::shared_ptr<const rcl_client_t> & client_handle,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
static rclcpp::TimerBase::SharedPtr
get_timer_by_handle(
std::shared_ptr<const rcl_timer_t> timer_handle,
const std::shared_ptr<const rcl_timer_t> & timer_handle,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
static rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(
rclcpp::CallbackGroup::SharedPtr group,
const rclcpp::CallbackGroup::SharedPtr & group,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
static rclcpp::CallbackGroup::SharedPtr
get_group_by_subscription(
rclcpp::SubscriptionBase::SharedPtr subscription,
const rclcpp::SubscriptionBase::SharedPtr & subscription,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
static rclcpp::CallbackGroup::SharedPtr
get_group_by_service(
rclcpp::ServiceBase::SharedPtr service,
const rclcpp::ServiceBase::SharedPtr & service,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
static rclcpp::CallbackGroup::SharedPtr
get_group_by_client(
rclcpp::ClientBase::SharedPtr client,
const rclcpp::ClientBase::SharedPtr & client,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
static rclcpp::CallbackGroup::SharedPtr
get_group_by_timer(
rclcpp::TimerBase::SharedPtr timer,
const rclcpp::TimerBase::SharedPtr & timer,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
static rclcpp::CallbackGroup::SharedPtr
get_group_by_waitable(
rclcpp::Waitable::SharedPtr waitable,
const rclcpp::Waitable::SharedPtr & waitable,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes);
};

View File

@@ -227,7 +227,7 @@ public:
)
);
/// Create a timer.
/// Create a wall timer that uses the wall clock to drive the callback.
/**
* \param[in] period Time interval between triggers of the callback.
* \param[in] callback User-defined callback function.
@@ -240,18 +240,47 @@ public:
CallbackT callback,
rclcpp::CallbackGroup::SharedPtr group = nullptr);
/// Create a timer that uses the node clock to drive the callback.
/**
* \param[in] period Time interval between triggers of the callback.
* \param[in] callback User-defined callback function.
* \param[in] group Callback group to execute this timer's callback in.
*/
template<typename DurationRepT = int64_t, typename DurationT = std::milli, typename CallbackT>
typename rclcpp::GenericTimer<CallbackT>::SharedPtr
create_timer(
std::chrono::duration<DurationRepT, DurationT> period,
CallbackT callback,
rclcpp::CallbackGroup::SharedPtr group = nullptr);
/// Create and return a Client.
/**
* \param[in] service_name The topic to service on.
* \param[in] qos_profile rmw_qos_profile_t Quality of service profile for client.
* \param[in] group Callback group to call the service.
* \return Shared pointer to the created client.
* \deprecated use rclcpp::QoS instead of rmw_qos_profile_t
*/
template<typename ServiceT>
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
typename rclcpp::Client<ServiceT>::SharedPtr
create_client(
const std::string & service_name,
const rmw_qos_profile_t & qos_profile,
rclcpp::CallbackGroup::SharedPtr group = nullptr);
/// Create and return a Client.
/**
* \param[in] service_name The name on which the service is accessible.
* \param[in] qos Quality of service profile for client.
* \param[in] group Callback group to handle the reply to service calls.
* \return Shared pointer to the created client.
*/
template<typename ServiceT>
typename rclcpp::Client<ServiceT>::SharedPtr
create_client(
const std::string & service_name,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
rclcpp::CallbackGroup::SharedPtr group = nullptr);
/// Create and return a Service.
@@ -261,13 +290,31 @@ public:
* \param[in] qos_profile rmw_qos_profile_t Quality of service profile for client.
* \param[in] group Callback group to call the service.
* \return Shared pointer to the created service.
* \deprecated use rclcpp::QoS instead of rmw_qos_profile_t
*/
template<typename ServiceT, typename CallbackT>
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
typename rclcpp::Service<ServiceT>::SharedPtr
create_service(
const std::string & service_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile,
rclcpp::CallbackGroup::SharedPtr group = nullptr);
/// Create and return a Service.
/**
* \param[in] service_name The topic to service on.
* \param[in] callback User-defined callback function.
* \param[in] qos Quality of service profile for the service.
* \param[in] group Callback group to call the service.
* \return Shared pointer to the created service.
*/
template<typename ServiceT, typename CallbackT>
typename rclcpp::Service<ServiceT>::SharedPtr
create_service(
const std::string & service_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
const rclcpp::QoS & qos = rclcpp::ServicesQoS(),
rclcpp::CallbackGroup::SharedPtr group = nullptr);
/// Create and return a GenericPublisher.
@@ -336,11 +383,21 @@ public:
*
* If `ignore_override` is `true`, the parameter override will be ignored.
*
* This method, if successful, will result in any callback registered with
* add_on_set_parameters_callback to be called.
* This method will result in any callback registered with
* `add_on_set_parameters_callback` and `add_post_set_parameters_callback`
* to be called for the parameter being set.
*
* If a callback was registered previously with `add_on_set_parameters_callback`,
* it will be called prior to setting the parameter for the node.
* If that callback prevents the initial value for the parameter from being
* set then rclcpp::exceptions::InvalidParameterValueException is thrown.
*
* If a callback was registered previously with `add_post_set_parameters_callback`,
* it will be called after setting the parameter successfully for the node.
*
* This method will _not_ result in any callbacks registered with
* `add_pre_set_parameters_callback` to be called.
*
* The returned reference will remain valid until the parameter is
* undeclared.
*
@@ -395,22 +452,6 @@ public:
rcl_interfaces::msg::ParameterDescriptor{},
bool ignore_override = false);
/// Declare a parameter
[[deprecated(
"declare_parameter() with only a name is deprecated and will be deleted in the future.\n" \
"If you want to declare a parameter that won't change type without a default value use:\n" \
"`node->declare_parameter<ParameterT>(name)`, where e.g. ParameterT=int64_t.\n\n" \
"If you want to declare a parameter that can dynamically change type use:\n" \
"```\n" \
"rcl_interfaces::msg::ParameterDescriptor descriptor;\n" \
"descriptor.dynamic_typing = true;\n" \
"node->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);\n" \
"```"
)]]
RCLCPP_PUBLIC
const rclcpp::ParameterValue &
declare_parameter(const std::string & name);
/// Declare and initialize a parameter with a type.
/**
* See the non-templated declare_parameter() on this class for details.
@@ -473,11 +514,22 @@ public:
* If `ignore_overrides` is `true`, all the overrides of the parameters declared
* by the function call will be ignored.
*
* This method will result in any callback registered with
* `add_on_set_parameters_callback` and `add_post_set_parameters_callback`
* to be called once for each parameter.
*
* This method, if successful, will result in any callback registered with
* add_on_set_parameters_callback to be called, once for each parameter.
* `add_on_set_parameters_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.
*
* If a callback was registered previously with `add_post_set_parameters_callback`,
* it will be called after setting the parameters successfully for the node,
* once for each parameter.
*
* This method will _not_ result in any callbacks registered with
* `add_pre_set_parameters_callback` to be called.
*
* \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.
@@ -515,8 +567,9 @@ public:
/// Undeclare a previously declared parameter.
/**
* This method will not cause a callback registered with
* add_on_set_parameters_callback to be called.
* This method will _not_ cause a callback registered with any of the
* `add_pre_set_parameters_callback`, `add_on_set_parameters_callback` and
* `add_post_set_parameters_callback` to be called.
*
* \param[in] name The name of the parameter to be undeclared.
* \throws rclcpp::exceptions::ParameterNotDeclaredException if the parameter
@@ -550,11 +603,24 @@ public:
* Parameter overrides are ignored by set_parameter.
*
* This method will result in any callback registered with
* add_on_set_parameters_callback to be called.
* `add_pre_set_parameters_callback`, add_on_set_parameters_callback` and
* `add_post_set_parameters_callback` to be called once for the parameter
* being set.
*
* This method will result in any callback registered with
* `add_on_set_parameters_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 a callback was registered previously with `add_pre_set_parameters_callback`,
* it will be called once prior to the validation of the parameter for the node.
* If this callback makes modified parameter list empty, then it will be reflected
* in the returned result; no exceptions will be raised in this case.
*
* If a callback was registered previously with `add_post_set_parameters_callback`,
* it will be called once after setting the parameter successfully for the node.
*
* 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.
@@ -591,11 +657,25 @@ public:
* corresponding SetParametersResult in the vector returned by this function.
*
* This method will result in any callback registered with
* add_on_set_parameters_callback to be called, once for each parameter.
* `add_pre_set_parameters_callback`, `add_on_set_parameters_callback` and
* `add_post_set_parameters_callback` to be called once for each parameter.
* If a callback was registered previously with `add_pre_set_parameters_callback`,
* it will be called prior to the validation of parameters for the node,
* once for each parameter.
* If this callback makes modified parameter list empty, then it will be reflected
* in the returned result; no exceptions will be raised in this case.
*
* This method will result in any callback registered with
* `add_on_set_parameters_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.
*
* If a callback was registered previously with `add_post_set_parameters_callback`,
* it will be called after setting the parameters successfully for the node,
* once for each parameter.
*
* Like set_parameter() this method will implicitly undeclare parameters
* with the type rclcpp::PARAMETER_NOT_SET.
*
@@ -622,11 +702,25 @@ public:
* If the exception is thrown then none of the parameters will have been set.
*
* This method will result in any callback registered with
* add_on_set_parameters_callback to be called, just one time.
* `add_pre_set_parameters_callback`, `add_on_set_parameters_callback` and
* `add_post_set_parameters_callback` to be called only 'once' for all parameters.
*
* If a callback was registered previously with `add_pre_set_parameters_callback`,
* it will be called prior to the validation of node parameters, just one time
* for all parameters.
* If this callback makes modified parameter list empty, then it will be reflected
* in the returned result; no exceptions will be raised in this case.
*
* This method will result in any callback registered with
* 'add_on_set_parameters_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 a callback was registered previously with `add_post_set_parameters_callback`,
* it will be called after setting the node parameters successfully, just one time
* for all parameters.
*
* If you pass multiple rclcpp::Parameter instances with the same name, then
* only the last one in the vector (forward iteration) will be set.
*
@@ -646,17 +740,21 @@ public:
/**
* If the parameter has not been declared, then this method may throw the
* rclcpp::exceptions::ParameterNotDeclaredException exception.
* If the parameter has not been initialized, then this method may throw the
* rclcpp::exceptions::ParameterUninitializedException exception.
*
* 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
* not throw the rclcpp::exceptions::ParameterNotDeclaredException 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.
* \throws rclcpp::exceptions::ParameterUninitializedException if the parameter
* has not been initialized.
*/
RCLCPP_PUBLIC
rclcpp::Parameter
@@ -720,14 +818,32 @@ public:
ParameterT & parameter,
const ParameterT & alternative_value) const;
/// Return the parameter value, or the "alternative_value" if not set.
/**
* If the parameter was not set, then the "alternative_value" argument is returned.
*
* 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[in] alternative_value Value to be stored in output if the parameter was not set.
* \returns The value of the parameter.
*/
template<typename ParameterT>
ParameterT
get_parameter_or(
const std::string & name,
const ParameterT & alternative_value) const;
/// Return the parameters by the given parameter names.
/**
* Like get_parameters(), this method may throw the
* Like get_parameter(const std::string &), this method may throw the
* rclcpp::exceptions::ParameterNotDeclaredException exception if the
* requested parameter has not been declared and undeclared parameters are
* not allowed.
* not allowed, and may throw the rclcpp::exceptions::ParameterUninitializedException exception.
*
* Also like get_parameters(), if undeclared parameters are allowed and the
* Also like get_parameter(const std::string &), 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.
@@ -737,6 +853,8 @@ public:
* \throws rclcpp::exceptions::ParameterNotDeclaredException if any of the
* parameters have not been declared and undeclared parameters are not
* allowed.
* \throws rclcpp::exceptions::ParameterUninitializedException if any of the
* parameters have not been initialized.
*/
RCLCPP_PUBLIC
std::vector<rclcpp::Parameter>
@@ -857,12 +975,85 @@ public:
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const;
using PreSetParametersCallbackHandle =
rclcpp::node_interfaces::PreSetParametersCallbackHandle;
using PreSetParametersCallbackType =
rclcpp::node_interfaces::NodeParametersInterface::PreSetParametersCallbackType;
using OnSetParametersCallbackHandle =
rclcpp::node_interfaces::OnSetParametersCallbackHandle;
using OnParametersSetCallbackType =
rclcpp::node_interfaces::NodeParametersInterface::OnParametersSetCallbackType;
using OnSetParametersCallbackType =
rclcpp::node_interfaces::NodeParametersInterface::OnSetParametersCallbackType;
using OnParametersSetCallbackType [[deprecated("use OnSetParametersCallbackType instead")]] =
OnSetParametersCallbackType;
/// Add a callback for when parameters are being set.
using PostSetParametersCallbackHandle =
rclcpp::node_interfaces::PostSetParametersCallbackHandle;
using PostSetParametersCallbackType =
rclcpp::node_interfaces::NodeParametersInterface::PostSetParametersCallbackType;
/// Add a callback that gets triggered before parameters are validated.
/**
* This callback can be used to modify the original list of parameters being
* set by the user.
*
* The modified list of parameters is then forwarded to the "on set parameter"
* callback for validation.
*
* The callback is called whenever any of the `set_parameter*` methods are called
* or when a set parameter service request is received.
*
* The callback takes a reference to the vector of parameters to be set.
*
* The vector of parameters may be modified by the callback.
*
* One of the use case of "pre set callback" can be updating additional parameters
* conditioned on changes to a parameter.
*
* Users should retain a copy of the returned shared pointer, as the callback
* is valid only as long as the smart pointer is alive.
*
* For an example callback:
*
*```cpp
* void
* preSetParameterCallback(std::vector<rclcpp::Parameter> & parameters)
* {
* for (auto & param : parameters) {
* if (param.get_name() == "param1") {
* parameters.push_back(rclcpp::Parameter("param2", 4.0));
* }
* }
* }
* ```
* The above callback appends 'param2' to the list of parameters to be set if
* 'param1' is being set by the user.
*
* All parameters in the vector will be set atomically.
*
* Note that the callback is only called while setting parameters with `set_parameter`,
* `set_parameters`, `set_parameters_atomically`, or externally with a parameters service.
*
* The callback is not called when parameters are declared with `declare_parameter`
* or `declare_parameters`.
*
* The callback is not called when parameters are undeclared with `undeclare_parameter`.
*
* An empty modified parameter list from the callback will result in "set_parameter*"
* returning an unsuccessful result.
*
* The `remove_pre_set_parameters_callback` can be used to deregister the callback.
*
* \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 PreSetParametersCallbackHandle fails.
*/
RCLCPP_PUBLIC
RCUTILS_WARN_UNUSED
PreSetParametersCallbackHandle::SharedPtr
add_pre_set_parameters_callback(PreSetParametersCallbackType callback);
/// Add a callback to validate parameters before they are 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
@@ -870,6 +1061,9 @@ public:
* rcl_interfaces::msg::SetParametersResult to indicate whether or not the
* parameter should be set or not, and if not why.
*
* Users should retain a copy of the returned shared pointer, as the callback
* is valid only as long as the smart pointer is alive.
*
* For an example callback:
*
* ```cpp
@@ -901,6 +1095,8 @@ public:
* 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.
*
* The callback is not called when parameters are undeclared with `undeclare_parameter`.
*
* Some constraints like read_only are enforced before the callback is called.
*
* The callback may introspect other already set parameters (by calling any
@@ -929,7 +1125,80 @@ public:
RCLCPP_PUBLIC
RCUTILS_WARN_UNUSED
OnSetParametersCallbackHandle::SharedPtr
add_on_set_parameters_callback(OnParametersSetCallbackType callback);
add_on_set_parameters_callback(OnSetParametersCallbackType callback);
/// Add a callback that gets triggered after parameters are set successfully.
/**
* The callback is called when any of the `set_parameter*` or `declare_parameter*`
* methods are successful.
*
* Users should retain a copy of the returned shared pointer, as the callback
* is valid only as long as the smart pointer is alive.
*
* The callback takes a reference to a const vector of parameters that have been
* set successfully.
*
* The post callback can be valuable as a place to cause side-effects based on
* parameter changes.
* For instance updating internally tracked class attributes once parameters
* have been changed successfully.
*
* For an example callback:
*
* ```cpp
* void
* postSetParameterCallback(const std::vector<rclcpp::Parameter> & parameters)
* {
* for(const auto & param:parameters) {
* // the internal class member can be changed after
* // successful change to param1 or param2
* if(param.get_name() == "param1") {
* internal_tracked_class_parameter_1_ = param.get_value<double>();
* }
* else if(param.get_name() == "param2") {
* internal_tracked_class_parameter_2_ = param.get_value<double>();
* }
* }
* }
* ```
*
* The above callback takes a const reference to list of parameters that have been
* set successfully and as a result of this updates the internally tracked class attributes
* `internal_tracked_class_parameter_1_` and `internal_tracked_class_parameter_2_`
* respectively.
*
* This callback should not modify parameters.
*
* The callback is called when parameters are declared with `declare_parameter`
* or `declare_parameters`. See `declare_parameter` or `declare_parameters` above.
*
* The callback is not called when parameters are undeclared with `undeclare_parameter`.
*
* If you want to make changes to parameters based on changes to another, use
* `add_pre_set_parameters_callback`.
*
* The `remove_post_set_parameters_callback` can be used to deregister the callback.
*
* \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
RCUTILS_WARN_UNUSED
PostSetParametersCallbackHandle::SharedPtr
add_post_set_parameters_callback(PostSetParametersCallbackType callback);
/// Remove a callback registered with `add_pre_set_parameters_callback`.
/**
* Delete a handler returned by `add_pre_set_parameters_callback`.
*
* \param handler The callback handler to remove.
* \throws std::runtime_error if the handler was not created with `add_pre_set_parameters_callback`,
* or if it has been removed before.
*/
RCLCPP_PUBLIC
void
remove_pre_set_parameters_callback(const PreSetParametersCallbackHandle * const handler);
/// Remove a callback registered with `add_on_set_parameters_callback`.
/**
@@ -958,6 +1227,18 @@ public:
void
remove_on_set_parameters_callback(const OnSetParametersCallbackHandle * const handler);
/// Remove a callback registered with `add_post_set_parameters_callback`.
/**
* Delete a handler returned by `add_post_set_parameters_callback`.
*
* \param handler The callback handler to remove.
* \throws std::runtime_error if the handler was not created with `add_post_set_parameters_callback`,
* or if it has been removed before.
*/
RCLCPP_PUBLIC
void
remove_post_set_parameters_callback(const PostSetParametersCallbackHandle * const handler);
/// Get the fully-qualified names of all available nodes.
/**
* The fully-qualified name includes the local namespace and name of the node.

View File

@@ -120,6 +120,38 @@ Node::create_wall_timer(
this->node_timers_.get());
}
template<typename DurationRepT, typename DurationT, typename CallbackT>
typename rclcpp::GenericTimer<CallbackT>::SharedPtr
Node::create_timer(
std::chrono::duration<DurationRepT, DurationT> period,
CallbackT callback,
rclcpp::CallbackGroup::SharedPtr group)
{
return rclcpp::create_timer(
this->get_clock(),
period,
std::move(callback),
group,
this->node_base_.get(),
this->node_timers_.get());
}
template<typename ServiceT>
typename Client<ServiceT>::SharedPtr
Node::create_client(
const std::string & service_name,
const rclcpp::QoS & qos,
rclcpp::CallbackGroup::SharedPtr group)
{
return rclcpp::create_client<ServiceT>(
node_base_,
node_graph_,
node_services_,
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
qos,
group);
}
template<typename ServiceT>
typename Client<ServiceT>::SharedPtr
Node::create_client(
@@ -136,6 +168,23 @@ Node::create_client(
group);
}
template<typename ServiceT, typename CallbackT>
typename rclcpp::Service<ServiceT>::SharedPtr
Node::create_service(
const std::string & service_name,
CallbackT && callback,
const rclcpp::QoS & qos,
rclcpp::CallbackGroup::SharedPtr group)
{
return rclcpp::create_service<ServiceT, CallbackT>(
node_base_,
node_services_,
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
std::forward<CallbackT>(callback),
qos,
group);
}
template<typename ServiceT, typename CallbackT>
typename rclcpp::Service<ServiceT>::SharedPtr
Node::create_service(
@@ -220,12 +269,16 @@ Node::declare_parameter(
// get advantage of parameter value template magic to get
// the correct rclcpp::ParameterType from ParameterT
rclcpp::ParameterValue value{ParameterT{}};
return this->declare_parameter(
name,
value.get_type(),
parameter_descriptor,
ignore_override
).get<ParameterT>();
try {
return this->declare_parameter(
name,
value.get_type(),
parameter_descriptor,
ignore_override
).get<ParameterT>();
} catch (const ParameterTypeException &) {
throw exceptions::UninitializedStaticallyTypedParameterException(name);
}
}
template<typename ParameterT>
@@ -309,6 +362,17 @@ Node::get_parameter_or(
return got_parameter;
}
template<typename ParameterT>
ParameterT
Node::get_parameter_or(
const std::string & name,
const ParameterT & alternative_value) const
{
ParameterT parameter;
get_parameter_or(name, parameter, alternative_value);
return 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.

View File

@@ -0,0 +1,208 @@
// Copyright 2022 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_
#define RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_
#include <memory>
#include <tuple>
#include <type_traits>
#include <utility>
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
namespace detail
{
// Support and Helper template classes for the NodeInterfaces class.
template<typename NodeT, typename ... Ts>
std::tuple<std::shared_ptr<Ts>...>
init_tuple(NodeT & n);
/// Stores the interfaces in a tuple, provides constructors, and getters.
template<typename ... InterfaceTs>
struct NodeInterfacesStorage
{
template<typename NodeT>
NodeInterfacesStorage(NodeT & node) // NOLINT(runtime/explicit)
: interfaces_(init_tuple<decltype(node), InterfaceTs ...>(node))
{}
NodeInterfacesStorage()
: interfaces_()
{}
explicit NodeInterfacesStorage(std::shared_ptr<InterfaceTs>... args)
: interfaces_(args ...)
{}
/// Individual Node Interface non-const getter.
template<typename NodeInterfaceT>
std::shared_ptr<NodeInterfaceT>
get()
{
static_assert(
(std::is_same_v<NodeInterfaceT, InterfaceTs>|| ...),
"NodeInterfaces class does not contain given NodeInterfaceT");
return std::get<std::shared_ptr<NodeInterfaceT>>(interfaces_);
}
/// Individual Node Interface const getter.
template<typename NodeInterfaceT>
std::shared_ptr<const NodeInterfaceT>
get() const
{
static_assert(
(std::is_same_v<NodeInterfaceT, InterfaceTs>|| ...),
"NodeInterfaces class does not contain given NodeInterfaceT");
return std::get<std::shared_ptr<NodeInterfaceT>>(interfaces_);
}
protected:
std::tuple<std::shared_ptr<InterfaceTs>...> interfaces_;
};
/// Prototype of NodeInterfacesSupports.
/**
* Should read NodeInterfacesSupports<..., T, ...> as "NodeInterfaces supports T", and
* if NodeInterfacesSupport is specialized for T, the is_supported should be
* set to std::true_type, but by default it is std::false_type, which will
* lead to a compiler error when trying to use T with NodeInterfaces.
*/
template<typename StorageClassT, typename ... Ts>
struct NodeInterfacesSupports;
/// Prototype of NodeInterfacesSupportCheck template meta-function.
/**
* This meta-function checks that all the types given are supported,
* throwing a more human-readable error if an unsupported type is used.
*/
template<typename StorageClassT, typename ... InterfaceTs>
struct NodeInterfacesSupportCheck;
/// Iterating specialization that ensures classes are supported and inherited.
template<typename StorageClassT, typename NextInterfaceT, typename ... RemainingInterfaceTs>
struct NodeInterfacesSupportCheck<StorageClassT, NextInterfaceT, RemainingInterfaceTs ...>
: public NodeInterfacesSupportCheck<StorageClassT, RemainingInterfaceTs ...>
{
static_assert(
NodeInterfacesSupports<StorageClassT, NextInterfaceT>::is_supported::value,
"given NodeInterfaceT is not supported by rclcpp::node_interfaces::NodeInterfaces");
};
/// Terminating case when there are no more "RemainingInterfaceTs".
template<typename StorageClassT>
struct NodeInterfacesSupportCheck<StorageClassT>
{};
/// Default specialization, needs to be specialized for each supported interface.
template<typename StorageClassT, typename ... RemainingInterfaceTs>
struct NodeInterfacesSupports
{
// Specializations need to set this to std::true_type in addition to other interfaces.
using is_supported = std::false_type;
};
/// Terminating specialization of NodeInterfacesSupports.
template<typename StorageClassT>
struct NodeInterfacesSupports<StorageClassT>
: public StorageClassT
{
/// Perfect forwarding constructor to get arguments down to StorageClassT.
template<typename ... ArgsT>
explicit NodeInterfacesSupports(ArgsT && ... args)
: StorageClassT(std::forward<ArgsT>(args) ...)
{}
};
// Helper functions to initialize the tuple in NodeInterfaces.
template<typename StorageClassT, typename ElementT, typename TupleT, typename NodeT>
void
init_element(TupleT & t, NodeT & n)
{
std::get<std::shared_ptr<ElementT>>(t) =
NodeInterfacesSupports<StorageClassT, ElementT>::get_from_node_like(n);
}
template<typename NodeT, typename ... Ts>
std::tuple<std::shared_ptr<Ts>...>
init_tuple(NodeT & n)
{
using StorageClassT = NodeInterfacesStorage<Ts ...>;
std::tuple<std::shared_ptr<Ts>...> t;
(init_element<StorageClassT, Ts>(t, n), ...);
return t;
}
/// Macro for creating specializations with less boilerplate.
/**
* You can use this macro to add support for your interface class if:
*
* - The standard getter is get_node_{NodeInterfaceName}_interface(), and
* - the getter returns a non-const shared_ptr<{NodeInterfaceType}>
*
* Examples of using this can be seen in the standard node interface headers
* in rclcpp, e.g. rclcpp/node_interfaces/node_base_interface.hpp has:
*
* RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base)
*
* If your interface has a non-standard getter, or you want to instrument it or
* something like that, then you'll need to create your own specialization of
* the NodeInterfacesSupports struct without this macro.
*/
#define RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(NodeInterfaceType, NodeInterfaceName) \
namespace rclcpp::node_interfaces::detail { \
template<typename StorageClassT, typename ... RemainingInterfaceTs> \
struct NodeInterfacesSupports< \
StorageClassT, \
NodeInterfaceType, \
RemainingInterfaceTs ...> \
: public NodeInterfacesSupports<StorageClassT, RemainingInterfaceTs ...> \
{ \
using is_supported = std::true_type; \
\
template<typename NodeT> \
static \
std::shared_ptr<NodeInterfaceType> \
get_from_node_like(NodeT & node_like) \
{ \
return node_like.get_node_ ## NodeInterfaceName ## _interface(); \
} \
\
/* Perfect forwarding constructor to get arguments down to StorageClassT (eventually). */ \
template<typename ... ArgsT> \
explicit NodeInterfacesSupports(ArgsT && ... args) \
: NodeInterfacesSupports<StorageClassT, RemainingInterfaceTs ...>( \
std::forward<ArgsT>(args) ...) \
{} \
\
std::shared_ptr<NodeInterfaceType> \
get_node_ ## NodeInterfaceName ## _interface() \
{ \
return StorageClassT::template get<NodeInterfaceType>(); \
} \
}; \
} // namespace rclcpp::node_interfaces::detail
} // namespace detail
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__DETAIL__NODE_INTERFACES_HELPERS_HPP_

View File

@@ -15,7 +15,7 @@
#ifndef RCLCPP__NODE_INTERFACES__NODE_BASE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_BASE_HPP_
#include <functional>
#include <atomic>
#include <memory>
#include <mutex>
#include <string>
@@ -39,6 +39,13 @@ class NodeBase : public NodeBaseInterface, public std::enable_shared_from_this<N
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBase)
/// Constructor.
/**
* If nullptr (default) is given for the default_callback_group, one will
* be created by the constructor using the create_callback_group() method,
* but virtual dispatch will not occur so overrides of that method will not
* be used.
*/
RCLCPP_PUBLIC
NodeBase(
const std::string & node_name,
@@ -46,7 +53,8 @@ public:
rclcpp::Context::SharedPtr context,
const rcl_node_options_t & rcl_node_options,
bool use_intra_process_default,
bool enable_topic_statistics_default);
bool enable_topic_statistics_default,
rclcpp::CallbackGroup::SharedPtr default_callback_group = nullptr);
RCLCPP_PUBLIC
virtual
@@ -113,10 +121,19 @@ public:
std::atomic_bool &
get_associated_with_executor_atomic() override;
[[deprecated("Use get_shared_notify_guard_condition or trigger_notify_guard_condition instead")]]
RCLCPP_PUBLIC
rclcpp::GuardCondition &
get_notify_guard_condition() override;
RCLCPP_PUBLIC
rclcpp::GuardCondition::SharedPtr
get_shared_notify_guard_condition() override;
RCLCPP_PUBLIC
void
trigger_notify_guard_condition() override;
RCLCPP_PUBLIC
bool
get_use_intra_process_default() const override;
@@ -145,7 +162,7 @@ private:
/// Guard condition for notifying the Executor of changes to this node.
mutable std::recursive_mutex notify_guard_condition_mutex_;
rclcpp::GuardCondition notify_guard_condition_;
std::shared_ptr<rclcpp::GuardCondition> notify_guard_condition_;
bool notify_guard_condition_is_valid_;
};

View File

@@ -15,10 +15,10 @@
#ifndef RCLCPP__NODE_INTERFACES__NODE_BASE_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_BASE_INTERFACE_HPP_
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "rcl/node.h"
@@ -26,6 +26,7 @@
#include "rclcpp/context.hpp"
#include "rclcpp/guard_condition.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -147,13 +148,33 @@ public:
/**
* For example, this should be notified when a publisher is added or removed.
*
* \return the GuardCondition if it is valid, else thow runtime error
* \return the GuardCondition if it is valid, else throw runtime error
*/
RCLCPP_PUBLIC
virtual
rclcpp::GuardCondition &
get_notify_guard_condition() = 0;
/// Return a guard condition that should be notified when the internal node state changes.
/**
* For example, this should be notified when a publisher is added or removed.
*
* \return the GuardCondition if it is valid, else nullptr
*/
RCLCPP_PUBLIC
virtual
rclcpp::GuardCondition::SharedPtr
get_shared_notify_guard_condition() = 0;
/// Trigger the guard condition that notifies of internal node state changes.
/**
* For example, this should be notified when a publisher is added or removed.
*/
RCLCPP_PUBLIC
virtual
void
trigger_notify_guard_condition() = 0;
/// Return the default preference for using intra process communication.
RCLCPP_PUBLIC
virtual
@@ -177,4 +198,6 @@ public:
} // namespace node_interfaces
} // namespace rclcpp
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base)
#endif // RCLCPP__NODE_INTERFACES__NODE_BASE_INTERFACE_HPP_

View File

@@ -15,6 +15,7 @@
#ifndef RCLCPP__NODE_INTERFACES__NODE_CLOCK_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_CLOCK_HPP_
#include "rcl/time.h"
#include "rclcpp/clock.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
@@ -42,7 +43,8 @@ public:
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging);
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
rcl_clock_type_t clock_type);
RCLCPP_PUBLIC
virtual
@@ -67,7 +69,7 @@ private:
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
rclcpp::Clock::SharedPtr ros_clock_;
rclcpp::Clock::SharedPtr clock_;
};
} // namespace node_interfaces

View File

@@ -17,6 +17,7 @@
#include "rclcpp/clock.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -50,4 +51,6 @@ public:
} // namespace node_interfaces
} // namespace rclcpp
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeClockInterface, clock)
#endif // RCLCPP__NODE_INTERFACES__NODE_CLOCK_INTERFACE_HPP_

View File

@@ -15,7 +15,9 @@
#ifndef RCLCPP__NODE_INTERFACES__NODE_GRAPH_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_GRAPH_HPP_
#include <atomic>
#include <chrono>
#include <condition_variable>
#include <map>
#include <memory>
#include <mutex>

View File

@@ -29,6 +29,7 @@
#include "rclcpp/event.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -56,7 +57,8 @@ public:
node_namespace_(info.node_namespace),
topic_type_(info.topic_type),
endpoint_type_(static_cast<rclcpp::EndpointType>(info.endpoint_type)),
qos_profile_({info.qos_profile.history, info.qos_profile.depth}, info.qos_profile)
qos_profile_({info.qos_profile.history, info.qos_profile.depth}, info.qos_profile),
topic_type_hash_(info.topic_type_hash)
{
std::copy(info.endpoint_gid, info.endpoint_gid + RMW_GID_STORAGE_SIZE, endpoint_gid_.begin());
}
@@ -121,6 +123,16 @@ public:
const rclcpp::QoS &
qos_profile() const;
/// Get a mutable reference to the type hash of the topic endpoint.
RCLCPP_PUBLIC
rosidl_type_hash_t &
topic_type_hash();
/// Get a const reference to the type hash of the topic endpoint.
RCLCPP_PUBLIC
const rosidl_type_hash_t &
topic_type_hash() const;
private:
std::string node_name_;
std::string node_namespace_;
@@ -128,6 +140,7 @@ private:
rclcpp::EndpointType endpoint_type_;
std::array<uint8_t, RMW_GID_STORAGE_SIZE> endpoint_gid_;
rclcpp::QoS qos_profile_;
rosidl_type_hash_t topic_type_hash_;
};
namespace node_interfaces
@@ -357,6 +370,8 @@ public:
/// Return the topic endpoint information about publishers on a given topic.
/**
* \param[in] topic_name the actual topic name used; it will not be automatically remapped.
* \param[in] no_mangle if `true`, `topic_name` needs to be a valid middleware topic name,
* otherwise it should be a valid ROS topic name.
* \sa rclcpp::Node::get_publishers_info_by_topic
*/
RCLCPP_PUBLIC
@@ -367,6 +382,8 @@ public:
/// Return the topic endpoint information about subscriptions on a given topic.
/**
* \param[in] topic_name the actual topic name used; it will not be automatically remapped.
* \param[in] no_mangle if `true`, `topic_name` needs to be a valid middleware topic name,
* otherwise it should be a valid ROS topic name.
* \sa rclcpp::Node::get_subscriptions_info_by_topic
*/
RCLCPP_PUBLIC
@@ -378,4 +395,6 @@ public:
} // namespace node_interfaces
} // namespace rclcpp
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeGraphInterface, graph)
#endif // RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_

View File

@@ -0,0 +1,164 @@
// Copyright 2022 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__NODE_INTERFACES__NODE_INTERFACES_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_INTERFACES_HPP_
#include <memory>
#include "rclcpp/detail/template_unique.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#define ALL_RCLCPP_NODE_INTERFACES \
rclcpp::node_interfaces::NodeBaseInterface, \
rclcpp::node_interfaces::NodeClockInterface, \
rclcpp::node_interfaces::NodeGraphInterface, \
rclcpp::node_interfaces::NodeLoggingInterface, \
rclcpp::node_interfaces::NodeParametersInterface, \
rclcpp::node_interfaces::NodeServicesInterface, \
rclcpp::node_interfaces::NodeTimeSourceInterface, \
rclcpp::node_interfaces::NodeTimersInterface, \
rclcpp::node_interfaces::NodeTopicsInterface, \
rclcpp::node_interfaces::NodeWaitablesInterface
namespace rclcpp
{
namespace node_interfaces
{
/// A helper class for aggregating node interfaces.
template<typename ... InterfaceTs>
class NodeInterfaces
: public detail::NodeInterfacesSupportCheck<
detail::NodeInterfacesStorage<InterfaceTs ...>,
InterfaceTs ...
>,
public detail::NodeInterfacesSupports<
detail::NodeInterfacesStorage<InterfaceTs ...>,
InterfaceTs ...
>
{
static_assert(
0 != sizeof ...(InterfaceTs),
"must provide at least one interface as a template argument");
static_assert(
rclcpp::detail::template_unique_v<InterfaceTs ...>,
"must provide unique template parameters");
using NodeInterfacesSupportsT = detail::NodeInterfacesSupports<
detail::NodeInterfacesStorage<InterfaceTs ...>,
InterfaceTs ...
>;
public:
/// Create a new NodeInterfaces object using the given node-like object's interfaces.
/**
* Specify which interfaces you need by passing them as template parameters.
*
* This allows you to aggregate interfaces from different sources together to pass as a single
* aggregate object to any functions that take node interfaces or node-likes, without needing to
* templatize that function.
*
* You may also use this constructor to create a NodeInterfaces that contains a subset of
* another NodeInterfaces' interfaces.
*
* Finally, this class supports implicit conversion from node-like objects, allowing you to
* directly pass a node-like to a function that takes a NodeInterfaces object.
*
* Usage examples:
* ```cpp
* // Suppose we have some function:
* void fn(NodeInterfaces<NodeBaseInterface, NodeClockInterface> interfaces);
*
* // Then we can, explicitly:
* rclcpp::Node node("some_node");
* auto ni = NodeInterfaces<NodeBaseInterface, NodeClockInterface>(node);
* fn(ni);
*
* // But also:
* fn(node);
*
* // Subsetting a NodeInterfaces object also works!
* auto ni_base = NodeInterfaces<NodeBaseInterface>(ni);
*
* // Or aggregate them (you could aggregate interfaces from disparate node-likes)
* auto ni_aggregated = NodeInterfaces<NodeBaseInterface, NodeClockInterface>(
* node->get_node_base_interface(),
* node->get_node_clock_interface()
* )
*
* // And then to access the interfaces:
* // Get with get<>
* auto base = ni.get<NodeBaseInterface>();
*
* // Or the appropriate getter
* auto clock = ni.get_clock_interface();
* ```
*
* You may use any of the standard node interfaces that come with rclcpp:
* - rclcpp::node_interfaces::NodeBaseInterface
* - rclcpp::node_interfaces::NodeClockInterface
* - rclcpp::node_interfaces::NodeGraphInterface
* - rclcpp::node_interfaces::NodeLoggingInterface
* - rclcpp::node_interfaces::NodeParametersInterface
* - rclcpp::node_interfaces::NodeServicesInterface
* - rclcpp::node_interfaces::NodeTimeSourceInterface
* - rclcpp::node_interfaces::NodeTimersInterface
* - rclcpp::node_interfaces::NodeTopicsInterface
* - rclcpp::node_interfaces::NodeWaitablesInterface
*
* Or you use custom interfaces as long as you make a template specialization
* of the rclcpp::node_interfaces::detail::NodeInterfacesSupport struct using
* the RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT macro.
*
* Usage example:
* ```RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeBaseInterface, base)```
*
* If you choose not to use the helper macro, then you can specialize the
* template yourself, but you must:
*
* - Provide a template specialization of the get_from_node_like method that gets the interface
* from any node-like that stores the interface, using the node-like's getter
* - Designate the is_supported type as std::true_type using a using directive
* - Provide any number of getter methods to be used to obtain the interface with the
* NodeInterface object, noting that the getters of the storage class will apply to all
* supported interfaces.
* - The getter method names should not clash in name with any other interface getter
* specializations if those other interfaces are meant to be aggregated in the same
* NodeInterfaces object.
*
* \param[in] node Node-like object from which to get the node interfaces
*/
template<typename NodeT>
NodeInterfaces(NodeT & node) // NOLINT(runtime/explicit)
: NodeInterfacesSupportsT(node)
{}
// Create a NodeInterfaces object with no bound interfaces
NodeInterfaces()
: NodeInterfacesSupportsT()
{}
explicit NodeInterfaces(std::shared_ptr<InterfaceTs>... args)
: NodeInterfacesSupportsT(args ...)
{}
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_INTERFACES_HPP_

View File

@@ -19,6 +19,7 @@
#include "rclcpp/logger.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -37,14 +38,18 @@ public:
~NodeLoggingInterface() = default;
/// Return the logger of the node.
/** \return The logger of the node. */
/**
* \return The logger of the node.
*/
RCLCPP_PUBLIC
virtual
rclcpp::Logger
get_logger() const = 0;
/// Return the logger name associated with the node.
/** \return The logger name associated with the node. */
/**
* \return The logger name associated with the node.
*/
RCLCPP_PUBLIC
virtual
const char *
@@ -54,4 +59,6 @@ public:
} // namespace node_interfaces
} // namespace rclcpp
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeLoggingInterface, logging)
#endif // RCLCPP__NODE_INTERFACES__NODE_LOGGING_INTERFACE_HPP_

View File

@@ -15,9 +15,10 @@
#ifndef RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_HPP_
#include <list>
#include <map>
#include <memory>
#include <list>
#include <mutex>
#include <string>
#include <vector>
@@ -84,6 +85,16 @@ class NodeParameters : public NodeParametersInterface
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeParameters)
/// Constructor.
/**
* If using automatically_declare_parameters_from_overrides, overrides of
* get_parameter_overrides(), has_parameter(), declare_parameter() will not
* be respected.
* If this is an issue, pass false for
* automatically_declare_parameters_from_overrides and invoke
* perform_automatically_declare_parameters_from_overrides() manually after
* construction.
*/
RCLCPP_PUBLIC
NodeParameters(
const node_interfaces::NodeBaseInterface::SharedPtr node_base,
@@ -103,25 +114,6 @@ public:
virtual
~NodeParameters();
// This is overriding a deprecated method, so we need to ignore the deprecation warning here.
// Users of the method will still get a warning!
#ifndef _WIN32
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wdeprecated-declarations"
#else
# pragma warning(push)
# pragma warning(disable: 4996)
#endif
[[deprecated(RCLCPP_INTERNAL_NODE_PARAMETERS_INTERFACE_DEPRECATE_DECLARE)]]
RCLCPP_PUBLIC
const rclcpp::ParameterValue &
declare_parameter(const std::string & name) override;
#ifndef _WIN32
# pragma GCC diagnostic pop
#else
# pragma warning(pop)
#endif
RCLCPP_PUBLIC
const rclcpp::ParameterValue &
declare_parameter(
@@ -190,20 +182,48 @@ public:
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const override;
RCLCPP_PUBLIC
RCUTILS_WARN_UNUSED
PreSetParametersCallbackHandle::SharedPtr
add_pre_set_parameters_callback(PreSetParametersCallbackType callback) override;
RCLCPP_PUBLIC
RCUTILS_WARN_UNUSED
OnSetParametersCallbackHandle::SharedPtr
add_on_set_parameters_callback(OnParametersSetCallbackType callback) override;
add_on_set_parameters_callback(OnSetParametersCallbackType callback) override;
RCLCPP_PUBLIC
RCUTILS_WARN_UNUSED
PostSetParametersCallbackHandle::SharedPtr
add_post_set_parameters_callback(PostSetParametersCallbackType callback) override;
RCLCPP_PUBLIC
void
remove_on_set_parameters_callback(const OnSetParametersCallbackHandle * const handler) override;
RCLCPP_PUBLIC
void
remove_post_set_parameters_callback(const PostSetParametersCallbackHandle * const handler)
override;
RCLCPP_PUBLIC
void
remove_pre_set_parameters_callback(const PreSetParametersCallbackHandle * const handler) override;
RCLCPP_PUBLIC
const std::map<std::string, rclcpp::ParameterValue> &
get_parameter_overrides() const override;
using CallbacksContainerType = std::list<OnSetParametersCallbackHandle::WeakPtr>;
using PreSetCallbacksHandleContainer = std::list<PreSetParametersCallbackHandle::WeakPtr>;
using OnSetCallbacksHandleContainer = std::list<OnSetParametersCallbackHandle::WeakPtr>;
using PostSetCallbacksHandleContainer = std::list<PostSetParametersCallbackHandle::WeakPtr>;
using CallbacksContainerType [[deprecated("use OnSetCallbacksHandleContainer instead")]] =
OnSetCallbacksHandleContainer;
protected:
RCLCPP_PUBLIC
void
perform_automatically_declare_parameters_from_overrides();
private:
RCLCPP_DISABLE_COPY(NodeParameters)
@@ -215,9 +235,11 @@ private:
// declare_parameter, etc). In those cases, this will be set to false.
bool parameter_modification_enabled_{true};
OnParametersSetCallbackType on_parameters_set_callback_ = nullptr;
PreSetCallbacksHandleContainer pre_set_parameters_callback_container_;
CallbacksContainerType on_parameters_set_callback_container_;
OnSetCallbacksHandleContainer on_set_parameters_callback_container_;
PostSetCallbacksHandleContainer post_set_parameters_callback_container_;
std::map<std::string, ParameterInfo> parameters_;

View File

@@ -15,8 +15,8 @@
#ifndef RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_
#include <functional>
#include <map>
#include <memory>
#include <string>
#include <vector>
@@ -25,6 +25,7 @@
#include "rcl_interfaces/msg/set_parameters_result.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -33,28 +34,39 @@ namespace rclcpp
namespace node_interfaces
{
struct PreSetParametersCallbackHandle
{
RCLCPP_SMART_PTR_DEFINITIONS(PreSetParametersCallbackHandle)
using PreSetParametersCallbackType =
std::function<void (std::vector<rclcpp::Parameter> &)>;
PreSetParametersCallbackType callback;
};
struct OnSetParametersCallbackHandle
{
RCLCPP_SMART_PTR_DEFINITIONS(OnSetParametersCallbackHandle)
using OnParametersSetCallbackType =
using OnSetParametersCallbackType =
std::function<
rcl_interfaces::msg::SetParametersResult(
const std::vector<rclcpp::Parameter> &)>;
using OnParametersSetCallbackType [[deprecated("use OnSetParametersCallbackType instead")]] =
OnSetParametersCallbackType;
OnParametersSetCallbackType callback;
OnSetParametersCallbackType callback;
};
#define RCLCPP_INTERNAL_NODE_PARAMETERS_INTERFACE_DEPRECATE_DECLARE \
"declare_parameter() with only a name is deprecated and will be deleted in the future.\n" \
"If you want to declare a parameter that won't change type without a default value use:\n" \
"`node_params->declare_parameter(name, type)`, with e.g. type=rclcpp::PARAMETER_INTEGER.\n\n" \
"If you want to declare a parameter that can dynamically change type use:\n" \
"```\n" \
"rcl_interfaces::msg::ParameterDescriptor descriptor;\n" \
"descriptor.dynamic_typing = true;\n" \
"node_params->declare_parameter(name, rclcpp::ParameterValue{}, descriptor);\n" \
"```"
struct PostSetParametersCallbackHandle
{
RCLCPP_SMART_PTR_DEFINITIONS(PostSetParametersCallbackHandle)
using PostSetParametersCallbackType =
std::function<void (const std::vector<rclcpp::Parameter> &)>;
PostSetParametersCallbackType callback;
};
/// Pure virtual interface class for the NodeParameters part of the Node API.
class NodeParametersInterface
@@ -66,15 +78,6 @@ public:
virtual
~NodeParametersInterface() = default;
/// Declare a parameter.
/**
* \sa rclcpp::Node::declare_parameter
*/
[[deprecated(RCLCPP_INTERNAL_NODE_PARAMETERS_INTERFACE_DEPRECATE_DECLARE)]]
virtual
const rclcpp::ParameterValue &
declare_parameter(const std::string & name) = 0;
/// Declare and initialize a parameter.
/**
* \sa rclcpp::Node::declare_parameter
@@ -205,16 +208,46 @@ public:
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const = 0;
using OnParametersSetCallbackType = OnSetParametersCallbackHandle::OnParametersSetCallbackType;
using OnSetParametersCallbackType = OnSetParametersCallbackHandle::OnSetParametersCallbackType;
using PostSetParametersCallbackType =
PostSetParametersCallbackHandle::PostSetParametersCallbackType;
using PreSetParametersCallbackType = PreSetParametersCallbackHandle::PreSetParametersCallbackType;
/// Add a callback for when parameters are being set.
/// Add a callback that gets triggered before parameters are validated.
/**
* \sa rclcpp::Node::add_pre_set_parameters_callback
*/
RCLCPP_PUBLIC
virtual
PreSetParametersCallbackHandle::SharedPtr
add_pre_set_parameters_callback(PreSetParametersCallbackType callback) = 0;
/// Add a callback to validate parameters before they are set.
/**
* \sa rclcpp::Node::add_on_set_parameters_callback
*/
RCLCPP_PUBLIC
virtual
OnSetParametersCallbackHandle::SharedPtr
add_on_set_parameters_callback(OnParametersSetCallbackType callback) = 0;
add_on_set_parameters_callback(OnSetParametersCallbackType callback) = 0;
/// Add a callback that gets triggered after parameters are set successfully.
/**
* \sa rclcpp::Node::add_post_set_parameters_callback
*/
RCLCPP_PUBLIC
virtual
PostSetParametersCallbackHandle::SharedPtr
add_post_set_parameters_callback(PostSetParametersCallbackType callback) = 0;
/// Remove a callback registered with `add_pre_set_parameters_callback`.
/**
* \sa rclcpp::Node::remove_pre_set_parameters_callback
*/
RCLCPP_PUBLIC
virtual
void
remove_pre_set_parameters_callback(const PreSetParametersCallbackHandle * const handler) = 0;
/// Remove a callback registered with `add_on_set_parameters_callback`.
/**
@@ -225,6 +258,15 @@ public:
void
remove_on_set_parameters_callback(const OnSetParametersCallbackHandle * const handler) = 0;
/// Remove a callback registered with `add_post_set_parameters_callback`.
/**
* \sa rclcpp::Node::remove_post_set_parameters_callback
*/
RCLCPP_PUBLIC
virtual
void
remove_post_set_parameters_callback(const PostSetParametersCallbackHandle * const handler) = 0;
/// Return the initial parameter values used by the NodeParameters to override default values.
RCLCPP_PUBLIC
virtual
@@ -235,4 +277,6 @@ public:
} // namespace node_interfaces
} // namespace rclcpp
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeParametersInterface, parameters)
#endif // RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_

View File

@@ -20,6 +20,7 @@
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -62,4 +63,6 @@ public:
} // namespace node_interfaces
} // namespace rclcpp
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeServicesInterface, services)
#endif // RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_

View File

@@ -16,6 +16,7 @@
#define RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_INTERFACE_HPP_
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
@@ -37,4 +38,6 @@ public:
} // namespace node_interfaces
} // namespace rclcpp
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeTimeSourceInterface, time_source)
#endif // RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_INTERFACE_HPP_

View File

@@ -17,6 +17,7 @@
#include "rclcpp/callback_group.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/timer.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -47,4 +48,6 @@ public:
} // namespace node_interfaces
} // namespace rclcpp
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeTimersInterface, timers)
#endif // RCLCPP__NODE_INTERFACES__NODE_TIMERS_INTERFACE_HPP_

View File

@@ -20,12 +20,16 @@
#include "rcl/publisher.h"
#include "rcl/subscription.h"
#include "rclcpp/callback_group.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/publisher_base.hpp"
#include "rclcpp/publisher_factory.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/subscription_base.hpp"
#include "rclcpp/subscription_factory.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp

View File

@@ -15,8 +15,6 @@
#ifndef RCLCPP__NODE_INTERFACES__NODE_TOPICS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_TOPICS_INTERFACE_HPP_
#include <functional>
#include <memory>
#include <string>
#include "rcl/publisher.h"
@@ -24,6 +22,7 @@
#include "rclcpp/callback_group.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
#include "rclcpp/publisher.hpp"
@@ -97,4 +96,6 @@ public:
} // namespace node_interfaces
} // namespace rclcpp
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeTopicsInterface, topics)
#endif // RCLCPP__NODE_INTERFACES__NODE_TOPICS_INTERFACE_HPP_

View File

@@ -17,6 +17,7 @@
#include "rclcpp/callback_group.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/detail/node_interfaces_helpers.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/waitable.hpp"
@@ -54,4 +55,6 @@ public:
} // namespace node_interfaces
} // namespace rclcpp
RCLCPP_NODE_INTERFACE_HELPERS_SUPPORT(rclcpp::node_interfaces::NodeWaitablesInterface, waitables)
#endif // RCLCPP__NODE_INTERFACES__NODE_WAITABLES_INTERFACE_HPP_

View File

@@ -19,6 +19,7 @@
#include <string>
#include <vector>
#include "rcl/time.h"
#include "rcl/node_options.h"
#include "rclcpp/context.hpp"
#include "rclcpp/contexts/default_context.hpp"
@@ -46,6 +47,7 @@ public:
* - enable_topic_statistics = false
* - start_parameter_services = true
* - start_parameter_event_publisher = true
* - clock_type = RCL_ROS_TIME
* - clock_qos = rclcpp::ClockQoS()
* - use_clock_thread = true
* - rosout_qos = rclcpp::RosoutQoS()
@@ -246,6 +248,19 @@ public:
NodeOptions &
start_parameter_event_publisher(bool start_parameter_event_publisher);
/// Return a reference to the clock type.
RCLCPP_PUBLIC
const rcl_clock_type_t &
clock_type() const;
/// Set the clock type.
/**
* The clock type to be used by the node.
*/
RCLCPP_PUBLIC
NodeOptions &
clock_type(const rcl_clock_type_t & clock_type);
/// Return a reference to the clock QoS.
RCLCPP_PUBLIC
const rclcpp::QoS &
@@ -400,6 +415,8 @@ private:
bool start_parameter_event_publisher_ {true};
rcl_clock_type_t clock_type_ {RCL_ROS_TIME};
rclcpp::QoS clock_qos_ = rclcpp::ClockQoS();
bool use_clock_thread_ {true};

View File

@@ -264,6 +264,7 @@ get_value_helper<rclcpp::Parameter>(const rclcpp::Parameter * parameter)
} // namespace detail
/// \cond
template<typename T>
decltype(auto)
Parameter::get_value() const
@@ -275,6 +276,7 @@ Parameter::get_value() const
throw exceptions::InvalidParameterTypeException(this->name_, ex.what());
}
}
/// \endcond
} // namespace rclcpp

View File

@@ -39,6 +39,7 @@
#include "rclcpp/node.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/parameter_map.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/rmw.h"
@@ -51,6 +52,37 @@ class AsyncParametersClient
public:
RCLCPP_SMART_PTR_DEFINITIONS(AsyncParametersClient)
/// Create an async parameters client.
/**
* \param[in] node_base_interface The node base interface of the corresponding node.
* \param[in] node_topics_interface Node topic base interface.
* \param[in] node_graph_interface The node graph interface of the corresponding node.
* \param[in] node_services_interface Node service interface.
* \param[in] remote_node_name Name of the remote node
* \param[in] qos_profile The rmw qos profile to use to subscribe
* \param[in] group (optional) The async parameter client will be added to this callback group.
* \deprecated use rclcpp::QoS instead of rmw_qos_profile_t
*/
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
RCLCPP_PUBLIC
AsyncParametersClient(
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
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,
rclcpp::CallbackGroup::SharedPtr group = nullptr)
: AsyncParametersClient(
node_base_interface,
node_topics_interface,
node_graph_interface,
node_services_interface,
remote_node_name,
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)),
group)
{}
/// Create an async parameters client.
/**
* \param[in] node_base_interface The node base interface of the corresponding node.
@@ -58,7 +90,7 @@ public:
* \param[in] node_graph_interface The node graph interface of the corresponding node.
* \param[in] node_services_interface Node service interface.
* \param[in] remote_node_name (optional) name of the remote node
* \param[in] qos_profile (optional) The rmw qos profile to use to subscribe
* \param[in] qos_profile (optional) The qos profile to use to subscribe
* \param[in] group (optional) The async parameter client will be added to this callback group.
*/
RCLCPP_PUBLIC
@@ -68,21 +100,45 @@ 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 rclcpp::QoS & qos_profile = rclcpp::ParametersQoS(),
rclcpp::CallbackGroup::SharedPtr group = nullptr);
/// Constructor
/**
* \param[in] node The async paramters client will be added to this node.
* \param[in] node The async parameters client will be added to this node.
* \param[in] remote_node_name name of the remote node
* \param[in] qos_profile The rmw qos profile to use to subscribe
* \param[in] group (optional) The async parameter client will be added to this callback group.
* \deprecated use rclcpp::QoS instead of rmw_qos_profile_t
*/
template<typename NodeT>
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
AsyncParametersClient(
const std::shared_ptr<NodeT> node,
const std::string & remote_node_name,
const rmw_qos_profile_t & qos_profile,
rclcpp::CallbackGroup::SharedPtr group = nullptr)
: AsyncParametersClient(
node->get_node_base_interface(),
node->get_node_topics_interface(),
node->get_node_graph_interface(),
node->get_node_services_interface(),
remote_node_name,
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)),
group)
{}
/**
* \param[in] node The async parameters client will be added to this node.
* \param[in] remote_node_name (optional) name of the remote node
* \param[in] qos_profile (optional) The rmw qos profile to use to subscribe
* \param[in] qos_profile (optional) The qos profile to use to subscribe
* \param[in] group (optional) The async parameter client will be added to this callback group.
*/
template<typename NodeT>
AsyncParametersClient(
explicit AsyncParametersClient(
const std::shared_ptr<NodeT> node,
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters,
const rclcpp::QoS & qos_profile = rclcpp::ParametersQoS(),
rclcpp::CallbackGroup::SharedPtr group = nullptr)
: AsyncParametersClient(
node->get_node_base_interface(),
@@ -96,16 +152,40 @@ public:
/// Constructor
/**
* \param[in] node The async paramters client will be added to this node.
* \param[in] node The async parameters client will be added to this node.
* \param[in] remote_node_name Name of the remote node
* \param[in] qos_profile The rmw qos profile to use to subscribe
* \param[in] group (optional) The async parameter client will be added to this callback group.
* \deprecated use rclcpp::QoS instead of rmw_qos_profile_t
*/
template<typename NodeT>
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
AsyncParametersClient(
NodeT * node,
const std::string & remote_node_name,
const rmw_qos_profile_t & qos_profile,
rclcpp::CallbackGroup::SharedPtr group = nullptr)
: AsyncParametersClient(
node->get_node_base_interface(),
node->get_node_topics_interface(),
node->get_node_graph_interface(),
node->get_node_services_interface(),
remote_node_name,
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)),
group)
{}
/**
* \param[in] node The async parameters client will be added to this node.
* \param[in] remote_node_name (optional) name of the remote node
* \param[in] qos_profile (optional) The rmw qos profile to use to subscribe
* \param[in] qos_profile (optional) The qos profile to use to subscribe
* \param[in] group (optional) The async parameter client will be added to this callback group.
*/
template<typename NodeT>
AsyncParametersClient(
explicit AsyncParametersClient(
NodeT * node,
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters,
const rclcpp::QoS & qos_profile = rclcpp::ParametersQoS(),
rclcpp::CallbackGroup::SharedPtr group = nullptr)
: AsyncParametersClient(
node->get_node_base_interface(),
@@ -185,7 +265,10 @@ public:
/**
* This function filters the parameters to be set based on the node name.
*
* \param yaml_filename the full name of the yaml file
* If two duplicate keys exist in node names belongs to one FQN, there is no guarantee
* which one could be set.
*
* \param parameter_map named parameters to be loaded
* \return the future of the set_parameter service used to load the parameters
* \throw InvalidParametersException if there is no parameter to set
*/
@@ -208,9 +291,7 @@ public:
typename rclcpp::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
on_parameter_event(
CallbackT && callback,
const rclcpp::QoS & qos = (
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events))
),
const rclcpp::QoS & qos = rclcpp::ParameterEventsQoS(),
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
))
@@ -235,9 +316,7 @@ public:
on_parameter_event(
NodeT && node,
CallbackT && callback,
const rclcpp::QoS & qos = (
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events))
),
const rclcpp::QoS & qos = rclcpp::ParameterEventsQoS(),
const rclcpp::SubscriptionOptionsWithAllocator<AllocatorT> & options = (
rclcpp::SubscriptionOptionsWithAllocator<AllocatorT>()
))
@@ -304,11 +383,24 @@ class SyncParametersClient
public:
RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient)
template<typename NodeT>
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
SyncParametersClient(
std::shared_ptr<NodeT> node,
const std::string & remote_node_name,
const rmw_qos_profile_t & qos_profile)
: SyncParametersClient(
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(),
node,
remote_node_name,
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)))
{}
template<typename NodeT>
explicit SyncParametersClient(
std::shared_ptr<NodeT> node,
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters)
const rclcpp::QoS & qos_profile = rclcpp::ParametersQoS())
: SyncParametersClient(
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(),
node,
@@ -316,12 +408,29 @@ public:
qos_profile)
{}
template<typename NodeT>
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
SyncParametersClient(
rclcpp::Executor::SharedPtr executor,
std::shared_ptr<NodeT> node,
const std::string & remote_node_name,
const rmw_qos_profile_t & qos_profile)
: SyncParametersClient(
executor,
node->get_node_base_interface(),
node->get_node_topics_interface(),
node->get_node_graph_interface(),
node->get_node_services_interface(),
remote_node_name,
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)))
{}
template<typename NodeT>
SyncParametersClient(
rclcpp::Executor::SharedPtr executor,
std::shared_ptr<NodeT> node,
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters)
const rclcpp::QoS & qos_profile = rclcpp::ParametersQoS())
: SyncParametersClient(
executor,
node->get_node_base_interface(),
@@ -333,10 +442,23 @@ public:
{}
template<typename NodeT>
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
SyncParametersClient(
NodeT * node,
const std::string & remote_node_name,
const rmw_qos_profile_t & qos_profile)
: SyncParametersClient(
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(),
node,
remote_node_name,
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)))
{}
template<typename NodeT>
explicit SyncParametersClient(
NodeT * node,
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters)
const rclcpp::QoS & qos_profile = rclcpp::ParametersQoS())
: SyncParametersClient(
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(),
node,
@@ -344,12 +466,29 @@ public:
qos_profile)
{}
template<typename NodeT>
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
SyncParametersClient(
rclcpp::Executor::SharedPtr executor,
NodeT * node,
const std::string & remote_node_name,
const rmw_qos_profile_t & qos_profile)
: SyncParametersClient(
executor,
node->get_node_base_interface(),
node->get_node_topics_interface(),
node->get_node_graph_interface(),
node->get_node_services_interface(),
remote_node_name,
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)))
{}
template<typename NodeT>
SyncParametersClient(
rclcpp::Executor::SharedPtr executor,
NodeT * node,
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters)
const rclcpp::QoS & qos_profile = rclcpp::ParametersQoS())
: SyncParametersClient(
executor,
node->get_node_base_interface(),
@@ -360,6 +499,28 @@ public:
qos_profile)
{}
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
RCLCPP_PUBLIC
SyncParametersClient(
rclcpp::Executor::SharedPtr executor,
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
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)
: executor_(executor), node_base_interface_(node_base_interface)
{
async_parameters_client_ =
std::make_shared<AsyncParametersClient>(
node_base_interface,
node_topics_interface,
node_graph_interface,
node_services_interface,
remote_node_name,
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)));
}
RCLCPP_PUBLIC
SyncParametersClient(
rclcpp::Executor::SharedPtr executor,
@@ -368,7 +529,7 @@ 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 rclcpp::QoS & qos_profile = rclcpp::ParametersQoS())
: executor_(executor), node_base_interface_(node_base_interface)
{
async_parameters_client_ =

View File

@@ -86,6 +86,9 @@ struct ParameterEventCallbackHandle
* the ROS node supplied in the ParameterEventHandler constructor.
* The callback, a lambda function in this case, simply prints out the value of the parameter.
*
* Note: the object returned from add_parameter_callback must be captured or the callback will
* be immediately unregistered.
*
* You may also monitor for changes to parameters in other nodes by supplying the node
* name to add_parameter_callback:
*
@@ -103,8 +106,8 @@ struct ParameterEventCallbackHandle
* In this case, the callback will be invoked whenever "some_remote_param_name" changes
* on remote node "some_remote_node_name".
*
* To remove a parameter callback, call remove_parameter_callback, passing the handle returned
* from add_parameter_callback:
* To remove a parameter callback, reset the callback handle smart pointer or call
* remove_parameter_callback, passing the handle returned from add_parameter_callback:
*
* param_handler->remove_parameter_callback(handle2);
*
@@ -152,9 +155,12 @@ struct ParameterEventCallbackHandle
* For both parameter callbacks and parameter event callbacks, when multiple callbacks are added,
* the callbacks are invoked last-in, first-called order (LIFO).
*
* To remove a parameter event callback, use:
* Note: the callback handle returned from add_parameter_event_callback must be captured or
* the callback will immediately be unregistered.
*
* param_handler->remove_event_parameter_callback(handle);
* To remove a parameter event callback, reset the callback smart pointer or use:
*
* param_handler->remove_event_parameter_callback(handle3);
*/
class ParameterEventHandler
{
@@ -165,7 +171,7 @@ public:
* \param[in] qos The QoS settings to use for any subscriptions.
*/
template<typename NodeT>
ParameterEventHandler(
explicit ParameterEventHandler(
NodeT node,
const rclcpp::QoS & qos =
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(rmw_qos_profile_parameter_events)))
@@ -189,10 +195,14 @@ public:
/**
* This function may be called multiple times to set multiple parameter event callbacks.
*
* Note: if the returned callback handle smart pointer is not captured, the callback is
* immediatedly unregistered. A compiler warning should be generated to warn of this.
*
* \param[in] callback Function callback to be invoked on parameter updates.
* \returns A handle used to refer to the callback.
*/
RCLCPP_PUBLIC
RCUTILS_WARN_UNUSED
ParameterEventCallbackHandle::SharedPtr
add_parameter_event_callback(
ParameterEventCallbackType callback);
@@ -212,12 +222,17 @@ public:
/**
* If a node_name is not provided, defaults to the current node.
*
* Note: if the returned callback handle smart pointer is not captured, the callback
* is immediately unregistered. A compiler warning should be generated to warn
* of this.
*
* \param[in] parameter_name Name of parameter to monitor.
* \param[in] callback Function callback to be invoked upon parameter update.
* \param[in] node_name Name of node which hosts the parameter.
* \returns A handle used to refer to the callback.
*/
RCLCPP_PUBLIC
RCUTILS_WARN_UNUSED
ParameterCallbackHandle::SharedPtr
add_parameter_callback(
const std::string & parameter_name,
@@ -268,6 +283,7 @@ public:
* \param[in] parameter_name Name of parameter.
* \param[in] node_name Name of node which hosts the parameter.
* \returns The resultant rclcpp::Parameter from the event.
* \throws std::runtime_error if input node name doesn't match the node name in parameter event.
*/
RCLCPP_PUBLIC
static rclcpp::Parameter

View File

@@ -35,11 +35,13 @@ using ParameterMap = std::unordered_map<std::string, std::vector<Parameter>>;
/// Convert parameters from rcl_yaml_param_parser into C++ class instances.
/// \param[in] c_params C structures containing parameters for multiple nodes.
/// \param[in] node_fqn a Fully Qualified Name of node, default value is nullptr.
/// If it's not nullptr, return the relative node parameters belonging to this node_fqn.
/// \returns a map where the keys are fully qualified node names and values a list of parameters.
/// \throws InvalidParametersException if the `rcl_params_t` is inconsistent or invalid.
RCLCPP_PUBLIC
ParameterMap
parameter_map_from(const rcl_params_t * const c_params);
parameter_map_from(const rcl_params_t * const c_params, const char * node_fqn = nullptr);
/// Convert parameter value from rcl_yaml_param_parser into a C++ class instance.
/// \param[in] c_value C structure containing a value of a parameter.
@@ -51,11 +53,20 @@ parameter_value_from(const rcl_variant_t * const c_value);
/// Get the ParameterMap from a yaml file.
/// \param[in] yaml_filename full name of the yaml file.
/// \param[in] node_fqn a Fully Qualified Name of node, default value is nullptr.
/// \returns an instance of a parameter map
/// \throws from rcl error of rcl_parse_yaml_file()
RCLCPP_PUBLIC
ParameterMap
parameter_map_from_yaml_file(const std::string & yaml_filename);
parameter_map_from_yaml_file(const std::string & yaml_filename, const char * node_fqn = nullptr);
/// Get the Parameters from ParameterMap.
/// \param[in] parameter_map a parameter map.
/// \param[in] node_fqn a Fully Qualified Name of node, default value is nullptr.
/// \returns a list of a parameter
RCLCPP_PUBLIC
std::vector<Parameter>
parameters_from_map(const ParameterMap & parameter_map, const char * node_fqn = nullptr);
} // namespace rclcpp

View File

@@ -28,6 +28,7 @@
#include "rclcpp/macros.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/rmw.h"
@@ -39,12 +40,26 @@ class ParameterService
public:
RCLCPP_SMART_PTR_DEFINITIONS(ParameterService)
[[deprecated("use rclcpp::QoS instead of rmw_qos_profile_t")]]
RCLCPP_PUBLIC
explicit ParameterService(
ParameterService(
const std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
const std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
rclcpp::node_interfaces::NodeParametersInterface * node_params,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
const rmw_qos_profile_t & qos_profile)
: ParameterService(
node_base,
node_services,
node_params,
rclcpp::QoS(rclcpp::QoSInitialization::from_rmw(qos_profile)))
{}
RCLCPP_PUBLIC
ParameterService(
const std::shared_ptr<node_interfaces::NodeBaseInterface> node_base,
const std::shared_ptr<node_interfaces::NodeServicesInterface> node_services,
rclcpp::node_interfaces::NodeParametersInterface * node_params,
const rclcpp::QoS & qos_profile = rclcpp::ParametersQoS());
private:
rclcpp::Service<rcl_interfaces::srv::GetParameters>::SharedPtr get_parameters_service_;

View File

@@ -20,12 +20,14 @@
#include <memory>
#include <sstream>
#include <string>
#include <type_traits>
#include <utility>
#include "rcl/error_handling.h"
#include "rcl/publisher.h"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
#include "rosidl_runtime_cpp/traits.hpp"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
@@ -81,7 +83,6 @@ public:
/// MessageT::custom_type if MessageT is a TypeAdapter, otherwise just MessageT.
using PublishedType = typename rclcpp::TypeAdapter<MessageT>::custom_type;
/// MessageT::ros_message_type if MessageT is a TypeAdapter, otherwise just MessageT.
using ROSMessageType = typename rclcpp::TypeAdapter<MessageT>::ros_message_type;
using PublishedTypeAllocatorTraits = allocator::AllocRebind<PublishedType, AllocatorT>;
@@ -130,40 +131,16 @@ public:
node_base,
topic,
rclcpp::get_message_type_support_handle<MessageT>(),
options.template to_rcl_publisher_options<MessageT>(qos)),
options.template to_rcl_publisher_options<MessageT>(qos),
// NOTE(methylDragon): Passing these args separately is necessary for event binding
options.event_callbacks,
options.use_default_callbacks),
options_(options),
published_type_allocator_(*options.get_allocator()),
ros_message_type_allocator_(*options.get_allocator())
{
allocator::set_allocator_for_deleter(&published_type_deleter_, &published_type_allocator_);
allocator::set_allocator_for_deleter(&ros_message_type_deleter_, &ros_message_type_allocator_);
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);
}
if (options_.event_callbacks.incompatible_qos_callback) {
this->add_event_handler(
options_.event_callbacks.incompatible_qos_callback,
RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
} else if (options_.use_default_callbacks) {
// Register default callback when not specified
try {
this->add_event_handler(
[this](QOSOfferedIncompatibleQoSInfo & info) {
this->default_incompatible_qos_callback(info);
},
RCL_PUBLISHER_OFFERED_INCOMPATIBLE_QOS);
} catch (UnsupportedEventTypeException & /*exc*/) {
// pass
}
}
// Setup continues in the post construction method, post_init_setup().
}
@@ -263,10 +240,11 @@ public:
get_subscription_count() > get_intra_process_subscription_count();
if (inter_process_publish_needed) {
auto shared_msg = this->do_intra_process_publish_and_return_shared(std::move(msg));
auto shared_msg =
this->do_intra_process_ros_message_publish_and_return_shared(std::move(msg));
this->do_inter_process_publish(*shared_msg);
} else {
this->do_intra_process_publish(std::move(msg));
this->do_intra_process_ros_message_publish(std::move(msg));
}
}
@@ -319,13 +297,28 @@ public:
>
publish(std::unique_ptr<T, PublishedTypeDeleter> msg)
{
// TODO(wjwwood): later update this to give the unique_ptr to the intra
// process manager and let it decide if it needs to be converted or not.
// For now, convert it unconditionally and pass it the ROSMessageType
// publish function specialization.
auto unique_ros_msg = this->create_ros_message_unique_ptr();
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*msg, *unique_ros_msg);
this->publish(std::move(unique_ros_msg));
// Avoid allocating when not using intra process.
if (!intra_process_is_enabled_) {
// In this case we're not using intra process.
ROSMessageType ros_msg;
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*msg, ros_msg);
return this->do_inter_process_publish(ros_msg);
}
bool inter_process_publish_needed =
get_subscription_count() > get_intra_process_subscription_count();
if (inter_process_publish_needed) {
ROSMessageType ros_msg;
// TODO(clalancette): This is unnecessarily doing an additional conversion
// that may have already been done in do_intra_process_publish_and_return_shared().
// We should just reuse that effort.
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(*msg, ros_msg);
this->do_intra_process_publish(std::move(msg));
this->do_inter_process_publish(ros_msg);
} else {
this->do_intra_process_publish(std::move(msg));
}
}
/// Publish a message on the topic.
@@ -346,12 +339,7 @@ public:
>
publish(const T & msg)
{
// TODO(wjwwood): later update this to give the unique_ptr to the intra
// process manager and let it decide if it needs to be converted or not.
// For now, convert it unconditionally and pass it the ROSMessageType
// publish function specialization.
// Avoid allocating when not using intra process.
// Avoid double allocating when not using intra process.
if (!intra_process_is_enabled_) {
// Convert to the ROS message equivalent and publish it.
ROSMessageType ros_msg;
@@ -359,13 +347,12 @@ public:
// In this case we're not using intra process.
return this->do_inter_process_publish(ros_msg);
}
// Otherwise we have to allocate memory in a unique_ptr, convert it,
// and pass it along.
// Otherwise we have to allocate memory in a unique_ptr and pass it along.
// As the message is not const, a copy should be made.
// A shared_ptr<const MessageT> could also be constructed here.
auto unique_ros_msg = this->create_ros_message_unique_ptr();
rclcpp::TypeAdapter<MessageT>::convert_to_ros_message(msg, *unique_ros_msg);
this->publish(std::move(unique_ros_msg));
auto unique_msg = this->duplicate_type_adapt_message_as_unique_ptr(msg);
this->publish(std::move(unique_msg));
}
void
@@ -394,10 +381,6 @@ public:
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
@@ -411,7 +394,7 @@ public:
} 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());
this->publish(loaned_msg.get());
}
}
@@ -491,7 +474,7 @@ protected:
}
void
do_intra_process_publish(std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> msg)
do_intra_process_publish(std::unique_ptr<PublishedType, PublishedTypeDeleter> msg)
{
auto ipm = weak_ipm_.lock();
if (!ipm) {
@@ -501,15 +484,41 @@ protected:
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());
ipm->template do_intra_process_publish<ROSMessageType, AllocatorT>(
ipm->template do_intra_process_publish<PublishedType, ROSMessageType, AllocatorT>(
intra_process_publisher_id_,
std::move(msg),
published_type_allocator_);
}
void
do_intra_process_ros_message_publish(std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> 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");
}
TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());
ipm->template do_intra_process_publish<ROSMessageType, ROSMessageType, AllocatorT>(
intra_process_publisher_id_,
std::move(msg),
ros_message_type_allocator_);
}
std::shared_ptr<const ROSMessageType>
do_intra_process_publish_and_return_shared(
do_intra_process_ros_message_publish_and_return_shared(
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter> msg)
{
auto ipm = weak_ipm_.lock();
@@ -520,14 +529,19 @@ protected:
if (!msg) {
throw std::runtime_error("cannot publish msg which is a null pointer");
}
TRACEPOINT(
rclcpp_intra_publish,
static_cast<const void *>(publisher_handle_.get()),
msg.get());
return ipm->template do_intra_process_publish_and_return_shared<ROSMessageType,
return ipm->template do_intra_process_publish_and_return_shared<ROSMessageType, ROSMessageType,
AllocatorT>(
intra_process_publisher_id_,
std::move(msg),
ros_message_type_allocator_);
}
/// Return a new unique_ptr using the ROSMessageType of the publisher.
std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>
create_ros_message_unique_ptr()
@@ -546,6 +560,15 @@ protected:
return std::unique_ptr<ROSMessageType, ROSMessageTypeDeleter>(ptr, ros_message_type_deleter_);
}
/// Duplicate a given type adapted message as a unique_ptr.
std::unique_ptr<PublishedType, PublishedTypeDeleter>
duplicate_type_adapt_message_as_unique_ptr(const PublishedType & msg)
{
auto ptr = PublishedTypeAllocatorTraits::allocate(published_type_allocator_, 1);
PublishedTypeAllocatorTraits::construct(published_type_allocator_, ptr, msg);
return std::unique_ptr<PublishedType, PublishedTypeDeleter>(ptr, published_type_deleter_);
}
/// Copy of original options passed during construction.
/**
* It is important to save a copy of this so that the rmw payload which it

View File

@@ -18,11 +18,14 @@
#include <rmw/error_handling.h>
#include <rmw/rmw.h>
#include <chrono>
#include <functional>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include "rcl/publisher.h"
@@ -30,9 +33,10 @@
#include "rclcpp/macros.hpp"
#include "rclcpp/network_flow_endpoint.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/event_handler.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcpputils/time.hpp"
namespace rclcpp
{
@@ -74,11 +78,18 @@ public:
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);
const rcl_publisher_options_t & publisher_options,
const PublisherEventCallbacks & event_callbacks,
bool use_default_callbacks);
RCLCPP_PUBLIC
virtual ~PublisherBase();
/// Add event handlers for passed in event_callbacks.
RCLCPP_PUBLIC
void
bind_event_callbacks(const PublisherEventCallbacks & event_callbacks, bool use_default_callbacks);
/// Get the topic that this publisher publishes on.
/** \return The topic name. */
RCLCPP_PUBLIC
@@ -110,9 +121,10 @@ public:
get_publisher_handle() const;
/// Get all the QoS event handlers associated with this publisher.
/** \return The vector of QoS event handlers. */
/** \return The map of QoS event handlers. */
RCLCPP_PUBLIC
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
const
std::unordered_map<rcl_publisher_event_type_t, std::shared_ptr<rclcpp::EventHandlerBase>> &
get_event_handlers() const;
/// Get subscription count
@@ -203,6 +215,111 @@ public:
std::vector<rclcpp::NetworkFlowEndpoint>
get_network_flow_endpoints() const;
/// Wait until all published messages are acknowledged or until the specified timeout elapses.
/**
* This method waits until all published messages are acknowledged by all matching
* subscriptions or the given timeout elapses.
*
* If the timeout is negative then this method will block indefinitely until all published
* messages are acknowledged.
* If the timeout is zero then this method will not block, it will check if all published
* messages are acknowledged and return immediately.
* If the timeout is greater than zero, this method will wait until all published messages are
* acknowledged or the timeout elapses.
*
* This method only waits for acknowledgments if the publisher's QoS profile is RELIABLE.
* Otherwise this method will immediately return `true`.
*
* \param[in] timeout the duration to wait for all published messages to be acknowledged.
* \return `true` if all published messages were acknowledged before the given timeout
* elapsed, otherwise `false`.
* \throws rclcpp::exceptions::RCLError if middleware doesn't support or internal error occurs
* \throws std::invalid_argument if timeout is greater than std::chrono::nanoseconds::max() or
* less than std::chrono::nanoseconds::min()
*/
template<typename DurationRepT = int64_t, typename DurationT = std::milli>
bool
wait_for_all_acked(
std::chrono::duration<DurationRepT, DurationT> timeout =
std::chrono::duration<DurationRepT, DurationT>(-1)) const
{
rcl_duration_value_t rcl_timeout = rcpputils::convert_to_nanoseconds(timeout).count();
rcl_ret_t ret = rcl_publisher_wait_for_all_acked(publisher_handle_.get(), rcl_timeout);
if (ret == RCL_RET_OK) {
return true;
} else if (ret == RCL_RET_TIMEOUT) {
return false;
} else {
rclcpp::exceptions::throw_from_rcl_error(ret);
}
}
/// Set a callback to be called when each new qos event instance occurs.
/**
* The callback receives a size_t which is the number of events that occurred
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if events occurred before any
* callback was set.
*
* Since this callback is called from the middleware, you should aim to make
* it fast and not blocking.
* If you need to do a lot of work or wait for some other event, you should
* spin it off to another thread, otherwise you risk blocking the middleware.
*
* Calling it again will clear any previously set callback.
*
* An exception will be thrown if the callback is not callable.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the qos event
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rclcpp::EventHandlerBase::set_on_ready_callback
*
* \param[in] callback functor to be called when a new event occurs
* \param[in] event_type identifier for the qos event we want to attach the callback to
*/
void
set_on_new_qos_event_callback(
std::function<void(size_t)> callback,
rcl_publisher_event_type_t event_type)
{
if (event_handlers_.count(event_type) == 0) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Calling set_on_new_qos_event_callback for non registered publisher event_type");
return;
}
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_new_qos_event_callback "
"is not callable.");
}
// The on_ready_callback signature has an extra `int` argument used to disambiguate between
// possible different entities within a generic waitable.
// We hide that detail to users of this method.
std::function<void(size_t, int)> new_callback = std::bind(callback, std::placeholders::_1);
event_handlers_[event_type]->set_on_ready_callback(new_callback);
}
/// Unset the callback registered for new qos events, if any.
void
clear_on_new_qos_event_callback(rcl_publisher_event_type_t event_type)
{
if (event_handlers_.count(event_type) == 0) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Calling clear_on_new_qos_event_callback for non registered event_type");
return;
}
event_handlers_[event_type]->clear_on_ready_callback();
}
protected:
template<typename EventCallbackT>
void
@@ -210,23 +327,27 @@ protected:
const EventCallbackT & callback,
const rcl_publisher_event_type_t event_type)
{
auto handler = std::make_shared<QOSEventHandler<EventCallbackT,
auto handler = std::make_shared<EventHandler<EventCallbackT,
std::shared_ptr<rcl_publisher_t>>>(
callback,
rcl_publisher_event_init,
publisher_handle_,
event_type);
event_handlers_.emplace_back(handler);
event_handlers_.insert(std::make_pair(event_type, handler));
}
RCLCPP_PUBLIC
void default_incompatible_qos_callback(QOSOfferedIncompatibleQoSInfo & info) const;
RCLCPP_PUBLIC
void default_incompatible_type_callback(IncompatibleTypeInfo & info) const;
std::shared_ptr<rcl_node_t> rcl_node_handle_;
std::shared_ptr<rcl_publisher_t> publisher_handle_;
std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
std::unordered_map<rcl_publisher_event_type_t,
std::shared_ptr<rclcpp::EventHandlerBase>> event_handlers_;
using IntraProcessManagerWeakPtr =
std::weak_ptr<rclcpp::experimental::IntraProcessManager>;
@@ -235,6 +356,10 @@ protected:
uint64_t intra_process_publisher_id_;
rmw_gid_t rmw_gid_;
const rosidl_message_type_support_t type_support_;
const PublisherEventCallbacks event_callbacks_;
};
} // namespace rclcpp

View File

@@ -26,7 +26,7 @@
#include "rclcpp/detail/rmw_implementation_specific_publisher_payload.hpp"
#include "rclcpp/intra_process_setting.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/event_handler.hpp"
#include "rclcpp/qos_overriding_options.hpp"
namespace rclcpp
@@ -72,7 +72,7 @@ struct PublisherOptionsWithAllocator : public PublisherOptionsBase
/// Optional custom allocator.
std::shared_ptr<Allocator> allocator = nullptr;
PublisherOptionsWithAllocator<Allocator>() {}
PublisherOptionsWithAllocator() {}
/// Constructor using base class as input.
explicit PublisherOptionsWithAllocator(const PublisherOptionsBase & publisher_options_base)

View File

@@ -44,6 +44,7 @@ enum class ReliabilityPolicy
BestEffort = RMW_QOS_POLICY_RELIABILITY_BEST_EFFORT,
Reliable = RMW_QOS_POLICY_RELIABILITY_RELIABLE,
SystemDefault = RMW_QOS_POLICY_RELIABILITY_SYSTEM_DEFAULT,
BestAvailable = RMW_QOS_POLICY_RELIABILITY_BEST_AVAILABLE,
Unknown = RMW_QOS_POLICY_RELIABILITY_UNKNOWN,
};
@@ -52,6 +53,7 @@ enum class DurabilityPolicy
Volatile = RMW_QOS_POLICY_DURABILITY_VOLATILE,
TransientLocal = RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
SystemDefault = RMW_QOS_POLICY_DURABILITY_SYSTEM_DEFAULT,
BestAvailable = RMW_QOS_POLICY_DURABILITY_BEST_AVAILABLE,
Unknown = RMW_QOS_POLICY_DURABILITY_UNKNOWN,
};
@@ -60,6 +62,7 @@ enum class LivelinessPolicy
Automatic = RMW_QOS_POLICY_LIVELINESS_AUTOMATIC,
ManualByTopic = RMW_QOS_POLICY_LIVELINESS_MANUAL_BY_TOPIC,
SystemDefault = RMW_QOS_POLICY_LIVELINESS_SYSTEM_DEFAULT,
BestAvailable = RMW_QOS_POLICY_LIVELINESS_BEST_AVAILABLE,
Unknown = RMW_QOS_POLICY_LIVELINESS_UNKNOWN,
};
@@ -77,7 +80,9 @@ struct RCLCPP_PUBLIC QoSInitialization
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);
QoSInitialization(
rmw_qos_history_policy_t history_policy_arg, size_t depth_arg,
bool print_depth_warning = true);
/// Create a QoSInitialization from an existing rmw_qos_profile_t, using its history and depth.
static
@@ -94,7 +99,7 @@ struct RCLCPP_PUBLIC KeepAll : public rclcpp::QoSInitialization
/// 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);
explicit KeepLast(size_t depth, bool print_depth_warning = true);
};
/// Encapsulation of Quality of Service settings.
@@ -180,6 +185,10 @@ public:
QoS &
best_effort();
/// Set the reliability setting to best available.
QoS &
reliability_best_available();
/// Set the durability setting.
QoS &
durability(rmw_qos_durability_policy_t durability);
@@ -199,6 +208,10 @@ public:
QoS &
transient_local();
/// Set the durability setting to best available.
QoS &
durability_best_available();
/// Set the deadline setting.
QoS &
deadline(rmw_time_t deadline);
@@ -488,6 +501,36 @@ public:
));
};
/**
* Best available QoS class
*
* Match majority of endpoints currently available while maintaining the highest level of service.
* Policies are chosen at the time of creating a subscription or publisher.
* The middleware is not expected to update policies after creating a subscription or publisher,
* even if one or more policies are incompatible with newly discovered endpoints.
* Therefore, this profile should be used with care since non-deterministic behavior can occur due
* to races with discovery.
*
* - History: Keep last,
* - Depth: 10,
* - Reliability: Best available,
* - Durability: Best available,
* - Deadline: Best available,
* - Lifespan: Default,
* - Liveliness: Best available,
* - Liveliness lease duration: Best available,
* - avoid ros namespace conventions: false
*/
class RCLCPP_PUBLIC BestAvailableQoS : public QoS
{
public:
explicit
BestAvailableQoS(
const QoSInitialization & qos_initialization = (
QoSInitialization::from_rmw(rmw_qos_profile_best_available)
));
};
} // namespace rclcpp
#endif // RCLCPP__QOS_HPP_

View File

@@ -15,159 +15,8 @@
#ifndef RCLCPP__QOS_EVENT_HPP_
#define RCLCPP__QOS_EVENT_HPP_
#include <functional>
#include <memory>
#include <stdexcept>
#include <string>
#warning This header is obsolete, please include rclcpp/event_handler.hpp instead
#include "rcl/error_handling.h"
#include "rmw/incompatible_qos_events_statuses.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 QOSMessageLostInfo = rmw_message_lost_status_t;
using QOSOfferedIncompatibleQoSInfo = rmw_offered_qos_incompatible_event_status_t;
using QOSRequestedIncompatibleQoSInfo = rmw_requested_qos_incompatible_event_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 &)>;
using QOSMessageLostCallbackType = std::function<void (QOSMessageLostInfo &)>;
using QOSOfferedIncompatibleQoSCallbackType = std::function<void (QOSOfferedIncompatibleQoSInfo &)>;
using QOSRequestedIncompatibleQoSCallbackType =
std::function<void (QOSRequestedIncompatibleQoSInfo &)>;
/// Contains callbacks for various types of events a Publisher can receive from the middleware.
struct PublisherEventCallbacks
{
QOSDeadlineOfferedCallbackType deadline_callback;
QOSLivelinessLostCallbackType liveliness_callback;
QOSOfferedIncompatibleQoSCallbackType incompatible_qos_callback;
};
/// Contains callbacks for non-message events that a Subscription can receive from the middleware.
struct SubscriptionEventCallbacks
{
QOSDeadlineRequestedCallbackType deadline_callback;
QOSLivelinessChangedCallbackType liveliness_callback;
QOSRequestedIncompatibleQoSCallbackType incompatible_qos_callback;
QOSMessageLostCallbackType message_lost_callback;
};
class UnsupportedEventTypeException : public exceptions::RCLErrorBase, public std::runtime_error
{
public:
RCLCPP_PUBLIC
UnsupportedEventTypeException(
rcl_ret_t ret,
const rcl_error_state_t * error_state,
const std::string & prefix);
RCLCPP_PUBLIC
UnsupportedEventTypeException(
const exceptions::RCLErrorBase & base_exc,
const std::string & prefix);
};
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
void
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, typename ParentHandleT>
class QOSEventHandler : public QOSEventHandlerBase
{
public:
template<typename InitFuncT, typename EventTypeEnum>
QOSEventHandler(
const EventCallbackT & callback,
InitFuncT init_func,
ParentHandleT parent_handle,
EventTypeEnum event_type)
: event_callback_(callback)
{
parent_handle_ = parent_handle;
event_handle_ = rcl_get_zero_initialized_event();
rcl_ret_t ret = init_func(&event_handle_, parent_handle.get(), event_type);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_UNSUPPORTED) {
UnsupportedEventTypeException exc(ret, rcl_get_error_state(), "Failed to initialize event");
rcl_reset_error();
throw exc;
} else {
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to initialize event");
}
}
}
/// Take data so that the callback cannot be scheduled again
std::shared_ptr<void>
take_data() 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 nullptr;
}
return std::static_pointer_cast<void>(std::make_shared<EventCallbackInfoT>(callback_info));
}
/// Execute any entities of the Waitable that are ready.
void
execute(std::shared_ptr<void> & data) override
{
if (!data) {
throw std::runtime_error("'data' is empty");
}
auto callback_ptr = std::static_pointer_cast<EventCallbackInfoT>(data);
event_callback_(*callback_ptr);
callback_ptr.reset();
}
private:
using EventCallbackInfoT = typename std::remove_reference<typename
rclcpp::function_traits::function_traits<EventCallbackT>::template argument_type<0>>::type;
ParentHandleT parent_handle_;
EventCallbackT event_callback_;
};
} // namespace rclcpp
#include "rclcpp/event_handler.hpp"
#endif // RCLCPP__QOS_EVENT_HPP_

View File

@@ -117,6 +117,18 @@
* - Allocator related items:
* - rclcpp/allocator/allocator_common.hpp
* - rclcpp/allocator/allocator_deleter.hpp
* - Dynamic typesupport wrappers
* - rclcpp::dynamic_typesupport::DynamicMessage
* - rclcpp::dynamic_typesupport::DynamicMessageType
* - rclcpp::dynamic_typesupport::DynamicMessageTypeBuilder
* - rclcpp::dynamic_typesupport::DynamicSerializationSupport
* - rclcpp/dynamic_typesupport/dynamic_message.hpp
* - rclcpp/dynamic_typesupport/dynamic_message_type.hpp
* - rclcpp/dynamic_typesupport/dynamic_message_type_builder.hpp
* - rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp
* - Dynamic typesupport
* - rclcpp::dynamic_typesupport::DynamicMessageTypeSupport
* - rclcpp/dynamic_typesupport/dynamic_message_type_support.hpp
* - Generic publisher
* - rclcpp::Node::create_generic_publisher()
* - rclcpp::GenericPublisher

View File

@@ -1,56 +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.
// Based on: http://the-witness.net/news/2012/11/scopeexit-in-c11/
// But I changed the lambda to include by reference rather than value, see:
// http://the-witness.net/news/2012/11/scopeexit-in-c11/comment-page-1/#comment-86873
#ifndef RCLCPP__SCOPE_EXIT_HPP_
#define RCLCPP__SCOPE_EXIT_HPP_
// TODO(christophebedard) remove this header completely in I-turtle
#warning rclcpp/scope_exit.hpp has been deprecated, please use rcpputils/scope_exit.hpp instead
#include <functional>
#include "rclcpp/macros.hpp"
namespace rclcpp
{
template<typename Callable>
struct ScopeExit
{
explicit ScopeExit(Callable callable)
: callable_(callable) {}
~ScopeExit() {callable_();}
private:
Callable callable_;
};
template<typename Callable>
ScopeExit<Callable>
make_scope_exit(Callable callable)
{
return ScopeExit<Callable>(callable);
}
} // namespace rclcpp
#define RCLCPP_SCOPE_EXIT(code) \
auto RCLCPP_STRING_JOIN(scope_exit_, __LINE__) = rclcpp::make_scope_exit([&]() {code;})
#endif // RCLCPP__SCOPE_EXIT_HPP_

View File

@@ -19,22 +19,31 @@
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include "rcl/error_handling.h"
#include "rcl/event_callback.h"
#include "rcl/service.h"
#include "rcl/service_introspection.h"
#include "rmw/error_handling.h"
#include "rmw/impl/cpp/demangle.hpp"
#include "rmw/rmw.h"
#include "tracetools/tracetools.h"
#include "rclcpp/any_service_callback.hpp"
#include "rclcpp/clock.hpp"
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rclcpp/logging.hpp"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
#include "tracetools/tracetools.h"
#include "rclcpp/macros.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
@@ -48,7 +57,7 @@ public:
explicit ServiceBase(std::shared_ptr<rcl_node_t> node_handle);
RCLCPP_PUBLIC
virtual ~ServiceBase();
virtual ~ServiceBase() = default;
/// Return the name of the service.
/** \return The name of the service. */
@@ -121,6 +130,124 @@ public:
bool
exchange_in_use_by_wait_set_state(bool in_use_state);
/// Get the actual response publisher 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 service, 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 response publisher qos settings.
* \throws std::runtime_error if failed to get qos settings
*/
RCLCPP_PUBLIC
rclcpp::QoS
get_response_publisher_actual_qos() const;
/// Get the actual request subscription 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 service, 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 request subscription qos settings.
* \throws std::runtime_error if failed to get qos settings
*/
RCLCPP_PUBLIC
rclcpp::QoS
get_request_subscription_actual_qos() const;
/// Set a callback to be called when each new request is received.
/**
* The callback receives a size_t which is the number of requests received
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if requests were received before any
* callback was set.
*
* Since this callback is called from the middleware, you should aim to make
* it fast and not blocking.
* If you need to do a lot of work or wait for some other event, you should
* spin it off to another thread, otherwise you risk blocking the middleware.
*
* Calling it again will clear any previously set callback.
*
*
* An exception will be thrown if the callback is not callable.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the service
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rmw_service_set_on_new_request_callback
* \sa rcl_service_set_on_new_request_callback
*
* \param[in] callback functor to be called when a new request is received
*/
void
set_on_new_request_callback(std::function<void(size_t)> callback)
{
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_new_request_callback "
"is not callable.");
}
auto new_callback =
[callback, this](size_t number_of_requests) {
try {
callback(number_of_requests);
} catch (const std::exception & exception) {
RCLCPP_ERROR_STREAM(
node_logger_,
"rclcpp::ServiceBase@" << this <<
" caught " << rmw::impl::cpp::demangle(exception) <<
" exception in user-provided callback for the 'on new request' callback: " <<
exception.what());
} catch (...) {
RCLCPP_ERROR_STREAM(
node_logger_,
"rclcpp::ServiceBase@" << this <<
" caught unhandled exception in user-provided callback " <<
"for the 'on new request' callback");
}
};
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
// been replaced but the middleware hasn't been told about the new one yet.
set_on_new_request_callback(
rclcpp::detail::cpp_callback_trampoline<decltype(new_callback), const void *, size_t>,
static_cast<const void *>(&new_callback));
// Store the std::function to keep it in scope, also overwrites the existing one.
on_new_request_callback_ = new_callback;
// Set it again, now using the permanent storage.
set_on_new_request_callback(
rclcpp::detail::cpp_callback_trampoline<
decltype(on_new_request_callback_), const void *, size_t>,
static_cast<const void *>(&on_new_request_callback_));
}
/// Unset the callback registered for new requests, if any.
void
clear_on_new_request_callback()
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
if (on_new_request_callback_) {
set_on_new_request_callback(nullptr, nullptr);
on_new_request_callback_ = nullptr;
}
}
protected:
RCLCPP_DISABLE_COPY(ServiceBase)
@@ -132,12 +259,21 @@ protected:
const rcl_node_t *
get_rcl_node_handle() const;
RCLCPP_PUBLIC
void
set_on_new_request_callback(rcl_event_callback_t callback, const void * user_data);
std::shared_ptr<rcl_node_t> node_handle_;
std::shared_ptr<rcl_service_t> service_handle_;
bool owns_rcl_handle_ = true;
rclcpp::Logger node_logger_;
std::atomic<bool> in_use_by_wait_set_{false};
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_request_callback_{nullptr};
};
template<typename ServiceT>
@@ -174,11 +310,9 @@ public:
const std::string & service_name,
AnyServiceCallback<ServiceT> any_callback,
rcl_service_options_t & service_options)
: ServiceBase(node_handle), any_callback_(any_callback)
: ServiceBase(node_handle), any_callback_(any_callback),
srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
{
using rosidl_typesupport_cpp::get_service_type_support_handle;
auto service_type_support_handle = get_service_type_support_handle<ServiceT>();
// rcl does the static memory allocation here
service_handle_ = std::shared_ptr<rcl_service_t>(
new rcl_service_t, [handle = node_handle_, service_name](rcl_service_t * service)
@@ -197,7 +331,7 @@ public:
rcl_ret_t ret = rcl_service_init(
service_handle_.get(),
node_handle.get(),
service_type_support_handle,
srv_type_support_handle_,
service_name.c_str(),
&service_options);
if (ret != RCL_RET_OK) {
@@ -237,8 +371,8 @@ public:
std::shared_ptr<rcl_node_t> node_handle,
std::shared_ptr<rcl_service_t> service_handle,
AnyServiceCallback<ServiceT> any_callback)
: ServiceBase(node_handle),
any_callback_(any_callback)
: ServiceBase(node_handle), any_callback_(any_callback),
srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
{
// check if service handle was initialized
if (!rcl_service_is_valid(service_handle.get())) {
@@ -272,8 +406,8 @@ public:
std::shared_ptr<rcl_node_t> node_handle,
rcl_service_t * service_handle,
AnyServiceCallback<ServiceT> any_callback)
: ServiceBase(node_handle),
any_callback_(any_callback)
: ServiceBase(node_handle), any_callback_(any_callback),
srv_type_support_handle_(rosidl_typesupport_cpp::get_service_type_support_handle<ServiceT>())
{
// check if service handle was initialized
if (!rcl_service_is_valid(service_handle)) {
@@ -353,10 +487,39 @@ public:
}
}
/// Configure client introspection.
/**
* \param[in] clock clock to use to generate introspection timestamps
* \param[in] qos_service_event_pub QoS settings to use when creating the introspection publisher
* \param[in] introspection_state the state to set introspection to
*/
void
configure_introspection(
Clock::SharedPtr clock, const QoS & qos_service_event_pub,
rcl_service_introspection_state_t introspection_state)
{
rcl_publisher_options_t pub_opts = rcl_publisher_get_default_options();
pub_opts.qos = qos_service_event_pub.get_rmw_qos_profile();
rcl_ret_t ret = rcl_service_configure_service_introspection(
service_handle_.get(),
node_handle_.get(),
clock->get_clock_handle(),
srv_type_support_handle_,
pub_opts,
introspection_state);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to configure service introspection");
}
}
private:
RCLCPP_DISABLE_COPY(Service)
AnyServiceCallback<ServiceT> any_callback_;
const rosidl_service_type_support_t * srv_type_support_handle_;
};
} // namespace rclcpp

View File

@@ -17,6 +17,7 @@
#include <memory>
#include <vector>
#include <utility>
#include "rcl/allocator.h"
@@ -120,8 +121,8 @@ public:
}
}
for (size_t i = 0; i < waitable_handles_.size(); ++i) {
if (!waitable_handles_[i]->is_ready(wait_set)) {
waitable_handles_[i].reset();
if (waitable_handles_[i]->is_ready(wait_set)) {
waitable_triggered_handles_.emplace_back(std::move(waitable_handles_[i]));
}
}
@@ -145,10 +146,7 @@ public:
timer_handles_.end()
);
waitable_handles_.erase(
std::remove(waitable_handles_.begin(), waitable_handles_.end(), nullptr),
waitable_handles_.end()
);
waitable_handles_.clear();
}
bool collect_entities(const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
@@ -164,30 +162,22 @@ public:
if (!group || !group->can_be_taken_from().load()) {
continue;
}
group->find_subscription_ptrs_if(
group->collect_all_ptrs(
[this](const rclcpp::SubscriptionBase::SharedPtr & subscription) {
subscription_handles_.push_back(subscription->get_subscription_handle());
return false;
});
group->find_service_ptrs_if(
},
[this](const rclcpp::ServiceBase::SharedPtr & service) {
service_handles_.push_back(service->get_service_handle());
return false;
});
group->find_client_ptrs_if(
},
[this](const rclcpp::ClientBase::SharedPtr & client) {
client_handles_.push_back(client->get_client_handle());
return false;
});
group->find_timer_ptrs_if(
},
[this](const rclcpp::TimerBase::SharedPtr & timer) {
timer_handles_.push_back(timer->get_timer_handle());
return false;
});
group->find_waitable_ptrs_if(
},
[this](const rclcpp::Waitable::SharedPtr & waitable) {
waitable_handles_.push_back(waitable);
return false;
});
}
@@ -204,7 +194,7 @@ public:
bool add_handles_to_wait_set(rcl_wait_set_t * wait_set) override
{
for (auto subscription : subscription_handles_) {
for (const std::shared_ptr<const rcl_subscription_t> & subscription : subscription_handles_) {
if (rcl_wait_set_add_subscription(wait_set, subscription.get(), NULL) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
@@ -213,7 +203,7 @@ public:
}
}
for (auto client : client_handles_) {
for (const std::shared_ptr<const rcl_client_t> & client : client_handles_) {
if (rcl_wait_set_add_client(wait_set, client.get(), NULL) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
@@ -222,7 +212,7 @@ public:
}
}
for (auto service : service_handles_) {
for (const std::shared_ptr<const rcl_service_t> & service : service_handles_) {
if (rcl_wait_set_add_service(wait_set, service.get(), NULL) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
@@ -231,7 +221,7 @@ public:
}
}
for (auto timer : timer_handles_) {
for (const std::shared_ptr<const rcl_timer_t> & timer : timer_handles_) {
if (rcl_wait_set_add_timer(wait_set, timer.get(), NULL) != RCL_RET_OK) {
RCUTILS_LOG_ERROR_NAMED(
"rclcpp",
@@ -244,7 +234,7 @@ public:
detail::add_guard_condition_to_rcl_wait_set(*wait_set, *guard_condition);
}
for (auto waitable : waitable_handles_) {
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
waitable->add_to_wait_set(wait_set);
}
return true;
@@ -400,16 +390,17 @@ public:
rclcpp::AnyExecutable & any_exec,
const WeakCallbackGroupsToNodesMap & weak_groups_to_nodes) override
{
auto it = waitable_handles_.begin();
while (it != waitable_handles_.end()) {
auto waitable = *it;
auto & waitable_handles = waitable_triggered_handles_;
auto it = waitable_handles.begin();
while (it != waitable_handles.end()) {
std::shared_ptr<Waitable> & waitable = *it;
if (waitable) {
// Find the group for this handle and see if it can be serviced
auto group = get_group_by_waitable(waitable, weak_groups_to_nodes);
if (!group) {
// Group was not found, meaning the waitable is not valid...
// Remove it from the ready list and continue looking
it = waitable_handles_.erase(it);
it = waitable_handles.erase(it);
continue;
}
if (!group->can_be_taken_from().load()) {
@@ -422,11 +413,11 @@ public:
any_exec.waitable = waitable;
any_exec.callback_group = group;
any_exec.node_base = get_node_by_group(group, weak_groups_to_nodes);
waitable_handles_.erase(it);
waitable_handles.erase(it);
return;
}
// Else, the waitable is no longer valid, remove it and continue
it = waitable_handles_.erase(it);
it = waitable_handles.erase(it);
}
}
@@ -438,7 +429,7 @@ public:
size_t number_of_ready_subscriptions() const override
{
size_t number_of_subscriptions = subscription_handles_.size();
for (auto waitable : waitable_handles_) {
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
number_of_subscriptions += waitable->get_number_of_ready_subscriptions();
}
return number_of_subscriptions;
@@ -447,7 +438,7 @@ public:
size_t number_of_ready_services() const override
{
size_t number_of_services = service_handles_.size();
for (auto waitable : waitable_handles_) {
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
number_of_services += waitable->get_number_of_ready_services();
}
return number_of_services;
@@ -456,7 +447,7 @@ public:
size_t number_of_ready_events() const override
{
size_t number_of_events = 0;
for (auto waitable : waitable_handles_) {
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
number_of_events += waitable->get_number_of_ready_events();
}
return number_of_events;
@@ -465,7 +456,7 @@ public:
size_t number_of_ready_clients() const override
{
size_t number_of_clients = client_handles_.size();
for (auto waitable : waitable_handles_) {
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
number_of_clients += waitable->get_number_of_ready_clients();
}
return number_of_clients;
@@ -474,7 +465,7 @@ public:
size_t number_of_guard_conditions() const override
{
size_t number_of_guard_conditions = guard_conditions_.size();
for (auto waitable : waitable_handles_) {
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
number_of_guard_conditions += waitable->get_number_of_ready_guard_conditions();
}
return number_of_guard_conditions;
@@ -483,7 +474,7 @@ public:
size_t number_of_ready_timers() const override
{
size_t number_of_timers = timer_handles_.size();
for (auto waitable : waitable_handles_) {
for (const std::shared_ptr<Waitable> & waitable : waitable_handles_) {
number_of_timers += waitable->get_number_of_ready_timers();
}
return number_of_timers;
@@ -507,6 +498,8 @@ private:
VectorRebind<std::shared_ptr<const rcl_timer_t>> timer_handles_;
VectorRebind<std::shared_ptr<Waitable>> waitable_handles_;
VectorRebind<std::shared_ptr<Waitable>> waitable_triggered_handles_;
std::shared_ptr<VoidAlloc> allocator_;
};

View File

@@ -140,44 +140,15 @@ public:
node_base,
type_support_handle,
topic_name,
options.template to_rcl_subscription_options<ROSMessageType>(qos),
callback.is_serialized_message_callback()),
options.to_rcl_subscription_options(qos),
// NOTE(methylDragon): Passing these args separately is necessary for event binding
options.event_callbacks,
options.use_default_callbacks,
callback.is_serialized_message_callback() ? DeliveredMessageKind::SERIALIZED_MESSAGE : DeliveredMessageKind::ROS_MESSAGE), // NOLINT
any_callback_(callback),
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);
}
if (options_.event_callbacks.incompatible_qos_callback) {
this->add_event_handler(
options_.event_callbacks.incompatible_qos_callback,
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
} else if (options_.use_default_callbacks) {
// Register default callback when not specified
try {
this->add_event_handler(
[this](QOSRequestedIncompatibleQoSInfo & info) {
this->default_incompatible_qos_callback(info);
},
RCL_SUBSCRIPTION_REQUESTED_INCOMPATIBLE_QOS);
} catch (UnsupportedEventTypeException & /*exc*/) {
// pass
}
}
if (options_.event_callbacks.message_lost_callback) {
this->add_event_handler(
options_.event_callbacks.message_lost_callback,
RCL_SUBSCRIPTION_MESSAGE_LOST);
}
// Setup intra process publishing if requested.
if (rclcpp::detail::resolve_use_intra_process(options_, *node_base)) {
using rclcpp::detail::resolve_intra_process_buffer_type;
@@ -197,6 +168,14 @@ public:
"intraprocess communication allowed only with volatile durability");
}
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
MessageT,
SubscribedType,
SubscribedTypeAllocator,
SubscribedTypeDeleter,
ROSMessageT,
AllocatorT>;
// First create a SubscriptionIntraProcess which will be given to the intra-process manager.
auto context = node_base->get_context();
subscription_intra_process_ = std::make_shared<SubscriptionIntraProcessT>(
@@ -276,7 +255,7 @@ public:
/// Take the next message from the inter-process subscription.
/**
* This verison takes a SubscribedType which is different frmo the
* This version takes a SubscribedType which is different from the
* ROSMessageType when a rclcpp::TypeAdapter is in used.
*
* \sa take(ROSMessageType &, rclcpp::MessageInfo &)
@@ -355,11 +334,31 @@ public:
void * loaned_message,
const rclcpp::MessageInfo & message_info) override
{
if (matches_any_intra_process_publishers(&message_info.get_rmw_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 = static_cast<ROSMessageType *>(loaned_message);
// message is loaned, so we have to make sure that the deleter does not deallocate the message
auto sptr = std::shared_ptr<ROSMessageType>(
typed_message, [](ROSMessageType * msg) {(void) msg;});
std::chrono::time_point<std::chrono::system_clock> now;
if (subscription_topic_statistics_) {
// get current time before executing callback to
// exclude callback duration from topic statistics result.
now = std::chrono::system_clock::now();
}
any_callback_.dispatch(sptr, message_info);
if (subscription_topic_statistics_) {
const auto nanos = std::chrono::time_point_cast<std::chrono::nanoseconds>(now);
const auto time = rclcpp::Time(nanos.time_since_epoch().count());
subscription_topic_statistics_->handle_message(*typed_message, time);
}
}
/// Return the borrowed message.
@@ -389,6 +388,57 @@ public:
return any_callback_.use_take_shared_method();
}
// DYNAMIC TYPE ==================================================================================
// TODO(methylDragon): Reorder later
// TODO(methylDragon): Implement later...
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
get_shared_dynamic_message_type() override
{
throw rclcpp::exceptions::UnimplementedError(
"get_shared_dynamic_message_type is not implemented for Subscription");
}
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
get_shared_dynamic_message() override
{
throw rclcpp::exceptions::UnimplementedError(
"get_shared_dynamic_message is not implemented for Subscription");
}
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support() override
{
throw rclcpp::exceptions::UnimplementedError(
"get_shared_dynamic_serialization_support is not implemented for Subscription");
}
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
create_dynamic_message() override
{
throw rclcpp::exceptions::UnimplementedError(
"create_dynamic_message is not implemented for Subscription");
}
void
return_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) override
{
(void) message;
throw rclcpp::exceptions::UnimplementedError(
"return_dynamic_message is not implemented for Subscription");
}
void
handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info) override
{
(void) message;
(void) message_info;
throw rclcpp::exceptions::UnimplementedError(
"handle_dynamic_message is not implemented for Subscription");
}
private:
RCLCPP_DISABLE_COPY(Subscription)
@@ -404,13 +454,6 @@ private:
/// Component which computes and publishes topic statistics for this subscriber
SubscriptionTopicStatisticsSharedPtr subscription_topic_statistics_{nullptr};
using SubscriptionIntraProcessT = rclcpp::experimental::SubscriptionIntraProcess<
ROSMessageType,
AllocatorT,
ROSMessageTypeDeleter,
MessageT>;
std::shared_ptr<SubscriptionIntraProcessT> subscription_intra_process_;
};
} // namespace rclcpp

View File

@@ -17,24 +17,32 @@
#include <atomic>
#include <memory>
#include <mutex>
#include <string>
#include <unordered_map>
#include <vector>
#include <utility>
#include "rcl/event_callback.h"
#include "rcl/subscription.h"
#include "rmw/impl/cpp/demangle.hpp"
#include "rmw/rmw.h"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/detail/cpp_callback_trampoline.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_message_type.hpp"
#include "rclcpp/dynamic_typesupport/dynamic_serialization_support.hpp"
#include "rclcpp/experimental/intra_process_manager.hpp"
#include "rclcpp/experimental/subscription_intra_process_base.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_info.hpp"
#include "rclcpp/network_flow_endpoint.hpp"
#include "rclcpp/qos.hpp"
#include "rclcpp/qos_event.hpp"
#include "rclcpp/event_handler.hpp"
#include "rclcpp/serialized_message.hpp"
#include "rclcpp/subscription_content_filter_options.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -55,6 +63,27 @@ namespace experimental
class IntraProcessManager;
} // namespace experimental
/// The kind of message that the subscription delivers in its callback, used by the executor
/**
* This enum needs to exist because the callback handle is not accessible to the executor's scope.
*
* "Kind" is used since what is being delivered is a category of messages, for example, there are
* different ROS message types that can be delivered, but they're all ROS messages.
*
* As a concrete example, all of the following callbacks will be considered ROS_MESSAGE for
* DeliveredMessageKind:
* - void callback(const std_msgs::msg::String &)
* - void callback(const std::string &) // type adaption
* - void callback(std::unique_ptr<std_msgs::msg::String>)
*/
enum class DeliveredMessageKind : uint8_t
{
INVALID = 0,
ROS_MESSAGE = 1, // The subscription delivers a ROS message to its callback
SERIALIZED_MESSAGE = 2, // The subscription delivers a serialized message to its callback
DYNAMIC_MESSAGE = 3, // The subscription delivers a dynamic message to its callback
};
/// 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>
@@ -71,7 +100,8 @@ public:
* \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
* \param[in] delivered_message_kind Enum flag to change how the message will be received and
* delivered
*/
RCLCPP_PUBLIC
SubscriptionBase(
@@ -79,12 +109,20 @@ public:
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);
const SubscriptionEventCallbacks & event_callbacks,
bool use_default_callbacks,
DeliveredMessageKind delivered_message_kind = DeliveredMessageKind::ROS_MESSAGE);
/// Destructor.
RCLCPP_PUBLIC
virtual ~SubscriptionBase();
/// Add event handlers for passed in event_callbacks.
RCLCPP_PUBLIC
void
bind_event_callbacks(
const SubscriptionEventCallbacks & event_callbacks, bool use_default_callbacks);
/// Get the topic that this subscription is subscribed on.
RCLCPP_PUBLIC
const char *
@@ -99,9 +137,10 @@ public:
get_subscription_handle() const;
/// Get all the QoS event handlers associated with this subscription.
/** \return The vector of QoS event handlers. */
/** \return The map of QoS event handlers. */
RCLCPP_PUBLIC
const std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> &
const
std::unordered_map<rcl_subscription_event_type_t, std::shared_ptr<rclcpp::EventHandlerBase>> &
get_event_handlers() const;
/// Get the actual QoS settings, after the defaults have been determined.
@@ -221,6 +260,14 @@ public:
bool
is_serialized() const;
/// Return the type of the subscription.
/**
* \return `DeliveredMessageKind`, which adjusts how messages are received and delivered.
*/
RCLCPP_PUBLIC
DeliveredMessageKind
get_subscription_type() const;
/// Get matching publisher count.
/** \return The number of publishers on this topic. */
RCLCPP_PUBLIC
@@ -283,6 +330,287 @@ public:
std::vector<rclcpp::NetworkFlowEndpoint>
get_network_flow_endpoints() const;
/// Set a callback to be called when each new message is received.
/**
* The callback receives a size_t which is the number of messages received
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if messages were received before any
* callback was set.
*
* Since this callback is called from the middleware, you should aim to make
* it fast and not blocking.
* If you need to do a lot of work or wait for some other event, you should
* spin it off to another thread, otherwise you risk blocking the middleware.
*
* Calling it again will clear any previously set callback.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the subscription
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rmw_subscription_set_on_new_message_callback
* \sa rcl_subscription_set_on_new_message_callback
*
* \param[in] callback functor to be called when a new message is received
*/
void
set_on_new_message_callback(std::function<void(size_t)> callback)
{
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_new_message_callback "
"is not callable.");
}
auto new_callback =
[callback, this](size_t number_of_messages) {
try {
callback(number_of_messages);
} catch (const std::exception & exception) {
RCLCPP_ERROR_STREAM(
node_logger_,
"rclcpp::SubscriptionBase@" << this <<
" caught " << rmw::impl::cpp::demangle(exception) <<
" exception in user-provided callback for the 'on new message' callback: " <<
exception.what());
} catch (...) {
RCLCPP_ERROR_STREAM(
node_logger_,
"rclcpp::SubscriptionBase@" << this <<
" caught unhandled exception in user-provided callback " <<
"for the 'on new message' callback");
}
};
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
// Set it temporarily to the new callback, while we replace the old one.
// This two-step setting, prevents a gap where the old std::function has
// been replaced but the middleware hasn't been told about the new one yet.
set_on_new_message_callback(
rclcpp::detail::cpp_callback_trampoline<decltype(new_callback), const void *, size_t>,
static_cast<const void *>(&new_callback));
// Store the std::function to keep it in scope, also overwrites the existing one.
on_new_message_callback_ = new_callback;
// Set it again, now using the permanent storage.
set_on_new_message_callback(
rclcpp::detail::cpp_callback_trampoline<
decltype(on_new_message_callback_), const void *, size_t>,
static_cast<const void *>(&on_new_message_callback_));
}
/// Unset the callback registered for new messages, if any.
void
clear_on_new_message_callback()
{
std::lock_guard<std::recursive_mutex> lock(callback_mutex_);
if (on_new_message_callback_) {
set_on_new_message_callback(nullptr, nullptr);
on_new_message_callback_ = nullptr;
}
}
/// Set a callback to be called when each new intra-process message is received.
/**
* The callback receives a size_t which is the number of messages received
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if messages were received before any
* callback was set.
*
* Calling it again will clear any previously set callback.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the subscription
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rclcpp::SubscriptionIntraProcessBase::set_on_ready_callback
*
* \param[in] callback functor to be called when a new message is received
*/
void
set_on_new_intra_process_message_callback(std::function<void(size_t)> callback)
{
if (!use_intra_process_) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Calling set_on_new_intra_process_message_callback for subscription with IPC disabled");
return;
}
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_new_intra_process_message_callback "
"is not callable.");
}
// The on_ready_callback signature has an extra `int` argument used to disambiguate between
// possible different entities within a generic waitable.
// We hide that detail to users of this method.
std::function<void(size_t, int)> new_callback = std::bind(callback, std::placeholders::_1);
subscription_intra_process_->set_on_ready_callback(new_callback);
}
/// Unset the callback registered for new intra-process messages, if any.
void
clear_on_new_intra_process_message_callback()
{
if (!use_intra_process_) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Calling clear_on_new_intra_process_message_callback for subscription with IPC disabled");
return;
}
subscription_intra_process_->clear_on_ready_callback();
}
/// Set a callback to be called when each new qos event instance occurs.
/**
* The callback receives a size_t which is the number of events that occurred
* since the last time this callback was called.
* Normally this is 1, but can be > 1 if events occurred before any
* callback was set.
*
* Since this callback is called from the middleware, you should aim to make
* it fast and not blocking.
* If you need to do a lot of work or wait for some other event, you should
* spin it off to another thread, otherwise you risk blocking the middleware.
*
* Calling it again will clear any previously set callback.
*
* An exception will be thrown if the callback is not callable.
*
* This function is thread-safe.
*
* If you want more information available in the callback, like the qos event
* or other information, you may use a lambda with captures or std::bind.
*
* \sa rclcpp::EventHandlerBase::set_on_ready_callback
*
* \param[in] callback functor to be called when a new event occurs
* \param[in] event_type identifier for the qos event we want to attach the callback to
*/
void
set_on_new_qos_event_callback(
std::function<void(size_t)> callback,
rcl_subscription_event_type_t event_type)
{
if (event_handlers_.count(event_type) == 0) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Calling set_on_new_qos_event_callback for non registered subscription event_type");
return;
}
if (!callback) {
throw std::invalid_argument(
"The callback passed to set_on_new_qos_event_callback "
"is not callable.");
}
// The on_ready_callback signature has an extra `int` argument used to disambiguate between
// possible different entities within a generic waitable.
// We hide that detail to users of this method.
std::function<void(size_t, int)> new_callback = std::bind(callback, std::placeholders::_1);
event_handlers_[event_type]->set_on_ready_callback(new_callback);
}
/// Unset the callback registered for new qos events, if any.
void
clear_on_new_qos_event_callback(rcl_subscription_event_type_t event_type)
{
if (event_handlers_.count(event_type) == 0) {
RCLCPP_WARN(
rclcpp::get_logger("rclcpp"),
"Calling clear_on_new_qos_event_callback for non registered event_type");
return;
}
event_handlers_[event_type]->clear_on_ready_callback();
}
/// Check if content filtered topic feature of the subscription instance is enabled.
/**
* \return boolean flag indicating if the content filtered topic of this subscription is enabled.
*/
RCLCPP_PUBLIC
bool
is_cft_enabled() const;
/// Set the filter expression and expression parameters for the subscription.
/**
* \param[in] filter_expression A filter expression to set.
* \sa ContentFilterOptions::filter_expression
* An empty string ("") will clear the content filter setting of the subscription.
* \param[in] expression_parameters Array of expression parameters to set.
* \sa ContentFilterOptions::expression_parameters
* \throws RCLBadAlloc if memory cannot be allocated
* \throws RCLError if an unexpect error occurs
*/
RCLCPP_PUBLIC
void
set_content_filter(
const std::string & filter_expression,
const std::vector<std::string> & expression_parameters = {});
/// Get the filter expression and expression parameters for the subscription.
/**
* \return rclcpp::ContentFilterOptions The content filter options to get.
* \throws RCLBadAlloc if memory cannot be allocated
* \throws RCLError if an unexpect error occurs
*/
RCLCPP_PUBLIC
rclcpp::ContentFilterOptions
get_content_filter() const;
// DYNAMIC TYPE ==================================================================================
// TODO(methylDragon): Reorder later
RCLCPP_PUBLIC
virtual
rclcpp::dynamic_typesupport::DynamicMessageType::SharedPtr
get_shared_dynamic_message_type() = 0;
RCLCPP_PUBLIC
virtual
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
get_shared_dynamic_message() = 0;
RCLCPP_PUBLIC
virtual
rclcpp::dynamic_typesupport::DynamicSerializationSupport::SharedPtr
get_shared_dynamic_serialization_support() = 0;
/// Borrow a new serialized message (this clones!)
/** \return Shared pointer to a rclcpp::dynamic_typesupport::DynamicMessage. */
RCLCPP_PUBLIC
virtual
rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr
create_dynamic_message() = 0;
RCLCPP_PUBLIC
virtual
void
return_dynamic_message(rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message) = 0;
RCLCPP_PUBLIC
virtual
void
handle_dynamic_message(
const rclcpp::dynamic_typesupport::DynamicMessage::SharedPtr & message,
const rclcpp::MessageInfo & message_info) = 0;
RCLCPP_PUBLIC
bool
take_dynamic_message(
rclcpp::dynamic_typesupport::DynamicMessage & message_out,
rclcpp::MessageInfo & message_info_out);
// ===============================================================================================
protected:
template<typename EventCallbackT>
void
@@ -290,45 +618,60 @@ protected:
const EventCallbackT & callback,
const rcl_subscription_event_type_t event_type)
{
auto handler = std::make_shared<QOSEventHandler<EventCallbackT,
auto handler = std::make_shared<EventHandler<EventCallbackT,
std::shared_ptr<rcl_subscription_t>>>(
callback,
rcl_subscription_event_init,
get_subscription_handle(),
event_type);
qos_events_in_use_by_wait_set_.insert(std::make_pair(handler.get(), false));
event_handlers_.emplace_back(handler);
event_handlers_.insert(std::make_pair(event_type, handler));
}
RCLCPP_PUBLIC
void default_incompatible_qos_callback(QOSRequestedIncompatibleQoSInfo & info) const;
RCLCPP_PUBLIC
void default_incompatible_type_callback(IncompatibleTypeInfo & info) const;
RCLCPP_PUBLIC
bool
matches_any_intra_process_publishers(const rmw_gid_t * sender_gid) const;
RCLCPP_PUBLIC
void
set_on_new_message_callback(rcl_event_callback_t callback, const void * user_data);
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_;
rclcpp::Logger node_logger_;
std::vector<std::shared_ptr<rclcpp::QOSEventHandlerBase>> event_handlers_;
std::unordered_map<rcl_subscription_event_type_t,
std::shared_ptr<rclcpp::EventHandlerBase>> event_handlers_;
bool use_intra_process_;
IntraProcessManagerWeakPtr weak_ipm_;
uint64_t intra_process_subscription_id_;
std::shared_ptr<rclcpp::experimental::SubscriptionIntraProcessBase> subscription_intra_process_;
const SubscriptionEventCallbacks event_callbacks_;
private:
RCLCPP_DISABLE_COPY(SubscriptionBase)
rosidl_message_type_support_t type_support_;
bool is_serialized_;
DeliveredMessageKind delivered_message_type_;
std::atomic<bool> subscription_in_use_by_wait_set_{false};
std::atomic<bool> intra_process_subscription_waitable_in_use_by_wait_set_{false};
std::unordered_map<rclcpp::QOSEventHandlerBase *,
std::unordered_map<rclcpp::EventHandlerBase *,
std::atomic<bool>> qos_events_in_use_by_wait_set_;
std::recursive_mutex callback_mutex_;
std::function<void(size_t)> on_new_message_callback_{nullptr};
};
} // namespace rclcpp

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