Compare commits

...

194 Commits

Author SHA1 Message Date
Dirk Thomas
9dd3d4c3c5 0.0.2 2017-06-30 15:11:46 -07:00
William Woodall
9c008267ef add rcutils tag file reference (#341) 2017-06-27 12:02:41 -07:00
Chris Lalancette
b8d72d682a Remove a constructor that we can't test. (#340)
There are currently no paths that lead to it, and it has
a bug anyway; if a large enough value is passed into sec,
then we will overflow sec on the multiply.  Just remove it
since we can't reach the code anyway.

Signed-off-by: Chris Lalancette <clalancette@openrobotics.org>
2017-06-21 09:24:01 -04:00
William Woodall
5a99bbdc6e refactor to support multiple types and no_demangle (#339)
* refactor to support multiple types and no_demangle

* add get_service_names_and_types
2017-06-16 18:03:16 -07:00
Karsten Knese
99441d8494 comply with rcl allocator (#336)
* comply with rcl allocator

* do not throw exception in destructor
2017-06-16 14:44:25 -07:00
Mikael Arguedas
756ef6886d use CMAKE_X_STANDARD and check compiler rather than platform 2017-06-16 13:24:30 -07:00
Karsten Knese
f396ff2bac string array takes allocator (#338) 2017-06-14 11:39:22 -07:00
Karsten Knese
2847e4aefd expose explicit transition calls (#334) 2017-06-05 18:24:12 -07:00
Mikael Arguedas
a4f00dc574 include rcutils/time (#333)
* include rcutils/time

* include rcl/time for time source enum
2017-06-05 14:42:36 -07:00
Dirk Thomas
c0a78bac37 Merge pull request #332 from ros2/move_time
use time from rcutils
2017-06-01 22:11:15 -07:00
Dirk Thomas
bfa09fb78c use steady and system time from rcutils 2017-06-01 21:01:38 -07:00
Karsten Knese
454d38776c Topic names (#331)
* expand topic name before invoking count pub/sub

* convenience function for get_namespace()

* uncrustify

* typo

* add get_namespace() test

* add get_namespace() for lifecycle
2017-06-01 14:01:18 -07:00
William Woodall
b90676871d Use namespaces (#328)
* add expand_topic_or_service_name()

* use namespace in intra process

(actual change to topic names in next commit)

* catch and report name issues

for node names, namespaces, and topic/service names

* address comment
2017-05-30 18:25:11 -07:00
Karsten Knese
708903e5df Warn unused (#327)
* comply with unused warnings

* fix flakiness and add test for transitions

* mark flaky test

* duplicate const char * in State constructor

* linters

* correct year in license

* mark flaky test
2017-05-25 19:52:50 -07:00
William Woodall
5777bbee79 add method to reset a timer in TimerBase (#326) 2017-05-10 00:34:52 -07:00
gerkey
100417a98d fix race conditions in guard condition handling (#325) 2017-04-27 18:47:13 -07:00
dhood
675ad04c76 Update spin error message to match function name (#323) 2017-04-25 09:56:01 -07:00
William Woodall
3e6a6d2781 rename {c_}utilities to rcutils (#322) 2017-04-20 11:15:10 -07:00
William Woodall
d2112b294b refactor to pass allocator to some functions in rcl (#321) 2017-04-19 12:37:55 -07:00
Karsten Knese
81b8255e61 use string_array_t from c_utilities package (#320)
* use c_utilities package

* compare return value to utilities_ret_ok

* fix error handling

* better error handling
2017-04-14 16:18:43 -07:00
William Woodall
e6e1848b97 expose node namespace in API and pass to rcl (#316)
* expose node namespace in API and pass to rcl

* name_space -> namespace_
2017-04-08 02:04:41 -07:00
William Woodall
dbe674deb7 compare unsigned to unsigned (#313) 2017-03-22 00:42:37 -07:00
Karsten Knese
c07aee5cf0 Rclcpp time (#311)
* initial commit for rclcpp::time::now()

* switch between times

* introduce time class

* add rclcpp/time.hpp to rclcpp/rclcpp.hpp

* throw exceptions on time error

* fix test_time to catch exceptions

* explicit one-parameter constructor

* fix msvc compiler warnings

* address review comments

* cleanup includes

* re-add todo for fixing test once ros-time is there
2017-03-21 17:41:49 -07:00
William Woodall
2009ca676b add a new Node::get_parameter() with a default value (#309)
* add a new Node::get_parameter() with a default value

* update function parameter name (signature)

* update function parameter name (definition)

* rename new function to get_parameter_or

* rename arg "parameter" to "value" and fix get_parameter

* add set_parameter_if_not_set

* add some comments to clarify logic in set_parameters_atomically

* uncrustify

* address comments

* add some docs for get_parameter*
2017-03-20 17:04:12 -07:00
Mikael Arguedas
71f5b7fe5b Use -Wpedantic (#306)
* add /W4 flag for windows

* use uint8 like defined in messages: fix warning C4244

* fix sign size_t comparison

* add only pedantic, not W4, deal with windows another day

* another sign compare warning
2017-02-27 21:07:57 -08:00
William Woodall
ce146cfdba add a publish method with the const MessageT * signature (#307) 2017-02-27 18:48:01 -08:00
Karsten Knese
dc6b15983a Rosnode list (#304)
* expose list of nodes

* uncrustify

* review comments

* warn for memory leak

* extend year of copyright
2017-01-30 10:30:57 -08:00
Dirk Thomas
4d8b60feb5 Merge pull request #302 from ros2/use_rmw_impl
use rmw implementation
2017-01-09 08:54:27 -08:00
Dirk Thomas
78dfef75b4 remove obsolete CMake functions 2017-01-06 15:46:08 -08:00
dhood
8ca02c9d37 Fix cmake for appending c/cxx flags (#300) 2017-01-06 07:48:47 +10:00
Dirk Thomas
fee3118bdd use rmw implementation 2017-01-05 12:16:12 -08:00
Mikael Arguedas
9603db7b7d dependency order (#301) 2017-01-05 17:27:53 +01:00
Dirk Thomas
e3d4995383 Merge pull request #299 from ros2/single_ts_shortcut
fix dependencies
2016-12-28 13:13:18 -08:00
Dirk Thomas
197c17ae99 fix dependencies 2016-12-28 12:12:05 -08:00
Dirk Thomas
d9673a556d Merge pull request #298 from ros2/typesupport_c_reloaded
use rosidl_typesupport_c
2016-12-28 09:02:43 -08:00
Rohan Agrawal
c0af872c18 Warn on second call of register_param_change_callback (#297) 2016-12-22 13:56:44 -08:00
Dirk Thomas
9e309db793 use rosidl_typesupport_c 2016-12-22 09:47:57 -08:00
William Woodall
321e0b61b0 Doxygen setup (#293)
* basic doxygen configuration

* fix up documentation

* change default value of PROJECT_NUMBER

* more generalization

* fixup

* fixup

* avoid displaying RCLCPP_PUBLIC on all functions

* main page
2016-12-20 18:30:18 -08:00
Dirk Thomas
0515e7aaf2 replace deprecated <CONFIGURATION> with <CONFIG> 2016-12-20 11:38:44 -08:00
Guillaume Papin
d00a441195 move user-defined literals in their own namespace (#284)
* isolate chrono literals in literals.hpp

* rclcpp.hpp: remove 'using namespace rclcpp::literals'

The examples have been migrated to the new namespace.

* literals: constexpr, make return types consistent with function body

_ms returned nanoseconds instead of milliseconds.

A few return types where using integral return type
for floating point literals.

* remove literals in favor of std::chrono_literals
2016-12-17 02:16:43 -08:00
Karsten Knese
2c6d95946e add rclcpp lifecycle
* initial state machine implementation

(fix) correctly initialize default state machine

uncrustify

(dev) first high level api interface

src/default_state_machine.c

(fix) correctly initialize arrays in statemachine

(dev) deactivate/activate publisher demo

(dev) initial state machine implementation in rcl

* (dev) demo application for a managed lifecycle node

* add visibility control

* correct install of c-library

* fix compilation on windows

* refactoring of external/internal api

* (dev) generate static functions for c-callback

* (fix) correct typo

* (dev) cleanup for c-statemachine

(dev) cleanup for c-statemachine

* (dev) cpp callback map

* (dev) mv source file into project folders

* (dev) more helper functions for valid transition

* (dev) pimpl implementation for cpp lifecyclemanager

* (dev) register non-default callback functions

* (dev) cleanup lifecycle node to serve as base class

* (dev) new my_node child of lifecyclenode for demo purpose

update

stuff

(cleanup) remove unused comments

(fix) correct dllexport in windows

(fix) correctly install libraries

(fix) uncrustify

(dev) composition over inheritance

(dev) publish notification in state_machine transition

 (dev) lifecycle talker + listener demo for notification

(dev) custom transition message generation

(dev) publish transition message on state change

(dev) correctly malloc/free c data structures

(fix) use single thread executor

(dev) use services for get state

(fix) set freed pointer to NULL

(dev) add change state service

(dev) introduce services: get_state and change_state in LM

(dev) asynchronous caller script for service client

(fix) correct dllexport for pimpl

(dev) correct constness

(dev) concatenate function for topic

(fix) uncrustify

prepare new service api

(tmp) refactor stash

(fix) correctly concatenate topics

(fix) correctly initialize Service wo/ copy

(dev) call both service types

extract demo files

(fix) remove debug prints

(dev) change to lifecycle_msgs

(refactor) extract rcl_lifecycle package

(refactor) extract lifecycle demos

(fix) address review comments

(fix) address review comments

(fix) pass shared_ptr by value

(fix) make find_package(rmw) required

(fix) return to shared node handle pointer

(refactor) attach sm to lifecycle node and disable lc_manager

(dev) construct service from existing rcl_service_t

(refactor) extract method for adding a service to a node

(fix) stop mock msgs from being installed

service takes rcl_node_t*

correct typo

add_service has to be public

uncrustify

initial state machine implementation

(fix) correctly initialize default state machine

uncrustify

(dev) first high level api interface

src/default_state_machine.c

(fix) correctly initialize arrays in statemachine

(dev) deactivate/activate publisher demo

(dev) initial state machine implementation in rcl

(dev) demo application for a managed lifecycle node

add visibility control

correct install of c-library

fix compilation on windows

refactoring of external/internal api

(dev) generate static functions for c-callback

(fix) correct typo

(dev) cleanup for c-statemachine

(dev) cleanup for c-statemachine

(dev) cpp callback map

(dev) mv source file into project folders

(dev) more helper functions for valid transition

(dev) pimpl implementation for cpp lifecyclemanager

(dev) register non-default callback functions

(dev) cleanup lifecycle node to serve as base class

(dev) new my_node child of lifecyclenode for demo purpose

update

stuff

(cleanup) remove unused comments

(fix) correct dllexport in windows

(fix) correctly install libraries

(fix) uncrustify

(dev) composition over inheritance

(dev) publish notification in state_machine transition

 (dev) lifecycle talker + listener demo for notification

(dev) custom transition message generation

(dev) publish transition message on state change

(dev) correctly malloc/free c data structures

(fix) use single thread executor

(dev) use services for get state

(fix) set freed pointer to NULL

(dev) add change state service

(dev) introduce services: get_state and change_state in LM

(dev) asynchronous caller script for service client

(fix) correct dllexport for pimpl

(dev) correct constness

(dev) concatenate function for topic

(fix) uncrustify

prepare new service api

(tmp) refactor stash

* (dev) construct service from existing rcl_service_t

* service takes rcl_node_t*

* correct typo

* add_service has to be public

* uncrustify

* (fix) correctly concatenate topics

* (fix) correctly initialize Service wo/ copy

* (dev) call both service types

* extract demo files

* (fix) remove debug prints

* (dev) change to lifecycle_msgs

* (refactor) extract rcl_lifecycle package

* (refactor) extract lifecycle demos

* (fix) address review comments

(fix) address review comments

* (fix) make find_package(rmw) required

* (refactor) attach sm to lifecycle node and disable lc_manager

* (fix) adjust code to rcl_test refactor

* (dev) remove unused deps

* (rebase) merge commit

* (bugfix) correct rcl_ret_t error handling

* (fix) depedencies

* (refactor) change to lifecycle_msgs

* (fix) correct find_rcl

* (refactor) comply for new state machine

* visibility control and test api

* (rebase) change to new typesupport

* uncrustify'

* fix visibility control

* (fix) correct whitespace

* (fix) unused variable

* comparison signed and unsigned

* get_state returns complete state

* get_available_states service

* new service msgs

* get available states and transitions api

* (broken) state after rebase, does not compile demos

* fix the way lifecycle node impl is included

* fixed rebase compilation errors

* remove copy&paste comment

* remove empty line

* (test) register custom callbacks

* (dev) return codes

* style

* test for exception handling

* refacotr new state machine

* c++14

* change exception message for windows ci bug

change exception message for windows ci bug
2016-12-14 09:29:27 -08:00
Morgan Quigley
d241a730fe c++14 (#287) 2016-12-13 14:41:22 -08:00
William Woodall
cc98d00add remove unused include (#291) 2016-12-12 11:56:59 -08:00
William Woodall
e2f53b09b4 fix the return type of create_subscription (#290) 2016-12-09 22:25:09 -08:00
William Woodall
734ac278db break Node into several separate interfaces (#277)
* add the NodeBaseInterface and impl NodeBase

* refactor rclcpp to use NodeBaseInterface

* triggering a guard condition is not const

* remove unnecessary pure virtual destructor

* remove unused private member, style

* create NodeTopics interface, refactor pub/sub

* add convenience functions to fix API breaks

* fix compilation errors from NodeTopics refactor

* move "Event" based exceptions to exceptions.hpp

* add the NodeGraphInterface and related API's

* update node and graph_listener to use NodeGraph API

* initialize node_topics_ and node_graph_ in Node

* remove methods from Node and reorganize the order

the removed methods are really low level and still
available via their respective Node*Interface class

* add the NodeServices API and implementation

* add the NodeParameters API and refactor Node

* mixups

* fixup NodeParameters constructor

* added NodeTimers API and refactor Node

* make new create_publisher and create_subscription free template functions

* fixup

* fixup

* fixup

* fixup share pointer to node in any_executable

* free env value before throwing on Windows

* uncrustify and cpplint

* address constness issues

* do not store the topic name as a std::string in subscription

* fixes to support const char * topic name

* fix incomplete type specification, which fails on Windows

* refactor after rebase from type support changes

* fixup Windows build

* fix template issues on Windows

* uncrustify

* remove the unnecessary callback group argument from the add_publisher func

* remove unnecessary using = directive

* do not store node name in C++

* fix client and service creation in Node constructor

* fix include orders
2016-12-09 17:09:29 -08:00
Dirk Thomas
2309e5e250 Merge pull request #285 from ros2/typesupport_reloaded
use rosidl_typesupport_cpp
2016-12-08 14:00:42 -08:00
Dirk Thomas
3fe1924c63 pass custom LIBRARY_NAME 2016-12-06 14:54:31 -08:00
Dirk Thomas
3405f489d3 use rosidl_typesupport_cpp 2016-12-06 11:07:11 -08:00
Karsten Knese
1b5168195b construct service from existing rcl_service_t (#279)
* (dev) construct service from existing rcl_service_t

* (refactor) extract method for adding a service to a node

* (fix) stop mock msgs from being installed

* service takes rcl_node_t*

* correct typo

* add_service has to be public

* uncrustify

* correctly initialize service_handle

* (fix) address review comments

* (fix) pass shared pointer by value

* (fix) return to shared node handle pointer

* (fix) make find_package(rmw) required

* style

* (revert) leave c++11 flags within CXX flags

* (fix) unused variable warning

* (fix) remove unnecessary if in cmake
2016-12-02 01:05:59 -08:00
geoffviola
a987f8d015 removed extra semi-colon (#253)
* removed extra semi-colon

* added -Wpedantic flag for GCC

* added same warnings for clang

* simplified CMake command
2016-11-28 14:11:22 -08:00
Geoffrey Biggs
aa2d0a3954 Fix has_invalid_weak_nodes (#266)
* Fix #264

* Corrected test comment
2016-11-17 09:59:50 -08:00
Dirk Thomas
5894a9cd4e Merge pull request #270 from ros2/composition
fix allocator type
2016-11-10 16:55:02 -08:00
Dirk Thomas
80fc2a59cd Merge pull request #271 from ros2/fix_error_reporting
fix error reporting for services
2016-11-10 13:25:29 -08:00
Dirk Thomas
d12154b1f9 fix error reporting for services 2016-11-10 11:13:19 -08:00
Dirk Thomas
a06a397cc6 fix allocator type 2016-11-10 10:09:24 -08:00
Dirk Thomas
4c876d5966 fix wrong variable name in docblock 2016-11-08 15:21:48 -08:00
Mikael Arguedas
da14d88cd6 line length (#269) 2016-11-07 18:47:05 -08:00
Dirk Thomas
01317def07 Merge pull request #268 from ros2/composition
add rclcpp_register_node_plugins macro
2016-11-07 15:41:14 -08:00
Dirk Thomas
74505f25fa add rclcpp_register_node_plugins macro 2016-11-07 15:21:53 -08:00
Dirk Thomas
ec71e6562e move CMake function into separate file 2016-11-07 10:55:17 -08:00
Karsten Knese
7f714a8601 open Node/Publisher API for allowing inheritance (#258)
* (dev) template create_publisher with publisher type

* (dev) template publisher type for dynamic publisher type instantiation

* (dev) make Publisher::publish function virtual

* (fix) uncrustify

* different indentation of long template declaration
2016-11-01 15:07:58 -07:00
William Woodall
7494350ad2 always check if the service is available, even if the graph event wasn't triggered (#262)
* always check if the service is available, even if the graph event wasn't triggered

* more descriptive comment

* Even more descriptive in case the link ever breaks
2016-10-28 18:31:15 -07:00
Rafał Kozik
d158dd46db CallbackGroup keeps now WeakPtrs to Services. (#261)
It was not possible to shutdown a service when there
was a shared pointer preventing destructor from being called.
2016-10-28 15:59:46 -07:00
Mikael Arguedas
a71ce25a5a Zero init topic names and types (#260)
* use zero init topic_names_and_type struct

* use the right arguments

* line length
2016-10-27 19:02:35 -07:00
Dirk Thomas
f28bb11078 Merge pull request #255 from ros2/fix_cpplint
comply with stricter cpplint rules
2016-10-03 16:17:03 -07:00
Dirk Thomas
29a1bd44dc comply with stricter cpplint rules 2016-10-01 11:23:53 -07:00
William Woodall
4e74edf8d4 fixup comment 2016-09-15 15:41:48 -07:00
William Woodall
6ea435f743 Issue 251 wjwwood (#252)
* removed warning in windows

* removed warning in windows

* fixup
2016-09-06 16:35:10 -07:00
Rohan Agrawal
902d558e64 Get single parameter (#245)
* added function to get parameter by exact name

* added ros1 style get_parameter for parameter variant

* added ros1 style get_param for non-variant types

* Make the get_parameter functions call a private base function

* Parameter Variant requires name in constructor

* Cleaned up to no longer need private function

* Made exception message more clear
2016-08-01 19:49:21 -07:00
Rohan Agrawal
1402715d76 Added basic hook for parameter changes (#244)
* Added basic hook for parameter changes

* Rename hook and add docblock
2016-07-28 18:01:24 -07:00
Dirk Thomas
8c5f6e4e06 Merge pull request #242 from SantiagoMunoz/setparameters
Added missing variable initialization in Node constructor
2016-07-20 08:17:16 -07:00
Santiago Munoz
2401c0f197 Added missing variable initialization in Node constructor 2016-07-20 16:56:33 +02:00
gerkey
fc0d539837 add parameter helpers (redo of #233) (#237)
* add parameter helpers

* respond to comments

* remove unnecessary indent comments

* replace temp variable assignment with explicit constructor invocation
2016-07-08 15:45:05 -07:00
gerkey
ea76716982 use the param profile for param services (#236)
* use the param profile for param services

* expose qos to users of param clients / services
2016-07-08 13:39:18 -07:00
Dirk Thomas
8251b84f68 update schema url 2016-06-28 20:02:25 -07:00
Esteve Fernandez
058de29628 Implement std::to_string for Android (#231) 2016-06-28 16:20:59 -07:00
Dirk Thomas
e8600d1b80 add schema to manifest files 2016-06-26 17:15:05 -07:00
William Woodall
5e2a76cc20 add wait_for_service() and service_is_ready() to Client (#222)
* add wait_for_service() and service_is_ready() to Client

* fix compile on Linux (maybe Windows)

* use visibility macros for Windows

* prevent unreasonable uncrustify change

* fixup comment

* add GraphListener::is_shutdown()

* disable copy on GraphListener

* use weak_ptr<Node> in client, throw if invalid

* ensure blocking wait_for_service wakes on rclcpp::shutdown/sigint

* rethrow exceptions after reporting them in thread

* lock ~Node() against notify_graph_change()

this essentially protects the notify_guard_condition_

* adjust thread sync strategy

* style

* moving initialization of wait set around, fix double free

* only fini wait set if started

* use rclcpp::shutdown to ensure graph listener resources clean up before static destruction

* uncrustify
2016-06-22 20:18:46 -07:00
gerkey
3553107823 Allow empty prefix list to get all params; check depth correctly. (#230)
* Allow empty prefix list to get all params; check depth correctly.

* use enum instead of constant
2016-06-20 17:55:57 -07:00
Dirk Thomas
759b063db5 Merge pull request #227 from ros2/cmake35
require CMake 3.5
2016-06-16 12:18:59 -07:00
Dirk Thomas
aeb3c55894 remove trailing spaces from comparisons, obsolete quotes and explicit variable expansion 2016-06-16 09:19:37 -07:00
gerkey
bf6394004c Fix style (#229) 2016-06-16 08:44:12 -07:00
gerkey
39f0a1b93f Give a different signal guard condition for each waitset (#226)
Fixes #225.
2016-06-15 13:14:44 -07:00
Dirk Thomas
7a5285a3d0 require CMake 3.5 2016-06-15 11:35:56 -07:00
gerkey
3c45a571e7 Merge pull request #223 from ros2/mutex_callbackgroup
Add mutex to protect vectors of pointers in callbackgroup
2016-06-06 17:36:25 -07:00
Brian Gerkey
af0b1e6b07 Add mutex to protect vectors of pointers in callbackgroup 2016-06-06 17:29:32 -07:00
Dirk Thomas
0f58c5305c fix spelling in comment 2016-05-31 09:06:07 -07:00
dhood
a5fa8277f3 Merge pull request #220 from dhood/test-linking-runtime
Regression test for #213
2016-05-25 02:44:16 +01:00
dhood
458019bdff Regression test for #213 2016-05-17 17:40:28 -07:00
Dirk Thomas
0a9f2e26a2 Merge pull request #215 from ros2/ctest_build_testing
use CTest BUILD_TESTING
2016-05-02 10:11:35 -07:00
Dirk Thomas
45f43ef523 use CTest BUILD_TESTING 2016-04-28 18:45:08 -07:00
Dirk Thomas
c99b9b9734 Merge pull request #213 from ros2/fix_wrong_link_library
fix wrong link library for test_rate gtest
2016-04-27 08:13:24 -07:00
Dirk Thomas
b34a5f5504 fix wrong link library for test_rate gtest 2016-04-26 17:42:57 -07:00
Jackie Kay
6adfb917a9 Virtual destructors (#212) 2016-04-25 16:31:03 -07:00
Jackie Kay
e961189be8 Refactor to use rcl (#207) 2016-04-24 21:25:19 +00:00
Jackie Kay
6bcd9db4d6 Remove fixed guard conditions, add notify guard conditions
* No more fixed guard conditions.

* Add notify guard condition to nodes
2016-04-01 14:07:07 -07:00
Jackie Kay
2be9568498 Merge pull request #208 from ros2/timer_specialization
Only enable Timer if clock type is steady
2016-03-15 18:20:33 -07:00
Jackie Kay
e7833fa709 Only enable Timer if clock type is steady 2016-03-15 16:23:39 -07:00
William Woodall
02311dee4c Merge pull request #202 from ros2/fix_flaky_subscription_and_spinning_test
Fix flaky subscription and spinning test
2016-03-08 23:15:38 +00:00
William Woodall
82139f1a12 refactor and test spin_until_future_complete 2016-03-08 15:10:05 -08:00
William Woodall
e8f9344015 refactor executor.spin_until_future_complete 2016-03-03 18:14:18 -08:00
William Woodall
0c826497f1 convert enum to enum class and provide to_string 2016-03-03 18:13:46 -08:00
Jackie Kay
249b7d80d8 Merge pull request #199 from ros2/request_header
Don't type-erase request header
2016-02-29 16:59:08 -08:00
Jackie Kay
f6ce2d8dc5 Don't type-erase request header 2016-02-29 16:57:53 -08:00
Dirk Thomas
69f7bca85d Merge pull request #193 from ros2/issue_192
potential fix for issue 192
2016-02-16 14:19:02 -08:00
Jackie Kay
4a04fe8b4a potential fix for issue 192 2016-02-16 13:44:38 -08:00
Jackie Kay
6fbf3f8c5f Merge pull request #194 from ros2/get_rclcpp_info_cleanup
Cleanup in get_rclcpp_information
2016-02-11 15:07:29 -08:00
Jackie Kay
9f84273467 Cleanup in get_rclcpp_information 2016-02-11 15:07:08 -08:00
Jackie Kay
9d754a70a2 Merge pull request #166 from ros2/waitset_handle
Store handle of rmw_waitset_t in Executor
2016-01-12 17:46:35 -08:00
Jackie Kay
4b0ad21b3d Adjust for new rmw_waitset_t API 2016-01-12 17:42:34 -08:00
gerkey
6ec5e8e974 Merge pull request #191 from ros2/fix_rate_test
fix rate test
2015-12-31 16:17:22 -08:00
Jenkins @ ROS 2
be0be759ec fix rate test 2015-12-31 16:16:51 -08:00
Tully Foote
4d47ef30ae Merge pull request #182 from ros2/rate_tests
Add unit tests for rclcpp Rate objects
2015-12-18 10:21:59 -08:00
gerkey
496c3e1798 Merge pull request #186 from ros2/remove_sleep
remove excess sleep
2015-12-18 09:13:02 -08:00
Tully Foote
8af64b95e5 fixing comment 2015-12-18 03:41:05 -08:00
Tully Foote
fac550cc82 adding coverage and an epsilon 2015-12-18 03:41:05 -08:00
Tully Foote
ebdf394dfa updating tests to be less vulnerable to load 2015-12-18 03:41:05 -08:00
Tully Foote
aa818076f8 softening tolerances on timing to pass on osx 2015-12-18 03:41:05 -08:00
Tully Foote
6d79b5e0e3 adding basic unit tests for rate 2015-12-18 03:41:04 -08:00
William Woodall
300c7ca137 Merge pull request #180 from ros2/rcl_refactor
adapt to new rmw changes
2015-12-17 19:47:32 -08:00
Brian Gerkey
a7a61119fd remove unused data structure for which unprotected concurrent access was causing segfaults and deadlocks on OSX 2015-12-17 19:06:28 -08:00
William Woodall
a8d9d894c3 adapt to new rmw changes 2015-12-17 17:58:00 -08:00
Jackie Kay
1fe47b717f Merge pull request #187 from ros2/set_allocations
Avoid reinitializing allocator
2015-12-17 16:10:43 -08:00
Esteve Fernandez
c878e966e3 Merge pull request #188 from ros2/move_callback_timer
Use move semantics to store callback and perfect forwarding to pass it down to GenericTimer
2015-12-17 15:59:23 -08:00
Esteve Fernandez
38e0911b8e Use move semantics to store callback and perfect forwarding to pass it down to GenericTimer 2015-12-17 15:21:11 -08:00
Jackie Kay
16da43e1f0 Avoid reinitializing allocator 2015-12-17 11:21:52 -08:00
Tully Foote
7422cda6c6 remove excess sleep identified in #169 2015-12-17 11:07:00 -08:00
Jackie Kay
bcbdd00212 Merge pull request #169 from ros2/multithreaded_idle_thread
Get rid of idle thread in MultiThreadedExecutor
2015-12-17 10:32:37 -08:00
Jackie Kay
dd1f38b4c8 Get rid of idle thread in MultiThreadedExecutor 2015-12-17 10:32:15 -08:00
Esteve Fernandez
8abdf6f670 Merge pull request #184 from ros2/std_bind_function_traits_free_functions
Added support for free functions and std::bind
2015-12-16 17:13:54 -08:00
Esteve Fernandez
1577ab2992 Added support for free functions and std::bind 2015-12-16 11:10:09 -08:00
Esteve Fernandez
534ae69ed5 Merge pull request #183 from ros2/std_bind_function_traits
Added support for std::bind (redux)
2015-12-15 17:32:29 -08:00
Tully Foote
4f9f152ce8 Merge pull request #178 from ros2/inline_helper_functions
inline helper functions in rclcpp.hpp to avoid multiple definitions
2015-12-15 17:23:05 -08:00
Esteve Fernandez
b22fca5780 Add support for std::bind 2015-12-15 17:19:25 -08:00
William Woodall
51a14baae2 Merge pull request #177 from ros2/remove_old_files
remove vestigial file and cmake logic
2015-12-14 00:45:45 -08:00
Tully Foote
6fd373d274 inline helper functions in rclcpp.hpp to avoid multiple definitions 2015-12-13 23:25:03 -08:00
William Woodall
46d18d5627 remove vestigial file and cmake logic 2015-12-12 18:02:49 -08:00
Dirk Thomas
9cd129c70f Merge pull request #172 from ros2/refactor_typesupport
refactor typesupport
2015-12-12 00:12:55 -08:00
Dirk Thomas
e38ac73c69 refactor typesupport 2015-12-11 15:09:09 -08:00
Jackie Kay
3688bb3215 Merge pull request #171 from ros2/add_additional_mutex
Lock mapped_ring_buffer
2015-12-10 13:55:09 -08:00
Jackie Kay
4f929bd257 Lock mapped_ring_buffer 2015-12-10 13:54:30 -08:00
Jackie Kay
c2554d61bc Merge pull request #170 from ros2/remove_extraneous_mutex
Remove extraneous mutex
2015-12-07 17:36:57 -08:00
Jackie Kay
6d2f8ae634 Remove mutex in publisher 2015-12-07 17:35:51 -08:00
Esteve Fernandez
67dcd9fe49 Merge pull request #168 from ros2/avoid_compiler_warning
Avoid unused return value warning
2015-12-04 12:27:24 -08:00
Esteve Fernandez
f0fd292ccb Avoid unused return value warning 2015-12-04 11:09:11 -08:00
Dirk Thomas
13d36a1bde Merge pull request #167 from ros2/fix_windows_context
Add RCLCPP_PUBLIC to Context constructor
2015-12-03 12:18:41 -08:00
Jackie Kay
74002333e6 Add RCLCPP_PUBLIC to Context constructor 2015-12-03 11:58:17 -08:00
Jackie Kay
f73ebcb8d7 Merge pull request #165 from ros2/intra_process_lock
Make intra-process manager thread safe, rename IPMState to IPMImpl
2015-12-03 09:34:06 -08:00
Jackie Kay
67151def41 Finish renaming, add mutex to store and take 2015-12-03 09:33:11 -08:00
Jackie Kay
7fdbc4a89a Rename intra_process_manager_state to intra_process_manager_impl 2015-12-01 11:57:13 -08:00
Jackie Kay
f5e5acd06e Merge pull request #163 from ros2/export_scope_exit
Move scope exit to include folder
2015-11-30 15:42:43 -08:00
Jackie Kay
741d701350 rename include 2015-11-30 15:40:41 -08:00
Jackie Kay
bfa5f2cdcb Move scope_exit 2015-11-30 15:40:37 -08:00
Dirk Thomas
7f35c72064 Merge pull request #162 from ros2/plain_shared_ptr
use std::shared_ptr type directly instead of using message specific type
2015-11-24 17:32:49 -08:00
Dirk Thomas
dbd2f6ac6a use std::shared_ptr type directly instead of using message specific type 2015-11-24 17:07:38 -08:00
Esteve Fernandez
12859aca2c Merge pull request #161 from ros2/fix_import_symbols
Fix cpplint and windows build
2015-11-24 10:30:19 -08:00
Jackie Kay
481d58bb6e Fix cpplint and windows build 2015-11-24 10:04:26 -08:00
Jackie Kay
2f6fc44d43 Merge pull request #159 from ros2/finite_timer
Add alternative callback signature to timer for accepting a Timer reference
2015-11-23 16:31:17 -08:00
Esteve Fernandez
82d895c749 Merge pull request #160 from ros2/function-traits-return-type
Added support for deducing the return type of a functor
2015-11-23 15:02:22 -08:00
Jackie Kay
3a5bbffc65 Implement multiple callback signatures for timer 2015-11-23 14:57:17 -08:00
Esteve Fernandez
eff6500aca Added support for deducing the return type of a functor 2015-11-23 13:37:43 -08:00
Esteve Fernandez
03697d149a Fix uncrustify warning 2015-11-19 14:00:47 -08:00
Jackie Kay
6e596336df Merge pull request #158 from ros2/redundant_allocator_constructions
Remove redundant vector initializations in allocator_memory_strategy
2015-11-19 10:52:51 -08:00
Jackie Kay
ff9b492041 remove redundant vector instantiation, replace with erase-remove idiom 2015-11-19 10:51:56 -08:00
Esteve Fernandez
10ce7a3bd6 Merge pull request #157 from ros2/return-request
Added async_send_request_return_request to return the originating request
2015-11-18 18:11:08 -08:00
Esteve Fernandez
26d52d949e Move function_traits templates to a separate namespace 2015-11-18 18:09:22 -08:00
Esteve Fernandez
e95d3f249d Added async_send_request_return_request to return the originating request 2015-11-18 18:09:20 -08:00
William Woodall
39c663ea64 Merge pull request #153 from ros2/cancel
Implement Executor::cancel
2015-11-17 15:33:07 -08:00
William Woodall
1c9eb0b367 only set scope exit after it was determined that spinning was changed to true 2015-11-17 11:32:58 -08:00
William Woodall
f1e7ea5ca0 remove redundant calls to set spinning false 2015-11-16 17:30:01 -08:00
William Woodall
2b342357d9 use scope exit to ensure spinning is reset 2015-11-16 17:21:26 -08:00
William Woodall
9dce2808ea add implementation of scope exit, aka scope guard 2015-11-16 17:21:18 -08:00
Jackie Kay
c469a8a2e9 Cancel instead of cancels 2015-11-13 10:17:45 -08:00
Jackie Kay
eb3a793eb7 Merge pull request #155 from ros2/cancel_wjwwood
refactor executor::cancel to use a spinning state
2015-11-13 09:28:26 -08:00
William Woodall
5bd71c1f80 refactor executor::cancel to use a spinning state 2015-11-12 17:39:10 -08:00
Jackie Kay
0a478e5233 Implement cancel 2015-11-12 16:29:17 -08:00
Jackie Kay
fabea6b4b6 Merge pull request #151 from ros2/qos_services
Expose QoS profile in create_service and create_client
2015-11-12 09:38:18 -08:00
Jackie Kay
2b8d38d012 Expose QoS profile in create_service and create_client 2015-11-11 18:41:09 -08:00
Jackie Kay
24f9df6102 Merge pull request #149 from ros2/executor_spin_future
Add spin_once and spin_until_future_complete to Executor
2015-11-06 16:21:09 -08:00
Jackie Kay
660d762072 Add spin_once and spin_until_future_complete to Executor 2015-11-06 16:20:17 -08:00
Esteve Fernandez
f4a094afc8 Merge pull request #147 from ros2/perfect-forwarding
Pass callbacks as universal references
2015-11-06 12:04:28 -08:00
Esteve Fernandez
e42d7d7044 Merge pull request #148 from ros2/cpplint
Fix cpplint warnings
2015-11-06 10:12:46 -08:00
Esteve Fernandez
39b66fa0b5 Fix cpplint warnings 2015-11-06 10:03:39 -08:00
Esteve Fernandez
8c47f6151b Pass callbacks as universal references 2015-11-06 09:49:31 -08:00
Jackie Kay
361faf650d Merge pull request #146 from ros2/fix_timer
Change behavior of timer on construction
2015-11-06 09:29:06 -08:00
Jackie Kay
dcd5117f89 Change timer beginning behavior 2015-11-06 09:28:10 -08:00
William Woodall
ca64af3145 Merge pull request #140 from ros2/rclcpp_library
Make rclcpp in to a library
2015-11-05 21:01:13 -08:00
William Woodall
36cd6e3cdd [style] uncrustify 2015-11-05 19:01:14 -08:00
William Woodall
3dd193efbb fixes for Windows 2015-11-05 18:55:53 -08:00
William Woodall
ef89f5a5ab remove unnecessary header 2015-11-05 18:55:52 -08:00
William Woodall
51695d1b24 [style] cpplint/uncrustify 2015-11-05 18:55:52 -08:00
William Woodall
0bbe9b2099 update code after splitting into headers and cpp files 2015-11-05 18:55:52 -08:00
William Woodall
f531b02928 split headers into cpp files 2015-11-05 18:55:51 -08:00
William Woodall
f0faa1fefe prepare splitting headers into cpp files 2015-11-05 18:55:47 -08:00
134 changed files with 14091 additions and 3208 deletions

1
rclcpp/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
doc_output

View File

@@ -1,34 +1,254 @@
cmake_minimum_required(VERSION 2.8.3)
cmake_minimum_required(VERSION 3.5)
project(rclcpp)
find_package(ament_cmake REQUIRED)
find_package(builtin_interfaces REQUIRED)
find_package(rcl REQUIRED)
find_package(rcl_interfaces REQUIRED)
find_package(rmw REQUIRED)
find_package(rmw_implementation REQUIRED)
find_package(rosidl_generator_cpp REQUIRED)
find_package(rosidl_typesupport_c REQUIRED)
find_package(rosidl_typesupport_cpp REQUIRED)
ament_export_dependencies(rmw)
ament_export_dependencies(rcl_interfaces)
# Default to C++14
if(NOT CMAKE_CXX_STANDARD)
set(CMAKE_CXX_STANDARD 14)
endif()
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
# we dont use add_compile_options with pedantic in message packages
# because the Python C extensions dont comply with it
# TODO(mikaelarguedas) change to add_compile_options
# once this is not a message package anymore
# https://github.com/ros2/system_tests/issues/191
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
endif()
include_directories(include)
set(${PROJECT_NAME}_SRCS
src/rclcpp/any_executable.cpp
src/rclcpp/callback_group.cpp
src/rclcpp/client.cpp
src/rclcpp/context.cpp
src/rclcpp/contexts/default_context.cpp
src/rclcpp/event.cpp
src/rclcpp/exceptions.cpp
src/rclcpp/executor.cpp
src/rclcpp/executors.cpp
src/rclcpp/expand_topic_or_service_name.cpp
src/rclcpp/executors/multi_threaded_executor.cpp
src/rclcpp/executors/single_threaded_executor.cpp
src/rclcpp/graph_listener.cpp
src/rclcpp/intra_process_manager.cpp
src/rclcpp/intra_process_manager_impl.cpp
src/rclcpp/memory_strategies.cpp
src/rclcpp/memory_strategy.cpp
src/rclcpp/node.cpp
src/rclcpp/node_interfaces/node_base.cpp
src/rclcpp/node_interfaces/node_graph.cpp
src/rclcpp/node_interfaces/node_parameters.cpp
src/rclcpp/node_interfaces/node_services.cpp
src/rclcpp/node_interfaces/node_timers.cpp
src/rclcpp/node_interfaces/node_topics.cpp
src/rclcpp/parameter.cpp
src/rclcpp/parameter_client.cpp
src/rclcpp/parameter_service.cpp
src/rclcpp/publisher.cpp
src/rclcpp/service.cpp
src/rclcpp/subscription.cpp
src/rclcpp/timer.cpp
src/rclcpp/type_support.cpp
src/rclcpp/utilities.cpp
)
add_library(${PROJECT_NAME} SHARED
${${PROJECT_NAME}_SRCS})
ament_target_dependencies(${PROJECT_NAME}
"builtin_interfaces"
"rcl"
"rosidl_generator_cpp"
"rosidl_typesupport_cpp")
# Causes the visibility macros to use dllexport rather than dllimport,
# which is appropriate when building the dll but not consuming it.
target_compile_definitions(${PROJECT_NAME}
PRIVATE "RCLCPP_BUILDING_LIBRARY")
install(
TARGETS ${PROJECT_NAME}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
ament_export_dependencies(ament_cmake)
ament_export_dependencies(builtin_interfaces)
ament_export_dependencies(rcl)
ament_export_dependencies(rosidl_generator_cpp)
ament_export_dependencies(rosidl_typesupport_c)
ament_export_dependencies(rosidl_typesupport_cpp)
ament_export_include_directories(include)
if(AMENT_ENABLE_TESTING)
ament_export_libraries(${PROJECT_NAME})
if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
find_package(rmw REQUIRED)
find_package(rmw_implementation_cmake REQUIRED)
if(NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra")
ament_add_gtest(test_client test/test_client.cpp)
if(TARGET test_client)
target_include_directories(test_client PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_client ${PROJECT_NAME})
endif()
ament_add_gtest(test_expand_topic_or_service_name test/test_expand_topic_or_service_name.cpp)
if(TARGET test_expand_topic_or_service_name)
target_include_directories(test_expand_topic_or_service_name PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_expand_topic_or_service_name ${PROJECT_NAME})
endif()
include_directories(include)
ament_add_gtest(test_function_traits test/test_function_traits.cpp)
if(TARGET test_function_traits)
target_include_directories(test_function_traits PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
endif()
ament_add_gtest(test_mapped_ring_buffer test/test_mapped_ring_buffer.cpp)
if(TARGET test_mapped_ring_buffer)
target_include_directories(test_mapped_ring_buffer PUBLIC
${rcl_INCLUDE_DIRS}
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
endif()
ament_add_gtest(test_intra_process_manager test/test_intra_process_manager.cpp)
if(TARGET test_intra_process_manager)
target_include_directories(test_intra_process_manager PUBLIC
"${rcl_interfaces_INCLUDE_DIRS}"
"${rmw_INCLUDE_DIRS}")
${rcl_INCLUDE_DIRS}
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
endif()
ament_add_gtest(test_node test/test_node.cpp)
if(TARGET test_node)
target_include_directories(test_node PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_node ${PROJECT_NAME})
endif()
ament_add_gtest(test_publisher test/test_publisher.cpp)
if(TARGET test_publisher)
target_include_directories(test_publisher PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_publisher ${PROJECT_NAME})
endif()
ament_add_gtest(test_rate test/test_rate.cpp
ENV RCL_ASSERT_RMW_ID_MATCHES=${rmw_implementation})
if(TARGET test_rate)
target_include_directories(test_rate PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_rate
${PROJECT_NAME}
)
endif()
ament_add_gtest(test_service test/test_service.cpp)
if(TARGET test_service)
target_include_directories(test_service PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_service ${PROJECT_NAME})
endif()
ament_add_gtest(test_subscription test/test_subscription.cpp)
if(TARGET test_subscription)
target_include_directories(test_subscription PUBLIC
${rcl_interfaces_INCLUDE_DIRS}
${rmw_INCLUDE_DIRS}
${rosidl_generator_cpp_INCLUDE_DIRS}
${rosidl_typesupport_cpp_INCLUDE_DIRS}
)
target_link_libraries(test_subscription ${PROJECT_NAME})
endif()
ament_add_gtest(test_find_weak_nodes test/test_find_weak_nodes.cpp)
if(TARGET test_find_weak_nodes)
target_include_directories(test_find_weak_nodes PUBLIC
${rcl_INCLUDE_DIRS}
)
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
endif()
get_default_rmw_implementation(default_rmw)
find_package(${default_rmw} REQUIRED)
get_rmw_typesupport(typesupport_impls_cpp "${default_rmw}" LANGUAGE "cpp")
get_rmw_typesupport(typesupport_impls_c "${default_rmw}" LANGUAGE "c")
set(mock_msg_files
"test/mock_msgs/srv/Mock.srv")
rosidl_generate_interfaces(mock_msgs
${mock_msg_files}
LIBRARY_NAME "rclcpp"
SKIP_INSTALL)
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
if(WIN32)
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
endif()
ament_add_gtest(test_externally_defined_services test/test_externally_defined_services.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_externally_defined_services)
target_include_directories(test_externally_defined_services PUBLIC
${rcl_INCLUDE_DIRS}
)
target_link_libraries(test_externally_defined_services ${PROJECT_NAME})
foreach(typesupport_impl_cpp ${typesupport_impls_cpp})
rosidl_target_interfaces(test_externally_defined_services
mock_msgs ${typesupport_impl_cpp})
endforeach()
foreach(typesupport_impl_c ${typesupport_impls_c})
rosidl_target_interfaces(test_externally_defined_services
mock_msgs ${typesupport_impl_c})
endforeach()
endif()
ament_add_gtest(test_time test/test_time.cpp
APPEND_LIBRARY_DIRS "${append_library_dirs}")
if(TARGET test_time)
ament_target_dependencies(test_time
"rcl")
target_link_libraries(test_time ${PROJECT_NAME})
endif()
endif()
@@ -37,11 +257,11 @@ ament_package(
)
install(
DIRECTORY include/
DESTINATION include
DIRECTORY cmake
DESTINATION share/${PROJECT_NAME}
)
install(
DIRECTORY src/
DESTINATION src/rclcpp
DIRECTORY include/
DESTINATION include
)

28
rclcpp/Doxyfile Normal file
View File

@@ -0,0 +1,28 @@
# All settings not listed here will use the Doxygen default values.
PROJECT_NAME = "rclcpp"
PROJECT_NUMBER = master
PROJECT_BRIEF = "C++ ROS Client Library API"
INPUT = ./include
RECURSIVE = YES
OUTPUT_DIRECTORY = doc_output
EXTRACT_ALL = YES
SORT_MEMBER_DOCS = NO
GENERATE_LATEX = NO
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = YES
EXPAND_ONLY_PREDEF = YES
PREDEFINED = RCLCPP_PUBLIC=
# 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/"
# 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/"
# Uncomment to generate tag files for cross-project linking.
#GENERATE_TAGFILE = "../../../../doxygen_tag_files/rclcpp.tag"

View File

@@ -0,0 +1,26 @@
# 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.
set(rclcpp_node_main_SRC "${rclcpp_DIR}/../../../src/rclcpp/node_main.cpp")
function(rclcpp_create_node_main node_library_target)
if(NOT TARGET ${node_library_target})
message(FATAL_ERROR "rclcpp_create_node_main() the first argument must be a valid target name")
endif()
set(executable_name_ ${node_library_target}_node)
add_executable(${executable_name_} ${rclcpp_node_main_SRC})
target_link_libraries(${executable_name_} ${node_library_target})
install(TARGETS ${executable_name_} DESTINATION bin)
endfunction()

View File

@@ -0,0 +1,17 @@
# Copyright 2016 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# register node plugins
ament_index_register_resource(
"node_plugin" CONTENT "${_RCLCPP__NODE_PLUGINS}")

View File

@@ -0,0 +1,61 @@
# Copyright 2016 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Register a node plugin with the ament resource index.
#
# The passed library can contain multiple plugins extending the node interface.
#
# :param target: the shared library target
# :type target: string
# :param ARGN: the unique plugin names being exported using class_loader
# :type ARGN: list of strings
#
macro(rclcpp_register_node_plugins target)
if(NOT TARGET ${target})
message(
FATAL_ERROR
"rclcpp_register_node_plugins() first argument "
"'${target}' is not a target")
endif()
get_target_property(_target_type ${target} TYPE)
if(NOT _target_type STREQUAL "SHARED_LIBRARY")
message(
FATAL_ERROR
"rclcpp_register_node_plugins() first argument "
"'${target}' is not a shared library target")
endif()
if(${ARGC} GREATER 0)
_rclcpp_register_package_hook()
set(_unique_names)
foreach(_arg ${ARGN})
if(_arg IN_LIST _unique_names)
message(
FATAL_ERROR
"rclcpp_register_node_plugins() the plugin names "
"must be unique (multiple '${_arg}')")
endif()
list(APPEND _unique_names "${_arg}")
if(WIN32)
set(_path "bin")
else()
set(_path "lib")
endif()
set(_RCLCPP__NODE_PLUGINS
"${_RCLCPP__NODE_PLUGINS}${_arg};${_path}/$<TARGET_FILE_NAME:${target}>\n")
endforeach()
endif()
endmacro()

View File

@@ -17,6 +17,8 @@
#include <memory>
#include "rcl/allocator.h"
#include "rclcpp/allocator/allocator_deleter.hpp"
namespace rclcpp
@@ -27,6 +29,66 @@ namespace allocator
template<typename T, typename Alloc>
using AllocRebind = typename std::allocator_traits<Alloc>::template rebind_traits<T>;
template<typename Alloc>
void * retyped_allocate(size_t size, void * untyped_allocator)
{
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
if (!typed_allocator) {
throw std::runtime_error("Received incorrect allocator type");
}
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
}
template<typename T, typename Alloc>
void retyped_deallocate(void * untyped_pointer, void * untyped_allocator)
{
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
if (!typed_allocator) {
throw std::runtime_error("Received incorrect allocator type");
}
auto typed_ptr = static_cast<T *>(untyped_pointer);
std::allocator_traits<Alloc>::deallocate(*typed_allocator, typed_ptr, 1);
}
template<typename T, typename Alloc>
void * retyped_reallocate(void * untyped_pointer, size_t size, void * untyped_allocator)
{
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
if (!typed_allocator) {
throw std::runtime_error("Received incorrect allocator type");
}
auto typed_ptr = static_cast<T *>(untyped_pointer);
std::allocator_traits<Alloc>::deallocate(*typed_allocator, typed_ptr, 1);
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
}
// Convert a std::allocator_traits-formatted Allocator into an rcl allocator
template<typename T, typename Alloc,
typename std::enable_if<!std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
{
rcl_allocator_t rcl_allocator = rcl_get_default_allocator();
#ifndef _WIN32
rcl_allocator.allocate = &retyped_allocate<Alloc>;
rcl_allocator.deallocate = &retyped_deallocate<T, Alloc>;
rcl_allocator.reallocate = &retyped_reallocate<T, Alloc>;
rcl_allocator.state = &allocator;
#else
(void)allocator; // Remove warning
#endif
return rcl_allocator;
}
// TODO(jacquelinekay) Workaround for an incomplete implementation of std::allocator<void>
template<typename T, typename Alloc,
typename std::enable_if<std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
{
(void)allocator;
return rcl_get_default_allocator();
}
} // namespace allocator
} // namespace rclcpp

View File

@@ -16,10 +16,10 @@
#define RCLCPP__ALLOCATOR__ALLOCATOR_DELETER_HPP_
#include <memory>
#include <stdexcept>
namespace rclcpp
{
namespace allocator
{

View File

@@ -12,13 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_ANY_EXECUTABLE_HPP_
#define RCLCPP_RCLCPP_ANY_EXECUTABLE_HPP_
#ifndef RCLCPP__ANY_EXECUTABLE_HPP_
#define RCLCPP__ANY_EXECUTABLE_HPP_
#include <memory>
#include <rclcpp/macros.hpp>
#include <rclcpp/node.hpp>
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/timer.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
@@ -27,10 +33,14 @@ namespace executor
struct AnyExecutable
{
RCLCPP_SMART_PTR_DEFINITIONS(AnyExecutable);
AnyExecutable()
: subscription(0), timer(0), callback_group(0), node(0)
{}
RCLCPP_SMART_PTR_DEFINITIONS(AnyExecutable)
RCLCPP_PUBLIC
AnyExecutable();
RCLCPP_PUBLIC
virtual ~AnyExecutable();
// Only one of the following pointers will be set.
rclcpp::subscription::SubscriptionBase::SharedPtr subscription;
rclcpp::subscription::SubscriptionBase::SharedPtr subscription_intra_process;
@@ -39,10 +49,10 @@ struct AnyExecutable
rclcpp::client::ClientBase::SharedPtr client;
// These are used to keep the scope on the containing items
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group;
rclcpp::node::Node::SharedPtr node;
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base;
};
} /* executor */
} /* rclcpp */
} // namespace executor
} // namespace rclcpp
#endif
#endif // RCLCPP__ANY_EXECUTABLE_HPP_

View File

@@ -12,16 +12,17 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_ANY_SERVICE_CALLBACK_HPP_
#define RCLCPP_RCLCPP_ANY_SERVICE_CALLBACK_HPP_
#include <rclcpp/function_traits.hpp>
#ifndef RCLCPP__ANY_SERVICE_CALLBACK_HPP_
#define RCLCPP__ANY_SERVICE_CALLBACK_HPP_
#include <functional>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <rmw/types.h>
#include "rclcpp/function_traits.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/types.h"
namespace rclcpp
{
@@ -56,7 +57,7 @@ public:
template<
typename CallbackT,
typename std::enable_if<
rclcpp::same_arguments<
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrCallback
>::value
@@ -70,7 +71,7 @@ public:
template<
typename CallbackT,
typename std::enable_if<
rclcpp::same_arguments<
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrWithRequestHeaderCallback
>::value
@@ -97,7 +98,7 @@ public:
}
};
} /* namespace any_service_callback */
} /* namespace rclcpp */
} // namespace any_service_callback
} // namespace rclcpp
#endif /* RCLCPP_RCLCPP_ANY_SERVICE_CALLBACK_HPP_ */
#endif // RCLCPP__ANY_SERVICE_CALLBACK_HPP_

View File

@@ -19,10 +19,13 @@
#include <functional>
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
@@ -70,7 +73,7 @@ public:
template<
typename CallbackT,
typename std::enable_if<
rclcpp::same_arguments<
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrCallback
>::value
@@ -84,7 +87,7 @@ public:
template<
typename CallbackT,
typename std::enable_if<
rclcpp::same_arguments<
rclcpp::function_traits::same_arguments<
CallbackT,
SharedPtrWithInfoCallback
>::value
@@ -98,7 +101,7 @@ public:
template<
typename CallbackT,
typename std::enable_if<
rclcpp::same_arguments<
rclcpp::function_traits::same_arguments<
CallbackT,
ConstSharedPtrCallback
>::value
@@ -112,7 +115,7 @@ public:
template<
typename CallbackT,
typename std::enable_if<
rclcpp::same_arguments<
rclcpp::function_traits::same_arguments<
CallbackT,
ConstSharedPtrWithInfoCallback
>::value
@@ -126,7 +129,7 @@ public:
template<
typename CallbackT,
typename std::enable_if<
rclcpp::same_arguments<
rclcpp::function_traits::same_arguments<
CallbackT,
UniquePtrCallback
>::value
@@ -140,7 +143,7 @@ public:
template<
typename CallbackT,
typename std::enable_if<
rclcpp::same_arguments<
rclcpp::function_traits::same_arguments<
CallbackT,
UniquePtrWithInfoCallback
>::value
@@ -181,16 +184,16 @@ public:
{
(void)message_info;
if (shared_ptr_callback_) {
typename MessageT::SharedPtr shared_message = std::move(message);
typename std::shared_ptr<MessageT> shared_message = std::move(message);
shared_ptr_callback_(shared_message);
} else if (shared_ptr_with_info_callback_) {
typename MessageT::SharedPtr shared_message = std::move(message);
typename std::shared_ptr<MessageT> shared_message = std::move(message);
shared_ptr_with_info_callback_(shared_message, message_info);
} else if (const_shared_ptr_callback_) {
typename MessageT::ConstSharedPtr const_shared_message = std::move(message);
typename std::shared_ptr<MessageT const> const_shared_message = std::move(message);
const_shared_ptr_callback_(const_shared_message);
} else if (const_shared_ptr_with_info_callback_) {
typename MessageT::ConstSharedPtr const_shared_message = std::move(message);
typename std::shared_ptr<MessageT const> const_shared_message = std::move(message);
const_shared_ptr_with_info_callback_(const_shared_message, message_info);
} else if (unique_ptr_callback_) {
unique_ptr_callback_(std::move(message));

View File

@@ -12,26 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_CALLBACK_GROUP_HPP_
#define RCLCPP_RCLCPP_CALLBACK_GROUP_HPP_
#ifndef RCLCPP__CALLBACK_GROUP_HPP_
#define RCLCPP__CALLBACK_GROUP_HPP_
#include <atomic>
#include <mutex>
#include <string>
#include <vector>
#include <rclcpp/subscription.hpp>
#include <rclcpp/timer.hpp>
#include <rclcpp/service.hpp>
#include <rclcpp/client.hpp>
#include "rclcpp/client.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/timer.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
// Forward declarations for friend statement in class CallbackGroup
namespace node
namespace node_interfaces
{
class Node;
} // namespace node
class NodeServices;
class NodeTimers;
class NodeTopics;
} // namespace node_interfaces
namespace callback_group
{
@@ -44,87 +48,70 @@ enum class CallbackGroupType
class CallbackGroup
{
friend class rclcpp::node::Node;
friend class rclcpp::node_interfaces::NodeServices;
friend class rclcpp::node_interfaces::NodeTimers;
friend class rclcpp::node_interfaces::NodeTopics;
public:
RCLCPP_SMART_PTR_DEFINITIONS(CallbackGroup);
RCLCPP_SMART_PTR_DEFINITIONS(CallbackGroup)
CallbackGroup(CallbackGroupType group_type)
: type_(group_type), can_be_taken_from_(true)
{}
RCLCPP_PUBLIC
explicit CallbackGroup(CallbackGroupType group_type);
const std::vector<subscription::SubscriptionBase::WeakPtr> &
get_subscription_ptrs() const
{
return subscription_ptrs_;
}
RCLCPP_PUBLIC
const std::vector<rclcpp::subscription::SubscriptionBase::WeakPtr> &
get_subscription_ptrs() const;
const std::vector<timer::TimerBase::WeakPtr> &
get_timer_ptrs() const
{
return timer_ptrs_;
}
RCLCPP_PUBLIC
const std::vector<rclcpp::timer::TimerBase::WeakPtr> &
get_timer_ptrs() const;
const std::vector<service::ServiceBase::SharedPtr> &
get_service_ptrs() const
{
return service_ptrs_;
}
RCLCPP_PUBLIC
const std::vector<rclcpp::service::ServiceBase::WeakPtr> &
get_service_ptrs() const;
const std::vector<client::ClientBase::SharedPtr> &
get_client_ptrs() const
{
return client_ptrs_;
}
RCLCPP_PUBLIC
const std::vector<rclcpp::client::ClientBase::WeakPtr> &
get_client_ptrs() const;
std::atomic_bool & can_be_taken_from()
{
return can_be_taken_from_;
}
RCLCPP_PUBLIC
std::atomic_bool &
can_be_taken_from();
const CallbackGroupType & type() const
{
return type_;
}
RCLCPP_PUBLIC
const CallbackGroupType &
type() const;
private:
RCLCPP_DISABLE_COPY(CallbackGroup);
protected:
RCLCPP_DISABLE_COPY(CallbackGroup)
RCLCPP_PUBLIC
void
add_subscription(
const subscription::SubscriptionBase::SharedPtr subscription_ptr)
{
subscription_ptrs_.push_back(subscription_ptr);
}
add_subscription(const rclcpp::subscription::SubscriptionBase::SharedPtr subscription_ptr);
RCLCPP_PUBLIC
void
add_timer(const timer::TimerBase::SharedPtr timer_ptr)
{
timer_ptrs_.push_back(timer_ptr);
}
add_timer(const rclcpp::timer::TimerBase::SharedPtr timer_ptr);
RCLCPP_PUBLIC
void
add_service(const service::ServiceBase::SharedPtr service_ptr)
{
service_ptrs_.push_back(service_ptr);
}
add_service(const rclcpp::service::ServiceBase::SharedPtr service_ptr);
RCLCPP_PUBLIC
void
add_client(const client::ClientBase::SharedPtr client_ptr)
{
client_ptrs_.push_back(client_ptr);
}
add_client(const rclcpp::client::ClientBase::SharedPtr client_ptr);
CallbackGroupType type_;
std::vector<subscription::SubscriptionBase::WeakPtr> subscription_ptrs_;
std::vector<timer::TimerBase::WeakPtr> timer_ptrs_;
std::vector<service::ServiceBase::SharedPtr> service_ptrs_;
std::vector<client::ClientBase::SharedPtr> client_ptrs_;
// Mutex to protect the subsequent vectors of pointers.
mutable std::mutex mutex_;
std::vector<rclcpp::subscription::SubscriptionBase::WeakPtr> subscription_ptrs_;
std::vector<rclcpp::timer::TimerBase::WeakPtr> timer_ptrs_;
std::vector<rclcpp::service::ServiceBase::WeakPtr> service_ptrs_;
std::vector<rclcpp::client::ClientBase::WeakPtr> client_ptrs_;
std::atomic_bool can_be_taken_from_;
};
} /* namespace callback_group */
} /* namespace rclcpp */
} // namespace callback_group
} // namespace rclcpp
#endif /* RCLCPP_RCLCPP_CALLBACK_GROUP_HPP_ */
#endif // RCLCPP__CALLBACK_GROUP_HPP_

View File

@@ -12,113 +12,190 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_CLIENT_HPP_
#define RCLCPP_RCLCPP_CLIENT_HPP_
#ifndef RCLCPP__CLIENT_HPP_
#define RCLCPP__CLIENT_HPP_
#include <future>
#include <iostream>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <tuple>
#include <utility>
#include <rmw/error_handling.h>
#include <rmw/rmw.h>
#include "rcl/client.h"
#include "rcl/error_handling.h"
#include "rcl/wait.h"
#include <rclcpp/macros.hpp>
#include <rclcpp/utilities.hpp>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/function_traits.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.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 "rmw/error_handling.h"
#include "rmw/rmw.h"
namespace rclcpp
{
namespace node_interfaces
{
class NodeBaseInterface;
} // namespace node_interfaces
namespace client
{
class ClientBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientBase)
RCLCPP_PUBLIC
ClientBase(
std::shared_ptr<rmw_node_t> node_handle,
rmw_client_t * client_handle,
const std::string & service_name)
: node_handle_(node_handle), client_handle_(client_handle), service_name_(service_name)
{}
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name);
virtual ~ClientBase()
{
if (client_handle_) {
if (rmw_destroy_client(client_handle_) != RMW_RET_OK) {
fprintf(stderr,
"Error in destruction of rmw client handle: %s\n", rmw_get_error_string_safe());
}
}
}
RCLCPP_PUBLIC
virtual ~ClientBase();
const std::string & get_service_name() const
{
return this->service_name_;
}
RCLCPP_PUBLIC
const std::string &
get_service_name() const;
const rmw_client_t * get_client_handle() const
RCLCPP_PUBLIC
const rcl_client_t *
get_client_handle() const;
RCLCPP_PUBLIC
bool
service_is_ready() const;
template<typename RatioT = std::milli>
bool
wait_for_service(
std::chrono::duration<int64_t, RatioT> timeout = std::chrono::duration<int64_t, RatioT>(-1))
{
return this->client_handle_;
return wait_for_service_nanoseconds(
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
);
}
virtual std::shared_ptr<void> create_response() = 0;
virtual std::shared_ptr<void> create_request_header() = 0;
virtual std::shared_ptr<rmw_request_id_t> create_request_header() = 0;
virtual void handle_response(
std::shared_ptr<void> & request_header, std::shared_ptr<void> & response) = 0;
std::shared_ptr<rmw_request_id_t> request_header, std::shared_ptr<void> response) = 0;
private:
RCLCPP_DISABLE_COPY(ClientBase);
protected:
RCLCPP_DISABLE_COPY(ClientBase)
std::shared_ptr<rmw_node_t> node_handle_;
RCLCPP_PUBLIC
bool
wait_for_service_nanoseconds(std::chrono::nanoseconds timeout);
rmw_client_t * client_handle_;
RCLCPP_PUBLIC
rcl_node_t *
get_rcl_node_handle() const;
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
std::shared_ptr<rcl_node_t> node_handle_;
rcl_client_t client_handle_ = rcl_get_zero_initialized_client();
std::string service_name_;
};
template<typename ServiceT>
class Client : public ClientBase
{
public:
using Promise = std::promise<typename ServiceT::Response::SharedPtr>;
using SharedRequest = typename ServiceT::Request::SharedPtr;
using SharedResponse = typename ServiceT::Response::SharedPtr;
using Promise = std::promise<SharedResponse>;
using PromiseWithRequest = std::promise<std::pair<SharedRequest, SharedResponse>>;
using SharedPromise = std::shared_ptr<Promise>;
using SharedFuture = std::shared_future<typename ServiceT::Response::SharedPtr>;
using SharedPromiseWithRequest = std::shared_ptr<PromiseWithRequest>;
using SharedFuture = std::shared_future<SharedResponse>;
using SharedFutureWithRequest = std::shared_future<std::pair<SharedRequest, SharedResponse>>;
using CallbackType = std::function<void(SharedFuture)>;
using CallbackWithRequestType = std::function<void(SharedFutureWithRequest)>;
RCLCPP_SMART_PTR_DEFINITIONS(Client);
RCLCPP_SMART_PTR_DEFINITIONS(Client)
Client(
std::shared_ptr<rmw_node_t> node_handle,
rmw_client_t * client_handle,
const std::string & service_name)
: ClientBase(node_handle, client_handle, service_name)
{}
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name,
rcl_client_options_t & client_options)
: ClientBase(node_base, node_graph, service_name)
{
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(
&client_handle_,
this->get_rcl_node_handle(),
service_type_support_handle,
service_name.c_str(),
&client_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_SERVICE_NAME_INVALID) {
auto rcl_node_handle = this->get_rcl_node_handle();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
service_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
true);
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create client");
}
}
std::shared_ptr<void> create_response()
virtual ~Client()
{
if (rcl_client_fini(&client_handle_, this->get_rcl_node_handle()) != RCL_RET_OK) {
fprintf(stderr,
"Error in destruction of rcl client handle: %s\n", rcl_get_error_string_safe());
rcl_reset_error();
}
}
std::shared_ptr<void>
create_response()
{
return std::shared_ptr<void>(new typename ServiceT::Response());
}
std::shared_ptr<void> create_request_header()
std::shared_ptr<rmw_request_id_t>
create_request_header()
{
// TODO(wjwwood): This should probably use rmw_request_id's allocator.
// (since it is a C type)
return std::shared_ptr<void>(new rmw_request_id_t);
return std::shared_ptr<rmw_request_id_t>(new rmw_request_id_t);
}
void handle_response(std::shared_ptr<void> & request_header, std::shared_ptr<void> & response)
void
handle_response(std::shared_ptr<rmw_request_id_t> request_header,
std::shared_ptr<void> response)
{
auto typed_request_header = std::static_pointer_cast<rmw_request_id_t>(request_header);
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
auto typed_response = std::static_pointer_cast<typename ServiceT::Response>(response);
int64_t sequence_number = typed_request_header->sequence_number;
// TODO this must check if the sequence_number is valid otherwise the call_promise will be null
int64_t sequence_number = request_header->sequence_number;
// TODO(esteve) this should throw instead since it is not expected to happen in the first place
if (this->pending_requests_.count(sequence_number) == 0) {
fprintf(stderr, "Received invalid sequence number. Ignoring...\n");
return;
}
auto tuple = this->pending_requests_[sequence_number];
auto call_promise = std::get<0>(tuple);
auto callback = std::get<1>(tuple);
@@ -128,37 +205,72 @@ public:
callback(future);
}
SharedFuture async_send_request(
typename ServiceT::Request::SharedPtr request)
SharedFuture
async_send_request(SharedRequest request)
{
return async_send_request(request, [](SharedFuture) {});
}
SharedFuture async_send_request(
typename ServiceT::Request::SharedPtr request,
CallbackType cb)
template<
typename CallbackT,
typename std::enable_if<
rclcpp::function_traits::same_arguments<
CallbackT,
CallbackType
>::value
>::type * = nullptr
>
SharedFuture
async_send_request(SharedRequest request, CallbackT && cb)
{
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
int64_t sequence_number;
if (RMW_RET_OK != rmw_send_request(get_client_handle(), request.get(), &sequence_number)) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
std::string("failed to send request: ") + rmw_get_error_string_safe());
// *INDENT-ON*
rcl_ret_t ret = rcl_send_request(get_client_handle(), request.get(), &sequence_number);
if (RCL_RET_OK != ret) {
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
}
SharedPromise call_promise = std::make_shared<Promise>();
SharedFuture f(call_promise->get_future());
pending_requests_[sequence_number] = std::make_tuple(call_promise, cb, f);
pending_requests_[sequence_number] =
std::make_tuple(call_promise, std::forward<CallbackType>(cb), f);
return f;
}
template<
typename CallbackT,
typename std::enable_if<
rclcpp::function_traits::same_arguments<
CallbackT,
CallbackWithRequestType
>::value
>::type * = nullptr
>
SharedFutureWithRequest
async_send_request(SharedRequest request, CallbackT && cb)
{
SharedPromiseWithRequest promise = std::make_shared<PromiseWithRequest>();
SharedFutureWithRequest future_with_request(promise->get_future());
auto wrapping_cb = [future_with_request, promise, request, &cb](SharedFuture future) {
auto response = future.get();
promise->set_value(std::make_pair(request, response));
cb(future_with_request);
};
async_send_request(request, wrapping_cb);
return future_with_request;
}
private:
RCLCPP_DISABLE_COPY(Client);
RCLCPP_DISABLE_COPY(Client)
std::map<int64_t, std::tuple<SharedPromise, CallbackType, SharedFuture>> pending_requests_;
std::mutex pending_requests_mutex_;
};
} /* namespace client */
} /* namespace rclcpp */
} // namespace client
} // namespace rclcpp
#endif /* RCLCPP_RCLCPP_CLIENT_HPP_ */
#endif // RCLCPP__CLIENT_HPP_

View File

@@ -12,20 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_CONTEXT_HPP_
#define RCLCPP_RCLCPP_CONTEXT_HPP_
#include <rclcpp/macros.hpp>
#ifndef RCLCPP__CONTEXT_HPP_
#define RCLCPP__CONTEXT_HPP_
#include <iostream>
#include <memory>
#include <mutex>
#include <typeinfo>
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <utility>
#include <rmw/rmw.h>
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/rmw.h"
namespace rclcpp
{
@@ -36,9 +36,10 @@ namespace context
class Context
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(Context);
RCLCPP_SMART_PTR_DEFINITIONS(Context)
Context() {}
RCLCPP_PUBLIC
Context();
template<typename SubContext, typename ... Args>
std::shared_ptr<SubContext>
@@ -51,7 +52,7 @@ public:
auto it = sub_contexts_.find(type_i);
if (it == sub_contexts_.end()) {
// It doesn't exist yet, make it
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
sub_context = std::shared_ptr<SubContext>(
new SubContext(std::forward<Args>(args) ...),
[] (SubContext * sub_context_ptr) {
@@ -67,14 +68,13 @@ public:
}
private:
RCLCPP_DISABLE_COPY(Context);
RCLCPP_DISABLE_COPY(Context)
std::unordered_map<std::type_index, std::shared_ptr<void>> sub_contexts_;
std::mutex mutex_;
};
} /* namespace context */
} /* namespace rclcpp */
} // namespace context
} // namespace rclcpp
#endif /* RCLCPP_RCLCPP_CONTEXT_HPP_ */
#endif // RCLCPP__CONTEXT_HPP_

View File

@@ -12,10 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_CONTEXTS_DEFAULT_CONTEXT_HPP_
#define RCLCPP_RCLCPP_CONTEXTS_DEFAULT_CONTEXT_HPP_
#ifndef RCLCPP__CONTEXTS__DEFAULT_CONTEXT_HPP_
#define RCLCPP__CONTEXTS__DEFAULT_CONTEXT_HPP_
#include <rclcpp/context.hpp>
#include "rclcpp/context.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
@@ -27,21 +28,18 @@ namespace default_context
class DefaultContext : public rclcpp::context::Context
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(DefaultContext);
DefaultContext() {}
RCLCPP_SMART_PTR_DEFINITIONS(DefaultContext)
RCLCPP_PUBLIC
DefaultContext();
};
RCLCPP_PUBLIC
DefaultContext::SharedPtr
get_global_default_context()
{
static DefaultContext::SharedPtr default_context = DefaultContext::make_shared();
return default_context;
}
get_global_default_context();
} // namespace default_context
} // namespace contexts
} // namespace rclcpp
} // namespace default_context
} // namespace contexts
} // namespace rclcpp
#endif /* RCLCPP_RCLCPP_CONTEXTS_DEFAULT_CONTEXT_HPP_ */
#endif // RCLCPP__CONTEXTS__DEFAULT_CONTEXT_HPP_

View File

@@ -0,0 +1,51 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__CREATE_PUBLISHER_HPP_
#define RCLCPP__CREATE_PUBLISHER_HPP_
#include <memory>
#include <string>
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/publisher_factory.hpp"
#include "rmw/qos_profiles.h"
namespace rclcpp
{
template<typename MessageT, typename AllocatorT, typename PublisherT>
std::shared_ptr<PublisherT>
create_publisher(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
const std::string & topic_name,
const rmw_qos_profile_t & qos_profile,
bool use_intra_process_comms,
std::shared_ptr<AllocatorT> allocator)
{
auto publisher_options = rcl_publisher_get_default_options();
publisher_options.qos = qos_profile;
auto pub = node_topics->create_publisher(
topic_name,
rclcpp::create_publisher_factory<MessageT, AllocatorT, PublisherT>(allocator),
publisher_options,
use_intra_process_comms);
node_topics->add_publisher(pub);
return std::dynamic_pointer_cast<PublisherT>(pub);
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_PUBLISHER_HPP_

View File

@@ -0,0 +1,62 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__CREATE_SUBSCRIPTION_HPP_
#define RCLCPP__CREATE_SUBSCRIPTION_HPP_
#include <memory>
#include <string>
#include <utility>
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/subscription_factory.hpp"
#include "rmw/qos_profiles.h"
namespace rclcpp
{
template<typename MessageT, typename CallbackT, typename AllocatorT, typename SubscriptionT>
typename rclcpp::subscription::Subscription<MessageT, AllocatorT>::SharedPtr
create_subscription(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
const std::string & topic_name,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
bool use_intra_process_comms,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, AllocatorT>::SharedPtr
msg_mem_strat,
typename std::shared_ptr<AllocatorT> allocator)
{
auto subscription_options = rcl_subscription_get_default_options();
subscription_options.qos = qos_profile;
subscription_options.ignore_local_publications = ignore_local_publications;
auto factory =
rclcpp::create_subscription_factory<MessageT, CallbackT, AllocatorT, SubscriptionT>(
std::forward<CallbackT>(callback), msg_mem_strat, allocator);
auto sub = node_topics->create_subscription(
topic_name,
factory,
subscription_options,
use_intra_process_comms);
node_topics->add_subscription(sub, group);
return std::dynamic_pointer_cast<SubscriptionT>(sub);
}
} // namespace rclcpp
#endif // RCLCPP__CREATE_SUBSCRIPTION_HPP_

View File

@@ -0,0 +1,58 @@
// Copyright 2016 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_HPP_
#define RCLCPP__EVENT_HPP_
#include <atomic>
#include <memory>
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace event
{
class Event
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Event)
RCLCPP_PUBLIC
Event();
RCLCPP_PUBLIC
bool
set();
RCLCPP_PUBLIC
bool
check();
RCLCPP_PUBLIC
bool
check_and_clear();
private:
RCLCPP_DISABLE_COPY(Event)
std::atomic_bool state_;
};
} // namespace event
} // namespace rclcpp
#endif // RCLCPP__EVENT_HPP_

View File

@@ -0,0 +1,187 @@
// Copyright 2016 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__EXCEPTIONS_HPP_
#define RCLCPP__EXCEPTIONS_HPP_
#include <stdexcept>
#include <string>
#include "rcl/error_handling.h"
#include "rcl/types.h"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace exceptions
{
/// Thrown when a method is trying to use a node, but it is invalid.
class InvalidNodeError : public std::runtime_error
{
public:
InvalidNodeError()
: std::runtime_error("node is invalid") {}
};
/// Thrown when a any kind of name (node, namespace, topic, etc.) is invalid.
class NameValidationError : public std::invalid_argument
{
public:
NameValidationError(
const char * name_type_,
const char * name_,
const char * error_msg_,
size_t invalid_index_)
: std::invalid_argument(format_error(name_type_, name_, error_msg_, invalid_index_)),
name_type(name_type_), name(name_), error_msg(error_msg_), invalid_index(invalid_index_)
{}
static std::string
format_error(
const char * name_type,
const char * name,
const char * error_msg,
size_t invalid_index);
const std::string name_type;
const std::string name;
const std::string error_msg;
const size_t invalid_index;
};
/// Thrown when a node name is invalid.
class InvalidNodeNameError : public NameValidationError
{
public:
InvalidNodeNameError(const char * node_name, const char * error_msg, size_t invalid_index)
: NameValidationError("node name", node_name, error_msg, invalid_index)
{}
};
/// Thrown when a node namespace is invalid.
class InvalidNamespaceError : public NameValidationError
{
public:
InvalidNamespaceError(const char * namespace_, const char * error_msg, size_t invalid_index)
: NameValidationError("namespace", namespace_, error_msg, invalid_index)
{}
};
/// Thrown when a topic name is invalid.
class InvalidTopicNameError : public NameValidationError
{
public:
InvalidTopicNameError(const char * namespace_, const char * error_msg, size_t invalid_index)
: NameValidationError("topic name", namespace_, error_msg, invalid_index)
{}
};
/// Thrown when a service name is invalid.
class InvalidServiceNameError : public NameValidationError
{
public:
InvalidServiceNameError(const char * namespace_, const char * error_msg, size_t invalid_index)
: NameValidationError("service name", namespace_, error_msg, invalid_index)
{}
};
/// 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
* to reset the error.
*
* \param ret the return code for the current error state
* \param prefix string to prefix to the error if applicable (not all errors have custom messages)
* \param error_state error state to create exception from, if nullptr rcl_get_error_state is used
* \param reset_error function to be called before throwing which whill clear the error state
* \throws std::invalid_argument if ret is RCL_RET_OK
* \throws std::runtime_error if the rcl_get_error_state returns 0
* \throws RCLErrorBase some child class exception based on ret
*/
RCLCPP_PUBLIC
void
throw_from_rcl_error(
rcl_ret_t ret,
const std::string & prefix = "",
const rcl_error_state_t * error_state = nullptr,
void (* reset_error)() = rcl_reset_error);
class RCLErrorBase
{
public:
RCLCPP_PUBLIC
RCLErrorBase(rcl_ret_t ret, const rcl_error_state_t * error_state);
virtual ~RCLErrorBase() {}
rcl_ret_t ret;
std::string message;
std::string file;
size_t line;
std::string formatted_message;
};
/// Created when the return code does not match one of the other specialized exceptions.
class RCLError : public RCLErrorBase, public std::runtime_error
{
public:
RCLCPP_PUBLIC
RCLError(rcl_ret_t ret, const rcl_error_state_t * error_state, const std::string & prefix);
RCLCPP_PUBLIC
RCLError(const RCLErrorBase & base_exc, const std::string & prefix);
};
/// Created when the ret is RCL_RET_BAD_ALLOC.
class RCLBadAlloc : public RCLErrorBase, public std::bad_alloc
{
public:
RCLCPP_PUBLIC
RCLBadAlloc(rcl_ret_t ret, const rcl_error_state_t * error_state);
RCLCPP_PUBLIC
explicit RCLBadAlloc(const RCLErrorBase & base_exc);
};
/// Created when the ret is RCL_RET_INVALID_ARGUMENT.
class RCLInvalidArgument : public RCLErrorBase, public std::invalid_argument
{
public:
RCLCPP_PUBLIC
RCLInvalidArgument(
rcl_ret_t ret,
const rcl_error_state_t * error_state,
const std::string & prefix);
RCLCPP_PUBLIC
RCLInvalidArgument(const RCLErrorBase & base_exc, const std::string & prefix);
};
/// Thrown when an invalid rclcpp::Event object or SharedPtr is encountered.
class InvalidEventError : public std::runtime_error
{
public:
InvalidEventError()
: std::runtime_error("event is invalid") {}
};
/// Thrown when an unregistered rclcpp::Event is encountered where a registered one was expected.
class EventNotRegisteredError : public std::runtime_error
{
public:
EventNotRegisteredError()
: std::runtime_error("event already registered") {}
};
} // namespace exceptions
} // namespace rclcpp
#endif // RCLCPP__EXCEPTIONS_HPP_

View File

@@ -17,26 +17,69 @@
#include <algorithm>
#include <cassert>
#include <chrono>
#include <cstdlib>
#include <iostream>
#include <list>
#include <memory>
#include <string>
#include <vector>
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rcl/guard_condition.h"
#include "rcl/wait.h"
#include "rclcpp/any_executable.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/memory_strategies.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
// Forward declaration is used in convenience method signature.
namespace node
{
class Node;
} // namespace node
namespace executor
{
/// Return codes to be used with spin_until_future_complete.
/**
* SUCCESS: The future is complete and can be accessed with "get" without blocking.
* INTERRUPTED: The future is not complete, spinning was interrupted by Ctrl-C or another error.
* TIMEOUT: Spinning timed out.
*/
enum class FutureReturnCode {SUCCESS, INTERRUPTED, TIMEOUT};
RCLCPP_PUBLIC
std::ostream &
operator<<(std::ostream & os, const FutureReturnCode & future_return_code);
RCLCPP_PUBLIC
std::string
to_string(const FutureReturnCode & future_return_code);
///
/**
* Options to be passed to the executor constructor.
*/
struct ExecutorArgs
{
memory_strategy::MemoryStrategy::SharedPtr memory_strategy;
size_t max_conditions = 0;
};
static inline ExecutorArgs create_default_executor_arguments()
{
ExecutorArgs args;
args.memory_strategy = memory_strategies::create_default_strategy();
args.max_conditions = 0;
return args;
}
/// Coordinate the order and timing of available communication tasks.
/**
* Executor provides spin functions (including spin_node_once and spin_some).
@@ -50,33 +93,21 @@ namespace executor
class Executor
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Executor);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Executor)
/// Default constructor.
// \param[in] ms The memory strategy to be used with this executor.
explicit Executor(memory_strategy::MemoryStrategy::SharedPtr ms =
memory_strategies::create_default_strategy())
: interrupt_guard_condition_(rmw_create_guard_condition()),
memory_strategy_(ms)
{
}
RCLCPP_PUBLIC
explicit Executor(const ExecutorArgs & args = create_default_executor_arguments());
/// Default destructor.
virtual ~Executor()
{
// Try to deallocate the interrupt guard condition.
if (interrupt_guard_condition_ != nullptr) {
rmw_ret_t status = rmw_destroy_guard_condition(interrupt_guard_condition_);
if (status != RMW_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rmw_get_error_string_safe());
}
}
}
RCLCPP_PUBLIC
virtual ~Executor();
/// Do work periodically as it becomes available to us. Blocking call, may block indefinitely.
// It is up to the implementation of Executor to implement spin.
virtual void spin() = 0;
virtual void
spin() = 0;
/// Add a node to the executor.
/**
@@ -86,26 +117,14 @@ public:
* the executor is blocked at the rmw layer while waiting for work and it is notified that a new
* node was added, it will wake up.
*/
RCLCPP_PUBLIC
virtual void
add_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify = true)
{
// Check to ensure node not already added
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (node == node_ptr) {
// TODO(jacquelinekay): Use a different error here?
throw std::runtime_error("Cannot add node to executor, node already added.");
}
}
weak_nodes_.push_back(node_ptr);
if (notify) {
// Interrupt waiting to handle new node
rmw_ret_t status = rmw_trigger_guard_condition(interrupt_guard_condition_);
if (status != RMW_RET_OK) {
throw std::runtime_error(rmw_get_error_string_safe());
}
}
}
add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify = true);
/// Convenience function which takes Node and forwards NodeBaseInterface.
RCLCPP_PUBLIC
virtual void
add_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify = true);
/// Remove a node from the executor.
/**
@@ -114,29 +133,14 @@ public:
* This is useful if the last node was removed from the executor while the executor was blocked
* waiting for work in another thread, because otherwise the executor would never be notified.
*/
RCLCPP_PUBLIC
virtual void
remove_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify = true)
{
bool node_removed = false;
weak_nodes_.erase(
std::remove_if(
weak_nodes_.begin(), weak_nodes_.end(),
[&](std::weak_ptr<rclcpp::node::Node> & i)
{
bool matched = (i.lock() == node_ptr);
node_removed |= matched;
return matched;
}));
if (notify) {
// If the node was matched and removed, interrupt waiting
if (node_removed) {
rmw_ret_t status = rmw_trigger_guard_condition(interrupt_guard_condition_);
if (status != RMW_RET_OK) {
throw std::runtime_error(rmw_get_error_string_safe());
}
}
}
}
remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify = true);
/// Convenience function which takes Node and forwards NodeBaseInterface.
RCLCPP_PUBLIC
virtual void
remove_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify = true);
/// Add a node to executor, execute the next available unit of work, and remove the node.
/**
@@ -146,28 +150,40 @@ public:
* function to be non-blocking.
*/
template<typename T = std::milli>
void spin_node_once(rclcpp::node::Node::SharedPtr node,
void
spin_node_once(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t, T>(-1))
{
this->add_node(node, false);
// non-blocking = true
auto any_exec = get_next_executable(timeout);
if (any_exec) {
execute_any_executable(any_exec);
}
this->remove_node(node, false);
return spin_node_once_nanoseconds(
node,
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
);
}
/// Convenience function which takes Node and forwards NodeBaseInterface.
template<typename NodeT = rclcpp::node::Node, typename T = std::milli>
void
spin_node_once(std::shared_ptr<NodeT> node,
std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t, T>(-1))
{
return spin_node_once_nanoseconds(
node->get_node_base_interface(),
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
);
}
/// Add a node, complete all immediately available work, and remove the node.
/**
* \param[in] node Shared pointer to the node to add.
*/
void spin_node_some(rclcpp::node::Node::SharedPtr node)
{
this->add_node(node, false);
spin_some();
this->remove_node(node, false);
}
RCLCPP_PUBLIC
void
spin_node_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node);
/// Convenience function which takes Node and forwards NodeBaseInterface.
RCLCPP_PUBLIC
void
spin_node_some(std::shared_ptr<rclcpp::node::Node> node);
/// Complete all available queued work without blocking.
/**
@@ -176,438 +192,166 @@ public:
* Adding subscriptions, timers, services, etc. with blocking callbacks will cause this function
* to block (which may have unintended consequences).
*/
virtual void spin_some()
RCLCPP_PUBLIC
virtual void
spin_some();
RCLCPP_PUBLIC
virtual void
spin_once(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
/// Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
/**
* \param[in] future The future to wait on. If SUCCESS, the future is safe to access after this
* function.
* \param[in] timeout Optional timeout parameter, which gets passed to Executor::spin_node_once.
* `-1` is block forever, `0` is non-blocking.
* If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
* code.
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
*/
template<typename ResponseT, typename TimeT = std::milli>
FutureReturnCode
spin_until_future_complete(
std::shared_future<ResponseT> & future,
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
{
while (AnyExecutable::SharedPtr any_exec =
get_next_executable(std::chrono::milliseconds::zero()))
{
execute_any_executable(any_exec);
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
// inside a callback executed by an executor.
// Check the future before entering the while loop.
// If the future is already complete, don't try to spin.
std::future_status status = future.wait_for(std::chrono::seconds(0));
if (status == std::future_status::ready) {
return FutureReturnCode::SUCCESS;
}
auto end_time = std::chrono::steady_clock::now();
std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
timeout);
if (timeout_ns > std::chrono::nanoseconds::zero()) {
end_time += timeout_ns;
}
std::chrono::nanoseconds timeout_left = timeout_ns;
while (rclcpp::utilities::ok()) {
// Do one item of work.
spin_once(timeout_left);
// Check if the future is set, return SUCCESS if it is.
status = future.wait_for(std::chrono::seconds(0));
if (status == std::future_status::ready) {
return FutureReturnCode::SUCCESS;
}
// If the original timeout is < 0, then this is blocking, never TIMEOUT.
if (timeout_ns < std::chrono::nanoseconds::zero()) {
continue;
}
// Otherwise check if we still have time to wait, return TIMEOUT if not.
auto now = std::chrono::steady_clock::now();
if (now >= end_time) {
return FutureReturnCode::TIMEOUT;
}
// Subtract the elapsed time from the original timeout.
timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
}
// The future did not complete before ok() returned false, return INTERRUPTED.
return FutureReturnCode::INTERRUPTED;
}
/// Cancel any running spin* function, causing it to return.
/* This function can be called asynchonously from any thread. */
RCLCPP_PUBLIC
void
cancel();
/// Support dynamic switching of the memory strategy.
/**
* 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.
*/
RCLCPP_PUBLIC
void
set_memory_strategy(memory_strategy::MemoryStrategy::SharedPtr memory_strategy)
{
if (memory_strategy == nullptr) {
throw std::runtime_error("Received NULL memory strategy in executor.");
}
memory_strategy_ = memory_strategy;
}
set_memory_strategy(memory_strategy::MemoryStrategy::SharedPtr memory_strategy);
protected:
RCLCPP_PUBLIC
void
spin_node_once_nanoseconds(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::nanoseconds timeout);
/// Find the next available executable and do the work associated with it.
/** \param[in] any_exec Union structure that can hold any executable type (timer, subscription,
* service, client).
*/
RCLCPP_PUBLIC
void
execute_any_executable(AnyExecutable::SharedPtr any_exec)
{
if (!any_exec) {
return;
}
if (any_exec->timer) {
execute_timer(any_exec->timer);
}
if (any_exec->subscription) {
execute_subscription(any_exec->subscription);
}
if (any_exec->subscription_intra_process) {
execute_intra_process_subscription(any_exec->subscription_intra_process);
}
if (any_exec->service) {
execute_service(any_exec->service);
}
if (any_exec->client) {
execute_client(any_exec->client);
}
// Reset the callback_group, regardless of type
any_exec->callback_group->can_be_taken_from().store(true);
// Wake the wait, because it may need to be recalculated or work that
// was previously blocked is now available.
rmw_ret_t status = rmw_trigger_guard_condition(interrupt_guard_condition_);
if (status != RMW_RET_OK) {
throw std::runtime_error(rmw_get_error_string_safe());
}
}
execute_any_executable(AnyExecutable::SharedPtr any_exec);
RCLCPP_PUBLIC
static void
execute_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription)
{
std::shared_ptr<void> message = subscription->create_message();
bool taken = false;
rmw_message_info_t message_info;
auto ret =
rmw_take_with_info(subscription->get_subscription_handle(),
message.get(), &taken, &message_info);
if (ret == RMW_RET_OK) {
if (taken) {
message_info.from_intra_process = false;
subscription->handle_message(message, message_info);
}
} else {
fprintf(stderr,
"[rclcpp::error] take failed for subscription on topic '%s': %s\n",
subscription->get_topic_name().c_str(), rmw_get_error_string_safe());
}
subscription->return_message(message);
}
rclcpp::subscription::SubscriptionBase::SharedPtr subscription);
RCLCPP_PUBLIC
static void
execute_intra_process_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription)
{
rcl_interfaces::msg::IntraProcessMessage ipm;
bool taken = false;
rmw_message_info_t message_info;
rmw_ret_t status = rmw_take_with_info(
subscription->get_intra_process_subscription_handle(),
&ipm,
&taken,
&message_info);
if (status == RMW_RET_OK) {
if (taken) {
message_info.from_intra_process = true;
subscription->handle_intra_process_message(ipm, message_info);
}
} else {
fprintf(stderr,
"[rclcpp::error] take failed for intra process subscription on topic '%s': %s\n",
subscription->get_topic_name().c_str(), rmw_get_error_string_safe());
}
}
rclcpp::subscription::SubscriptionBase::SharedPtr subscription);
RCLCPP_PUBLIC
static void
execute_timer(
rclcpp::timer::TimerBase::SharedPtr timer)
{
timer->execute_callback();
}
execute_timer(rclcpp::timer::TimerBase::SharedPtr timer);
RCLCPP_PUBLIC
static void
execute_service(
rclcpp::service::ServiceBase::SharedPtr service)
{
std::shared_ptr<void> request_header = service->create_request_header();
std::shared_ptr<void> request = service->create_request();
bool taken = false;
rmw_ret_t status = rmw_take_request(
service->get_service_handle(),
request_header.get(),
request.get(),
&taken);
if (status == RMW_RET_OK) {
if (taken) {
service->handle_request(request_header, request);
}
} else {
fprintf(stderr,
"[rclcpp::error] take request failed for server of service '%s': %s\n",
service->get_service_name().c_str(), rmw_get_error_string_safe());
}
}
execute_service(rclcpp::service::ServiceBase::SharedPtr service);
RCLCPP_PUBLIC
static void
execute_client(
rclcpp::client::ClientBase::SharedPtr client)
{
std::shared_ptr<void> request_header = client->create_request_header();
std::shared_ptr<void> response = client->create_response();
bool taken = false;
rmw_ret_t status = rmw_take_response(
client->get_client_handle(),
request_header.get(),
response.get(),
&taken);
if (status == RMW_RET_OK) {
if (taken) {
client->handle_response(request_header, response);
}
} else {
fprintf(stderr,
"[rclcpp::error] take response failed for client of service '%s': %s\n",
client->get_service_name().c_str(), rmw_get_error_string_safe());
}
}
execute_client(rclcpp::client::ClientBase::SharedPtr client);
/*** Populate class storage from stored weak node pointers and wait. ***/
template<typename T = std::milli>
RCLCPP_PUBLIC
void
wait_for_work(std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t, T>(-1))
{
memory_strategy_->clear_active_entities();
wait_for_work(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
// Collect the subscriptions and timers to be waited on
bool has_invalid_weak_nodes = memory_strategy_->collect_entities(weak_nodes_);
// Clean up any invalid nodes, if they were detected
if (has_invalid_weak_nodes) {
weak_nodes_.erase(
remove_if(weak_nodes_.begin(), weak_nodes_.end(),
[](std::weak_ptr<rclcpp::node::Node> i)
{
return i.expired();
}));
}
// Use the number of subscriptions to allocate memory in the handles
rmw_subscriptions_t subscriber_handles;
subscriber_handles.subscriber_count =
memory_strategy_->fill_subscriber_handles(subscriber_handles.subscribers);
rmw_services_t service_handles;
service_handles.service_count =
memory_strategy_->fill_service_handles(service_handles.services);
rmw_clients_t client_handles;
client_handles.client_count =
memory_strategy_->fill_client_handles(client_handles.clients);
// The number of guard conditions is fixed at 2: 1 for the ctrl-c guard cond,
// and one for the executor's guard cond (interrupt_guard_condition_)
size_t number_of_guard_conds = 2;
rmw_guard_conditions_t guard_condition_handles;
guard_condition_handles.guard_condition_count = number_of_guard_conds;
guard_condition_handles.guard_conditions = static_cast<void **>(guard_cond_handles_.data());
if (guard_condition_handles.guard_conditions == NULL &&
number_of_guard_conds > 0)
{
// TODO(wjwwood): Use a different error here? maybe std::bad_alloc?
throw std::runtime_error("Could not malloc for guard condition pointers.");
}
// Put the global ctrl-c guard condition in
assert(guard_condition_handles.guard_condition_count > 1);
guard_condition_handles.guard_conditions[0] = \
rclcpp::utilities::get_global_sigint_guard_condition()->data;
// Put the executor's guard condition in
guard_condition_handles.guard_conditions[1] = \
interrupt_guard_condition_->data;
rmw_time_t * wait_timeout = NULL;
rmw_time_t rmw_timeout;
auto next_timer_duration = get_earliest_timer();
// If the next timer timeout must preempt the requested timeout
// or if the requested timeout blocks forever, and there exists a valid timer,
// replace the requested timeout with the next timeout.
bool has_valid_timer = next_timer_duration >= std::chrono::nanoseconds::zero();
if ((next_timer_duration < timeout ||
timeout < std::chrono::duration<int64_t, T>::zero()) && has_valid_timer)
{
rmw_timeout.sec =
std::chrono::duration_cast<std::chrono::seconds>(next_timer_duration).count();
rmw_timeout.nsec = next_timer_duration.count() % (1000 * 1000 * 1000);
wait_timeout = &rmw_timeout;
} else if (timeout >= std::chrono::duration<int64_t, T>::zero()) {
// Convert timeout representation to rmw_time
rmw_timeout.sec = std::chrono::duration_cast<std::chrono::seconds>(timeout).count();
rmw_timeout.nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count() %
(1000 * 1000 * 1000);
wait_timeout = &rmw_timeout;
}
// Now wait on the waitable subscriptions and timers
rmw_ret_t status = rmw_wait(
&subscriber_handles,
&guard_condition_handles,
&service_handles,
&client_handles,
wait_timeout);
if (status != RMW_RET_OK && status != RMW_RET_TIMEOUT) {
throw std::runtime_error(rmw_get_error_string_safe());
}
// If ctrl-c guard condition, return directly
if (guard_condition_handles.guard_conditions[0] != 0) {
// Make sure to free or clean memory
memory_strategy_->clear_handles();
return;
}
memory_strategy_->revalidate_handles();
}
/******************************/
rclcpp::node::Node::SharedPtr
get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
if (!group) {
return rclcpp::node::Node::SharedPtr();
}
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto callback_group = weak_group.lock();
if (callback_group == group) {
return node;
}
}
}
return rclcpp::node::Node::SharedPtr();
}
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group);
RCLCPP_PUBLIC
rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_timer(
rclcpp::timer::TimerBase::SharedPtr timer)
{
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & weak_timer : group->get_timer_ptrs()) {
auto t = weak_timer.lock();
if (t == timer) {
return group;
}
}
}
}
return rclcpp::callback_group::CallbackGroup::SharedPtr();
}
get_group_by_timer(rclcpp::timer::TimerBase::SharedPtr timer);
RCLCPP_PUBLIC
void
get_next_timer(AnyExecutable::SharedPtr any_exec)
{
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group || !group->can_be_taken_from().load()) {
continue;
}
for (auto & timer_ref : group->get_timer_ptrs()) {
auto timer = timer_ref.lock();
if (timer && timer->check_and_trigger()) {
any_exec->timer = timer;
any_exec->callback_group = group;
node = get_node_by_group(group);
return;
}
}
}
}
}
std::chrono::nanoseconds
get_earliest_timer()
{
std::chrono::nanoseconds latest = std::chrono::nanoseconds::max();
bool timers_empty = true;
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group || !group->can_be_taken_from().load()) {
continue;
}
for (auto & timer_ref : group->get_timer_ptrs()) {
timers_empty = false;
// Check the expected trigger time
auto timer = timer_ref.lock();
if (timer && timer->time_until_trigger() < latest) {
latest = timer->time_until_trigger();
}
}
}
}
if (timers_empty) {
return std::chrono::nanoseconds(-1);
}
return latest;
}
get_next_timer(AnyExecutable::SharedPtr any_exec);
RCLCPP_PUBLIC
AnyExecutable::SharedPtr
get_next_ready_executable()
{
auto any_exec = memory_strategy_->instantiate_next_executable();
// Check the timers to see if there are any that are ready, if so return
get_next_timer(any_exec);
if (any_exec->timer) {
return any_exec;
}
// Check the subscriptions to see if there are any that are ready
memory_strategy_->get_next_subscription(any_exec, weak_nodes_);
if (any_exec->subscription || any_exec->subscription_intra_process) {
return any_exec;
}
// Check the services to see if there are any that are ready
memory_strategy_->get_next_service(any_exec, weak_nodes_);
if (any_exec->service) {
return any_exec;
}
// Check the clients to see if there are any that are ready
memory_strategy_->get_next_client(any_exec, weak_nodes_);
if (any_exec->client) {
return any_exec;
}
// If there is no ready executable, return a null ptr
return nullptr;
}
get_next_ready_executable();
template<typename T = std::milli>
RCLCPP_PUBLIC
AnyExecutable::SharedPtr
get_next_executable(std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t,
T>(-1))
{
// Check to see if there are any subscriptions or timers needing service
// TODO(wjwwood): improve run to run efficiency of this function
auto any_exec = get_next_ready_executable();
// If there are none
if (!any_exec) {
// Wait for subscriptions or timers to work on
wait_for_work(timeout);
// Try again
any_exec = get_next_ready_executable();
}
// At this point any_exec should be valid with either a valid subscription
// or a valid timer, or it should be a null shared_ptr
if (any_exec) {
// If it is valid, check to see if the group is mutually exclusive or
// not, then mark it accordingly
if (any_exec->callback_group && any_exec->callback_group->type() == \
callback_group::CallbackGroupType::MutuallyExclusive)
{
// It should not have been taken otherwise
assert(any_exec->callback_group->can_be_taken_from().load());
// Set to false to indicate something is being run from this group
any_exec->callback_group->can_be_taken_from().store(false);
}
}
return any_exec;
}
get_next_executable(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
/// 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.
rmw_guard_condition_t * interrupt_guard_condition_;
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
/// Waitset for managing entities that the rmw layer waits on.
rcl_wait_set_t waitset_ = rcl_get_zero_initialized_wait_set();
/// The memory strategy: an interface for handling user-defined memory allocation strategies.
memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;
private:
RCLCPP_DISABLE_COPY(Executor);
RCLCPP_DISABLE_COPY(Executor)
std::vector<std::weak_ptr<rclcpp::node::Node>> weak_nodes_;
std::array<void *, 2> guard_cond_handles_;
std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
};
} // namespace executor

View File

@@ -12,76 +12,113 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_EXECUTORS_HPP_
#define RCLCPP_RCLCPP_EXECUTORS_HPP_
#ifndef RCLCPP__EXECUTORS_HPP_
#define RCLCPP__EXECUTORS_HPP_
#include <future>
#include <rclcpp/executors/multi_threaded_executor.hpp>
#include <rclcpp/executors/single_threaded_executor.hpp>
#include <rclcpp/node.hpp>
#include <rclcpp/utilities.hpp>
#include <memory>
#include "rclcpp/executors/multi_threaded_executor.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// Create a default single-threaded executor and execute any immediately available work.
/** \param[in] node_ptr Shared pointer to the node to spin. */
RCLCPP_PUBLIC
void
spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
RCLCPP_PUBLIC
void
spin_some(rclcpp::node::Node::SharedPtr node_ptr);
/// Create a default single-threaded executor and spin the specified node.
/** \param[in] node_ptr Shared pointer to the node to spin. */
RCLCPP_PUBLIC
void
spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
RCLCPP_PUBLIC
void
spin(rclcpp::node::Node::SharedPtr node_ptr);
namespace executors
{
using rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor;
using rclcpp::executors::single_threaded_executor::SingleThreadedExecutor;
/// Return codes to be used with spin_until_future_complete.
/**
* SUCCESS: The future is complete and can be accessed with "get" without blocking.
* INTERRUPTED: The future is not complete, spinning was interrupted by Ctrl-C or another error.
* TIMEOUT: Spinning timed out.
*/
enum FutureReturnCode {SUCCESS, INTERRUPTED, TIMEOUT};
/// Spin (blocking) until the future is complete, until the function times out (if applicable),
/// or until rclcpp is interrupted.
/// Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
/**
* \param[in] executor The executor which will spin the node.
* \param[in] node_ptr The node to spin.
* \param[in] future The future to wait on. If SUCCESS, the future is safe to access after this function
* \param[in] timeout Optional timeout parameter, which gets passed to Executor::spin_node_once.
-1 is block forever, 0 is non-blocking.
If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return code.
* \return The return code, one of SUCCESS, INTERRUPTED, or TIMEOUT.
* \param[in] future The future to wait on. If `SUCCESS`, the future is safe to
* access after this function
* \param[in] timeout Optional timeout parameter, which gets passed to
* Executor::spin_node_once.
* `-1` is block forever, `0` is non-blocking.
* If the time spent inside the blocking loop exceeds this timeout, return a `TIMEOUT` return code.
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
*/
template<typename ResponseT, typename TimeT = std::milli>
FutureReturnCode
rclcpp::executor::FutureReturnCode
spin_node_until_future_complete(
rclcpp::executor::Executor & executor, rclcpp::node::Node::SharedPtr node_ptr,
rclcpp::executor::Executor & executor,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
std::shared_future<ResponseT> & future,
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
{
// TODO(wjwwood): does not work recursively right, can't call spin_node_until_future_complete
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
// inside a callback executed by an executor.
// Check the future before entering the while loop.
// If the future is already complete, don't try to spin.
std::future_status status = future.wait_for(std::chrono::seconds(0));
auto start_time = std::chrono::system_clock::now();
while (status != std::future_status::ready && rclcpp::utilities::ok()) {
executor.spin_node_once(node_ptr, timeout);
if (timeout.count() >= 0) {
if (start_time + timeout < std::chrono::system_clock::now()) {
return TIMEOUT;
}
}
status = future.wait_for(std::chrono::seconds(0));
}
// If the future completed, and we weren't interrupted by ctrl-C, return the response
if (status == std::future_status::ready) {
return FutureReturnCode::SUCCESS;
}
return FutureReturnCode::INTERRUPTED;
executor.add_node(node_ptr);
auto retcode = executor.spin_until_future_complete(future, timeout);
executor.remove_node(node_ptr);
return retcode;
}
} // namespace executors
} // namespace rclcpp
template<typename NodeT = rclcpp::node::Node, typename ResponseT, typename TimeT = std::milli>
rclcpp::executor::FutureReturnCode
spin_node_until_future_complete(
rclcpp::executor::Executor & executor,
std::shared_ptr<NodeT> node_ptr,
std::shared_future<ResponseT> & future,
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
{
return rclcpp::executors::spin_node_until_future_complete(
executor,
node_ptr->get_node_base_interface(),
future,
timeout);
}
#endif /* RCLCPP_RCLCPP_EXECUTORS_HPP_ */
} // namespace executors
template<typename FutureT, typename TimeT = std::milli>
rclcpp::executor::FutureReturnCode
spin_until_future_complete(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
std::shared_future<FutureT> & future,
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
{
rclcpp::executors::SingleThreadedExecutor executor;
return executors::spin_node_until_future_complete<FutureT>(executor, node_ptr, future, timeout);
}
template<typename NodeT = rclcpp::node::Node, typename FutureT, typename TimeT = std::milli>
rclcpp::executor::FutureReturnCode
spin_until_future_complete(
std::shared_ptr<NodeT> node_ptr,
std::shared_future<FutureT> & future,
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
{
return rclcpp::spin_until_future_complete(node_ptr->get_node_base_interface(), future, timeout);
}
} // namespace rclcpp
#endif // RCLCPP__EXECUTORS_HPP_

View File

@@ -15,18 +15,14 @@
#ifndef RCLCPP__EXECUTORS__MULTI_THREADED_EXECUTOR_HPP_
#define RCLCPP__EXECUTORS__MULTI_THREADED_EXECUTOR_HPP_
#include <rmw/rmw.h>
#include <cassert>
#include <cstdlib>
#include <memory>
#include <mutex>
#include <vector>
#include <thread>
#include <unordered_map>
#include "rclcpp/executor.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/memory_strategies.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
@@ -38,62 +34,30 @@ namespace multi_threaded_executor
class MultiThreadedExecutor : public executor::Executor
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MultiThreadedExecutor);
RCLCPP_SMART_PTR_DEFINITIONS(MultiThreadedExecutor)
MultiThreadedExecutor(memory_strategy::MemoryStrategy::SharedPtr ms =
memory_strategies::create_default_strategy())
: executor::Executor(ms)
{
number_of_threads_ = std::thread::hardware_concurrency();
if (number_of_threads_ == 0) {
number_of_threads_ = 1;
}
}
RCLCPP_PUBLIC
MultiThreadedExecutor(
const executor::ExecutorArgs & args = rclcpp::executor::create_default_executor_arguments());
virtual ~MultiThreadedExecutor() {}
RCLCPP_PUBLIC
virtual ~MultiThreadedExecutor();
RCLCPP_PUBLIC
void
spin()
{
std::vector<std::thread> threads;
{
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
size_t thread_id_ = 1; // Use a _ suffix to avoid shadowing `rclcpp::thread_id`
for (size_t i = number_of_threads_; i > 0; --i) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
auto func = std::bind(&MultiThreadedExecutor::run, this, thread_id_++);
threads.emplace_back(func);
}
}
for (auto & thread : threads) {
thread.join();
}
}
spin();
RCLCPP_PUBLIC
size_t
get_number_of_threads()
{
return number_of_threads_;
}
get_number_of_threads();
protected:
RCLCPP_PUBLIC
void
run(size_t this_thread_number);
private:
void run(size_t this_thread_id)
{
rclcpp::thread_id = this_thread_id;
while (rclcpp::utilities::ok()) {
executor::AnyExecutable::SharedPtr any_exec;
{
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
if (!rclcpp::utilities::ok()) {
return;
}
any_exec = get_next_executable();
}
execute_any_executable(any_exec);
}
}
RCLCPP_DISABLE_COPY(MultiThreadedExecutor);
RCLCPP_DISABLE_COPY(MultiThreadedExecutor)
std::mutex wait_mutex_;
size_t number_of_threads_;

View File

@@ -28,6 +28,7 @@
#include "rclcpp/node.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/rate.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
@@ -41,29 +42,26 @@ namespace single_threaded_executor
class SingleThreadedExecutor : public executor::Executor
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SingleThreadedExecutor);
RCLCPP_SMART_PTR_DEFINITIONS(SingleThreadedExecutor)
/// Default constructor. See the default constructor for Executor.
SingleThreadedExecutor(memory_strategy::MemoryStrategy::SharedPtr ms =
memory_strategies::create_default_strategy())
: executor::Executor(ms) {}
RCLCPP_PUBLIC
SingleThreadedExecutor(
const executor::ExecutorArgs & args = rclcpp::executor::create_default_executor_arguments());
/// Default destrcutor.
virtual ~SingleThreadedExecutor() {}
RCLCPP_PUBLIC
virtual ~SingleThreadedExecutor();
/// Single-threaded implementation of spin.
// This function will block until work comes in, execute it, and keep blocking.
// It will only be interrupt by a CTRL-C (managed by the global signal handler).
void spin()
{
while (rclcpp::utilities::ok()) {
auto any_exec = get_next_executable();
execute_any_executable(any_exec);
}
}
RCLCPP_PUBLIC
void
spin();
private:
RCLCPP_DISABLE_COPY(SingleThreadedExecutor);
RCLCPP_DISABLE_COPY(SingleThreadedExecutor)
};
} // namespace single_threaded_executor

View File

@@ -0,0 +1,63 @@
// Copyright 2017 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__EXPAND_TOPIC_OR_SERVICE_NAME_HPP_
#define RCLCPP__EXPAND_TOPIC_OR_SERVICE_NAME_HPP_
#include <string>
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// Expand a topic or service name and throw if it is not valid.
/**
* This function can be used to "just" validate a topic or service name too,
* since expanding the topic name is required to fully validate a name.
*
* If the name is invalid, then InvalidTopicNameError is thrown or
* InvalidServiceNameError if is_service is true.
*
* This function can take any form of a topic or service name, i.e. it does not
* have to be a fully qualified name.
* The node name and namespace are used to expand it if necessary while
* validating it.
*
* Expansion is done with rcl_expand_topic_name.
* The validation is doen with rcl_validate_topic_name and
* rmw_validate_full_topic_name, so details about failures can be found in the
* documentation for those functions.
*
* \param name the topic or service name to be validated
* \param node_name the name of the node associated with the name
* \param namespace_ the namespace of the node associated with the name
* \param is_service if true InvalidServiceNameError is thrown instead
* \returns expanded (and validated) topic name
* \throws InvalidTopicNameError if name is invalid and is_service is false
* \throws InvalidServiceNameError if name is invalid and is_service is true
* \throws std::bad_alloc if memory cannot be allocated
* \throws RCLError if an unexpect error occurs
*/
RCLCPP_PUBLIC
std::string
expand_topic_or_service_name(
const std::string & name,
const std::string & node_name,
const std::string & namespace_,
bool is_service = false);
} // namespace rclcpp
#endif // RCLCPP__EXPAND_TOPIC_OR_SERVICE_NAME_HPP_

View File

@@ -12,13 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_FUNCTION_TRAITS_HPP_
#define RCLCPP_RCLCPP_FUNCTION_TRAITS_HPP_
#ifndef RCLCPP__FUNCTION_TRAITS_HPP_
#define RCLCPP__FUNCTION_TRAITS_HPP_
#include <functional>
#include <memory>
#include <tuple>
namespace rclcpp
{
namespace function_traits
{
/* NOTE(esteve):
* We support service callbacks that can optionally take the request id,
* which should be possible with two overloaded create_service methods,
@@ -49,6 +55,8 @@ struct function_traits
template<std::size_t N>
using argument_type = typename std::tuple_element<N, arguments>::type;
using return_type = typename function_traits<decltype( & FunctionT::operator())>::return_type;
};
// Free functions
@@ -61,17 +69,57 @@ struct function_traits<ReturnTypeT(Args ...)>
template<std::size_t N>
using argument_type = typename std::tuple_element<N, arguments>::type;
using return_type = ReturnTypeT;
};
// Function pointers
template<typename ReturnTypeT, typename ... Args>
struct function_traits<ReturnTypeT (*)(Args ...)>: public function_traits<ReturnTypeT(Args ...)>
struct function_traits<ReturnTypeT (*)(Args ...)>: function_traits<ReturnTypeT(Args ...)>
{};
// std::bind for object methods
template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ... FArgs>
#if defined _LIBCPP_VERSION // libc++ (Clang)
struct function_traits<std::__1::__bind<ReturnTypeT (ClassT::*)(Args ...), FArgs ...>>
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
struct function_traits<std::_Bind<std::_Mem_fn<ReturnTypeT (ClassT::*)(Args ...)>(FArgs ...)>>
#elif defined _MSC_VER // MS Visual Studio
struct function_traits<
std::_Binder<std::_Unforced, ReturnTypeT(__cdecl ClassT::*)(Args ...), FArgs ...>
>
#else
#error "Unsupported C++ compiler / standard library"
#endif
: function_traits<ReturnTypeT(Args ...)>
{};
// std::bind for free functions
template<typename ReturnTypeT, typename ... Args, typename ... FArgs>
#if defined _LIBCPP_VERSION // libc++ (Clang)
struct function_traits<std::__1::__bind<ReturnTypeT( &)(Args ...), FArgs ...>>
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
struct function_traits<std::_Bind<ReturnTypeT(*(FArgs ...))(Args ...)>>
#elif defined _MSC_VER // MS Visual Studio
struct function_traits<std::_Binder<std::_Unforced, ReturnTypeT(__cdecl &)(Args ...), FArgs ...>>
#else
#error "Unsupported C++ compiler / standard library"
#endif
: function_traits<ReturnTypeT(Args ...)>
{};
// Lambdas
template<typename ClassT, typename ReturnTypeT, typename ... Args>
struct function_traits<ReturnTypeT (ClassT::*)(Args ...) const>
: public function_traits<ReturnTypeT(ClassT &, Args ...)>
: function_traits<ReturnTypeT(ClassT &, Args ...)>
{};
template<typename FunctionT>
struct function_traits<FunctionT &>: function_traits<FunctionT>
{};
template<typename FunctionT>
struct function_traits<FunctionT &&>: function_traits<FunctionT>
{};
/* NOTE(esteve):
@@ -95,6 +143,9 @@ struct same_arguments : std::is_same<
typename function_traits<FunctorBT>::arguments
>
{};
} /* namespace rclcpp */
#endif /* RCLCPP_RCLCPP_FUNCTION_TRAITS_HPP_ */
} // namespace function_traits
} // namespace rclcpp
#endif // RCLCPP__FUNCTION_TRAITS_HPP_

View File

@@ -0,0 +1,174 @@
// Copyright 2016 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__GRAPH_LISTENER_HPP_
#define RCLCPP__GRAPH_LISTENER_HPP_
#include <atomic>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
#include "rcl/guard_condition.h"
#include "rcl/wait.h"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace graph_listener
{
/// Thrown when a function is called on a GraphListener that is already shutdown.
class GraphListenerShutdownError : public std::runtime_error
{
public:
GraphListenerShutdownError()
: std::runtime_error("GraphListener already shutdown") {}
};
/// Thrown when a node has already been added to the GraphListener.
class NodeAlreadyAddedError : public std::runtime_error
{
public:
NodeAlreadyAddedError()
: std::runtime_error("node already added") {}
};
/// Thrown when the given node is not in the GraphListener.
class NodeNotFoundError : public std::runtime_error
{
public:
NodeNotFoundError()
: std::runtime_error("node not found") {}
};
/// Notifies many nodes of graph changes by listening in a thread.
class GraphListener : public std::enable_shared_from_this<GraphListener>
{
public:
RCLCPP_PUBLIC
GraphListener();
RCLCPP_PUBLIC
virtual ~GraphListener();
/// Start the graph listener's listen thread if it hasn't been started.
/**
* This function is thread-safe.
*
* \throws GraphListenerShutdownError if the GraphListener is shutdown
*/
RCLCPP_PUBLIC
virtual
void
start_if_not_started();
/// Add a node to the graph listener's list of nodes.
/**
* \throws GraphListenerShutdownError if the GraphListener is shutdown
* \throws NodeAlreadyAddedError if the given node is already in the list
* \throws std::invalid_argument if node is nullptr
* \throws std::system_error anything std::mutex::lock() throws
*/
RCLCPP_PUBLIC
virtual
void
add_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
/// Return true if the given node is in the graph listener's list of nodes.
/**
* Also return false if given nullptr.
*
* \throws std::system_error anything std::mutex::lock() throws
*/
RCLCPP_PUBLIC
virtual
bool
has_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
/// Remove a node from the graph listener's list of nodes.
/**
*
* \throws NodeNotFoundError if the given node is not in the list
* \throws std::invalid_argument if node is nullptr
* \throws std::system_error anything std::mutex::lock() throws
*/
RCLCPP_PUBLIC
virtual
void
remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
/// Stop the listening thread.
/**
* The thread cannot be restarted, and the class is defunct after calling.
* This function is called by the ~GraphListener() and does nothing if
* shutdown() was already called.
* This function exists separately from the ~GraphListener() so that it can
* be called before and exceptions can be caught.
*
* If start_if_not_started() was never called, this function still succeeds,
* but start_if_not_started() still cannot be called after this function.
*
* \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
*/
RCLCPP_PUBLIC
virtual
void
shutdown();
/// Return true if shutdown() has been called, else false.
RCLCPP_PUBLIC
virtual
bool
is_shutdown();
protected:
/// Main function for the listening thread.
RCLCPP_PUBLIC
virtual
void
run();
RCLCPP_PUBLIC
virtual
void
run_loop();
private:
RCLCPP_DISABLE_COPY(GraphListener)
std::thread listener_thread_;
bool is_started_;
std::atomic_bool is_shutdown_;
mutable std::mutex shutdown_mutex_;
mutable std::mutex node_graph_interfaces_barrier_mutex_;
mutable std::mutex node_graph_interfaces_mutex_;
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> node_graph_interfaces_;
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
rcl_guard_condition_t * shutdown_guard_condition_;
rcl_wait_set_t wait_set_ = rcl_get_zero_initialized_wait_set();
};
} // namespace graph_listener
} // namespace rclcpp
#endif // RCLCPP__GRAPH_LISTENER_HPP_

View File

@@ -27,11 +27,12 @@
#include <set>
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/intra_process_manager_state.hpp"
#include "rclcpp/intra_process_manager_impl.hpp"
#include "rclcpp/mapped_ring_buffer.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
@@ -39,7 +40,8 @@ namespace intra_process_manager
{
/// This class facilitates intra process communication between nodes.
/* This class is used in the creation of publishers and subscriptions.
/**
* This class is used in the creation of publishers and subscriptions.
* A singleton instance of this class is owned by a rclcpp::Context and a
* rclcpp::Node can use an associated Context to get an instance of this class.
* Nodes which do not have a common Context will not exchange intra process
@@ -53,7 +55,7 @@ namespace intra_process_manager
* When a publisher is created, it advertises on the topic the user provided,
* as well as a "shadowing" topic of type rcl_interfaces/IntraProcessMessage.
* For instance, if the user specified the topic '/namespace/chatter', then the
* corresponding intra process topic might be '/namespace/chatter__intra'.
* corresponding intra process topic might be '/namespace/chatter/_intra'.
* The publisher is also allocated an id which is unique among all publishers
* and subscriptions in this process.
* Additionally, when registered with this class a ring buffer is created and
@@ -119,20 +121,21 @@ namespace intra_process_manager
class IntraProcessManager
{
private:
RCLCPP_DISABLE_COPY(IntraProcessManager);
RCLCPP_DISABLE_COPY(IntraProcessManager)
public:
RCLCPP_SMART_PTR_DEFINITIONS(IntraProcessManager);
RCLCPP_SMART_PTR_DEFINITIONS(IntraProcessManager)
RCLCPP_PUBLIC
explicit IntraProcessManager(
IntraProcessManagerStateBase::SharedPtr state = create_default_state()
)
: state_(state)
{
}
IntraProcessManagerImplBase::SharedPtr state = create_default_impl());
RCLCPP_PUBLIC
virtual ~IntraProcessManager();
/// Register a subscription with the manager, returns subscriptions unique id.
/* In addition to generating a unique intra process id for the subscription,
/**
* In addition to generating a unique intra process id for the subscription,
* this method also stores the topic name of the subscription.
*
* This method is normally called during the creation of a subscription,
@@ -143,27 +146,23 @@ public:
* \param subscription the Subscription to register.
* \return an unsigned 64-bit integer which is the subscription's unique id.
*/
RCLCPP_PUBLIC
uint64_t
add_subscription(subscription::SubscriptionBase::SharedPtr subscription)
{
auto id = IntraProcessManager::get_next_unique_id();
state_->add_subscription(id, subscription);
return id;
}
add_subscription(subscription::SubscriptionBase::SharedPtr subscription);
/// Unregister a subscription using the subscription's unique id.
/* This method does not allocate memory.
/**
* This method does not allocate memory.
*
* \param intra_process_subscription_id id of the subscription to remove.
*/
RCLCPP_PUBLIC
void
remove_subscription(uint64_t intra_process_subscription_id)
{
state_->remove_subscription(intra_process_subscription_id);
}
remove_subscription(uint64_t intra_process_subscription_id);
/// Register a publisher with the manager, returns the publisher unique id.
/* In addition to generating and returning a unique id for the publisher,
/**
* In addition to generating and returning a unique id for the publisher,
* this method creates internal ring buffer storage for "in-flight" intra
* process messages which are stored when store_intra_process_message is
* called with this publisher's unique id.
@@ -194,23 +193,23 @@ public:
auto mrb = mapped_ring_buffer::MappedRingBuffer<MessageT,
typename publisher::Publisher<MessageT, Alloc>::MessageAlloc>::make_shared(
size, publisher->get_allocator());
state_->add_publisher(id, publisher, mrb, size);
impl_->add_publisher(id, publisher, mrb, size);
return id;
}
/// Unregister a publisher using the publisher's unique id.
/* This method does not allocate memory.
/**
* This method does not allocate memory.
*
* \param intra_process_publisher_id id of the publisher to remove.
*/
RCLCPP_PUBLIC
void
remove_publisher(uint64_t intra_process_publisher_id)
{
state_->remove_publisher(intra_process_publisher_id);
}
remove_publisher(uint64_t intra_process_publisher_id);
/// Store a message in the manager, and return the message sequence number.
/* The given message is stored in internal storage using the given publisher
/**
* The given message is stored in internal storage using the given publisher
* id and the newly generated message sequence, which is also returned.
* The combination of publisher id and message sequence number can later
* be used with a subscription id to retrieve the message by calling
@@ -249,7 +248,7 @@ public:
using MRBMessageAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<MessageT>;
using TypedMRB = typename mapped_ring_buffer::MappedRingBuffer<MessageT, MRBMessageAlloc>;
uint64_t message_seq = 0;
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = state_->get_publisher_info_for_id(
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = impl_->get_publisher_info_for_id(
intra_process_publisher_id, message_seq);
typename TypedMRB::SharedPtr typed_buffer = std::static_pointer_cast<TypedMRB>(buffer);
if (!typed_buffer) {
@@ -261,14 +260,15 @@ public:
// TODO(wjwwood): do something when a message was displaced. log debug?
(void)did_replace; // Avoid unused variable warning.
state_->store_intra_process_message(intra_process_publisher_id, message_seq);
impl_->store_intra_process_message(intra_process_publisher_id, message_seq);
// Return the message sequence which is sent to the subscription.
return message_seq;
}
/// Take an intra process message.
/* The intra_process_publisher_id and message_sequence_number parameters
/**
* The intra_process_publisher_id and message_sequence_number parameters
* uniquely identify a message instance, which should be taken.
*
* The requesting_subscriptions_intra_process_id parameter is used to make
@@ -315,7 +315,7 @@ public:
message = nullptr;
size_t target_subs_size = 0;
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = state_->take_intra_process_message(
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = impl_->take_intra_process_message(
intra_process_publisher_id,
message_sequence_number,
requesting_subscriptions_intra_process_id,
@@ -336,41 +336,18 @@ public:
}
/// Return true if the given rmw_gid_t matches any stored Publishers.
RCLCPP_PUBLIC
bool
matches_any_publishers(const rmw_gid_t * id) const
{
return state_->matches_any_publishers(id);
}
matches_any_publishers(const rmw_gid_t * id) const;
private:
static uint64_t get_next_unique_id()
{
auto next_id = next_unique_id_.fetch_add(1, std::memory_order_relaxed);
// Check for rollover (we started at 1).
if (0 == next_id) {
// This puts a technical limit on the number of times you can add a publisher or subscriber.
// But even if you could add (and remove) them at 1 kHz (very optimistic rate)
// it would still be a very long time before you could exhaust the pool of id's:
// 2^64 / 1000 times per sec / 60 sec / 60 min / 24 hours / 365 days = 584,942,417 years
// So around 585 million years. Even at 1 GHz, it would take 585 years.
// I think it's safe to avoid trying to handle overflow.
// If we roll over then it's most likely a bug.
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::overflow_error(
"exhausted the unique id's for publishers and subscribers in this process "
"(congratulations your computer is either extremely fast or extremely old)");
// *INDENT-ON*
}
return next_id;
}
RCLCPP_PUBLIC
static uint64_t
get_next_unique_id();
static std::atomic<uint64_t> next_unique_id_;
IntraProcessManagerStateBase::SharedPtr state_;
IntraProcessManagerImplBase::SharedPtr impl_;
};
std::atomic<uint64_t> IntraProcessManager::next_unique_id_ {1};
} // namespace intra_process_manager
} // namespace rclcpp

View File

@@ -12,33 +12,40 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__INTRA_PROCESS_MANAGER_STATE_HPP_
#define RCLCPP__INTRA_PROCESS_MANAGER_STATE_HPP_
#include <rclcpp/mapped_ring_buffer.hpp>
#include <rclcpp/publisher.hpp>
#include <rclcpp/subscription.hpp>
#ifndef RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
#define RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
#include <algorithm>
#include <atomic>
#include <cstring>
#include <functional>
#include <limits>
#include <memory>
#include <map>
#include <unordered_map>
#include <memory>
#include <mutex>
#include <set>
#include <string>
#include <unordered_map>
#include <utility>
#include "rclcpp/macros.hpp"
#include "rclcpp/mapped_ring_buffer.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace intra_process_manager
{
class IntraProcessManagerStateBase
class IntraProcessManagerImplBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerStateBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerImplBase)
IntraProcessManagerImplBase() = default;
~IntraProcessManagerImplBase() = default;
virtual void
add_subscription(uint64_t id, subscription::SubscriptionBase::SharedPtr subscription) = 0;
@@ -70,16 +77,24 @@ public:
virtual bool
matches_any_publishers(const rmw_gid_t * id) const = 0;
private:
RCLCPP_DISABLE_COPY(IntraProcessManagerImplBase)
};
template<typename Allocator = std::allocator<void>>
class IntraProcessManagerState : public IntraProcessManagerStateBase
class IntraProcessManagerImpl : public IntraProcessManagerImplBase
{
public:
IntraProcessManagerImpl() = default;
~IntraProcessManagerImpl() = default;
void
add_subscription(uint64_t id, subscription::SubscriptionBase::SharedPtr subscription)
{
subscriptions_[id] = subscription;
// subscription->get_topic_name() -> const char * can be used as the key,
// since subscriptions_ shares the ownership of subscription
subscription_ids_by_topic_[subscription->get_topic_name()].insert(id);
}
@@ -127,13 +142,14 @@ public:
uint64_t intra_process_publisher_id,
uint64_t & message_seq)
{
std::lock_guard<std::mutex> lock(runtime_mutex_);
auto it = publishers_.find(intra_process_publisher_id);
if (it == publishers_.end()) {
throw std::runtime_error("store_intra_process_message called with invalid publisher id");
throw std::runtime_error("get_publisher_info_for_id called with invalid publisher id");
}
PublisherInfo & info = it->second;
// Calculate the next message sequence number.
message_seq = info.sequence_number.fetch_add(1, std::memory_order_relaxed);
message_seq = info.sequence_number.fetch_add(1);
return info.buffer;
}
@@ -141,6 +157,7 @@ public:
void
store_intra_process_message(uint64_t intra_process_publisher_id, uint64_t message_seq)
{
std::lock_guard<std::mutex> lock(runtime_mutex_);
auto it = publishers_.find(intra_process_publisher_id);
if (it == publishers_.end()) {
throw std::runtime_error("store_intra_process_message called with invalid publisher id");
@@ -154,7 +171,12 @@ public:
// Figure out what subscriptions should receive the message.
auto & destined_subscriptions = subscription_ids_by_topic_[publisher->get_topic_name()];
// Store the list for later comparison.
info.target_subscriptions_by_message_sequence[message_seq].clear();
if (info.target_subscriptions_by_message_sequence.count(message_seq) == 0) {
info.target_subscriptions_by_message_sequence.emplace(
message_seq, AllocSet(std::less<uint64_t>(), uint64_allocator));
} else {
info.target_subscriptions_by_message_sequence[message_seq].clear();
}
std::copy(
destined_subscriptions.begin(), destined_subscriptions.end(),
// Memory allocation occurs in info.target_subscriptions_by_message_sequence[message_seq]
@@ -173,6 +195,7 @@ public:
size_t & size
)
{
std::lock_guard<std::mutex> lock(runtime_mutex_);
PublisherInfo * info;
{
auto it = publishers_.find(intra_process_publisher_id);
@@ -222,15 +245,31 @@ public:
}
private:
RCLCPP_DISABLE_COPY(IntraProcessManagerImpl)
template<typename T>
using RebindAlloc = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
RebindAlloc<uint64_t> uint64_allocator;
using AllocSet = std::set<uint64_t, std::less<uint64_t>, RebindAlloc<uint64_t>>;
using SubscriptionMap = std::unordered_map<uint64_t, subscription::SubscriptionBase::WeakPtr,
std::hash<uint64_t>, std::equal_to<uint64_t>,
RebindAlloc<std::pair<const uint64_t, subscription::SubscriptionBase::WeakPtr>>>;
using IDTopicMap = std::map<std::string, AllocSet,
std::less<std::string>, RebindAlloc<std::pair<std::string, AllocSet>>>;
struct strcmp_wrapper : public std::binary_function<const char *, const char *, bool>
{
bool
operator()(const char * lhs, const char * rhs) const
{
return std::strcmp(lhs, rhs) < 0;
}
};
using IDTopicMap = std::map<
const char *,
AllocSet,
strcmp_wrapper,
RebindAlloc<std::pair<const std::string, AllocSet>>>;
SubscriptionMap subscriptions_;
@@ -238,7 +277,7 @@ private:
struct PublisherInfo
{
RCLCPP_DISABLE_COPY(PublisherInfo);
RCLCPP_DISABLE_COPY(PublisherInfo)
PublisherInfo() = default;
@@ -257,14 +296,15 @@ private:
RebindAlloc<std::pair<const uint64_t, PublisherInfo>>>;
PublisherMap publishers_;
std::mutex runtime_mutex_;
};
static IntraProcessManagerStateBase::SharedPtr create_default_state()
{
return std::make_shared<IntraProcessManagerState<>>();
}
RCLCPP_PUBLIC
IntraProcessManagerImplBase::SharedPtr
create_default_impl();
} // namespace intra_process_manager
} // namespace rclcpp
#endif // RCLCPP__INTRA_PROCESS_MANAGER_STATE_HPP_
#endif // RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_

View File

@@ -12,10 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_MACROS_HPP_
#define RCLCPP_RCLCPP_MACROS_HPP_
#include <memory>
#include <utility>
/* Disables the copy constructor and operator= for the given class.
#ifndef RCLCPP__MACROS_HPP_
#define RCLCPP__MACROS_HPP_
/**
* Disables the copy constructor and operator= for the given class.
*
* Use in the private section of the class.
*/
@@ -23,31 +27,50 @@
__VA_ARGS__(const __VA_ARGS__ &) = delete; \
__VA_ARGS__ & operator=(const __VA_ARGS__ &) = delete;
/* Defines aliases and static functions for using the Class with smart pointers.
/**
* Defines aliases and static functions for using the Class with smart pointers.
*
* Use in the public section of the class.
* Make sure to include <memory> in the header when using this.
* Make sure to include `<memory>` in the header when using this.
*/
#define RCLCPP_SMART_PTR_DEFINITIONS(...) \
RCLCPP_SHARED_PTR_DEFINITIONS(__VA_ARGS__) \
RCLCPP_WEAK_PTR_DEFINITIONS(__VA_ARGS__) \
RCLCPP_UNIQUE_PTR_DEFINITIONS(__VA_ARGS__)
/* Defines aliases and static functions for using the Class with smart pointers.
/**
* Defines aliases and static functions for using the Class with smart pointers.
*
* Same as RCLCPP_SMART_PTR_DEFINITIONS expect it excludes the static
* Class::make_unique() method definition which does not work on classes which
* are not CopyConstructable.
*
* Use in the public section of the class.
* Make sure to include <memory> in the header when using this.
* Make sure to include `<memory>` in the header when using this.
*/
#define RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(...) \
RCLCPP_SHARED_PTR_DEFINITIONS(__VA_ARGS__) \
RCLCPP_WEAK_PTR_DEFINITIONS(__VA_ARGS__) \
__RCLCPP_UNIQUE_PTR_ALIAS(__VA_ARGS__)
#define __RCLCPP_SHARED_PTR_ALIAS(...) using SharedPtr = std::shared_ptr<__VA_ARGS__>;
/**
* Defines aliases only for using the Class with smart pointers.
*
* Same as RCLCPP_SMART_PTR_DEFINITIONS expect it excludes the static
* method definitions which do not work on pure virtual classes and classes
* which are not CopyConstructable.
*
* Use in the public section of the class.
* Make sure to include `<memory>` in the header when using this.
*/
#define RCLCPP_SMART_PTR_ALIASES_ONLY(...) \
__RCLCPP_SHARED_PTR_ALIAS(__VA_ARGS__) \
__RCLCPP_WEAK_PTR_ALIAS(__VA_ARGS__) \
__RCLCPP_MAKE_SHARED_DEFINITION(__VA_ARGS__)
#define __RCLCPP_SHARED_PTR_ALIAS(...) \
using SharedPtr = std::shared_ptr<__VA_ARGS__>; \
using ConstSharedPtr = std::shared_ptr<const __VA_ARGS__>;
#define __RCLCPP_MAKE_SHARED_DEFINITION(...) \
template<typename ... Args> \
@@ -62,7 +85,9 @@
__RCLCPP_SHARED_PTR_ALIAS(__VA_ARGS__) \
__RCLCPP_MAKE_SHARED_DEFINITION(__VA_ARGS__)
#define __RCLCPP_WEAK_PTR_ALIAS(...) using WeakPtr = std::weak_ptr<__VA_ARGS__>;
#define __RCLCPP_WEAK_PTR_ALIAS(...) \
using WeakPtr = std::weak_ptr<__VA_ARGS__>; \
using ConstWeakPtr = std::weak_ptr<const __VA_ARGS__>;
/// Defines aliases and static functions for using the Class with weak_ptrs.
#define RCLCPP_WEAK_PTR_DEFINITIONS(...) __RCLCPP_WEAK_PTR_ALIAS(__VA_ARGS__)
@@ -76,11 +101,15 @@
{ \
return std::unique_ptr<__VA_ARGS__>(new __VA_ARGS__(std::forward<Args>(args) ...)); \
}
/// Defines aliases and static functions for using the Class with unique_ptrs.
#define RCLCPP_UNIQUE_PTR_DEFINITIONS(...) \
__RCLCPP_UNIQUE_PTR_ALIAS(__VA_ARGS__) \
__RCLCPP_MAKE_UNIQUE_DEFINITION(__VA_ARGS__)
#define RCLCPP_STRING_JOIN(arg1, arg2) RCLCPP_DO_STRING_JOIN(arg1, arg2)
#define RCLCPP_DO_STRING_JOIN(arg1, arg2) arg1 ## arg2
#define RCLCPP_INFO(Args) std::cout << Args << std::endl;
#endif /* RCLCPP_RCLCPP_MACROS_HPP_ */
#endif // RCLCPP__MACROS_HPP_

View File

@@ -19,24 +19,28 @@
#include <cstddef>
#include <cstdint>
#include <memory>
#include <mutex>
#include <utility>
#include <vector>
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace mapped_ring_buffer
{
class MappedRingBufferBase
class RCLCPP_PUBLIC MappedRingBufferBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBufferBase);
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBufferBase)
};
/// Ring buffer container of unique_ptr's of T, which can be accessed by a key.
/* T must be a CopyConstructable and CopyAssignable.
/**
* T must be a CopyConstructable and CopyAssignable.
* This class can be used in a container by using the base class MappedRingBufferBase.
* This class must have a positive, non-zero size.
* This class cannot be resized nor can it reserve additional space after construction.
@@ -55,7 +59,7 @@ template<typename T, typename Alloc = std::allocator<void>>
class MappedRingBuffer : public MappedRingBufferBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBuffer<T, Alloc>);
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBuffer<T, Alloc>)
using ElemAllocTraits = allocator::AllocRebind<T, Alloc>;
using ElemAlloc = typename ElemAllocTraits::allocator_type;
using ElemDeleter = allocator::Deleter<ElemAlloc, T>;
@@ -63,9 +67,11 @@ public:
using ElemUniquePtr = std::unique_ptr<T, ElemDeleter>;
/// Constructor.
/* The constructor will allocate memory while reserving space.
/**
* The constructor will allocate memory while reserving space.
*
* \param size size of the ring buffer; must be positive and non-zero.
* \param allocator optional custom allocator
*/
explicit MappedRingBuffer(size_t size, std::shared_ptr<Alloc> allocator = nullptr)
: elements_(size), head_(0)
@@ -83,7 +89,8 @@ public:
virtual ~MappedRingBuffer() {}
/// Return a copy of the value stored in the ring buffer at the given key.
/* The key is matched if an element in the ring buffer has a matching key.
/**
* The key is matched if an element in the ring buffer has a matching key.
* This method will allocate in order to return a copy.
*
* The key is not guaranteed to be unique, see the class docs for more.
@@ -96,6 +103,7 @@ public:
void
get_copy_at_key(uint64_t key, ElemUniquePtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
auto it = get_iterator_of_key(key);
value = nullptr;
if (it != elements_.end() && it->in_use) {
@@ -106,7 +114,8 @@ public:
}
/// Return ownership of the value stored in the ring buffer, leaving a copy.
/* The key is matched if an element in the ring bufer has a matching key.
/**
* The key is matched if an element in the ring bufer has a matching key.
* This method will allocate in order to store a copy.
*
* The key is not guaranteed to be unique, see the class docs for more.
@@ -127,6 +136,7 @@ public:
void
get_ownership_at_key(uint64_t key, ElemUniquePtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
auto it = get_iterator_of_key(key);
value = nullptr;
if (it != elements_.end() && it->in_use) {
@@ -142,7 +152,8 @@ public:
}
/// Return ownership of the value stored in the ring buffer at the given key.
/* The key is matched if an element in the ring buffer has a matching key.
/**
* The key is matched if an element in the ring buffer has a matching key.
*
* The key is not guaranteed to be unique, see the class docs for more.
*
@@ -154,6 +165,7 @@ public:
void
pop_at_key(uint64_t key, ElemUniquePtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
auto it = get_iterator_of_key(key);
value = nullptr;
if (it != elements_.end() && it->in_use) {
@@ -163,7 +175,8 @@ public:
}
/// Insert a key-value pair, displacing an existing pair if necessary.
/* The key's uniqueness is not checked on insertion.
/**
* The key's uniqueness is not checked on insertion.
* It is up to the user to ensure the key is unique.
* This method should not allocate memory.
*
@@ -176,6 +189,7 @@ public:
bool
push_and_replace(uint64_t key, ElemUniquePtr & value)
{
std::lock_guard<std::mutex> lock(data_mutex_);
bool did_replace = elements_[head_].in_use;
elements_[head_].key = key;
elements_[head_].value.swap(value);
@@ -195,11 +209,12 @@ public:
bool
has_key(uint64_t key)
{
std::lock_guard<std::mutex> lock(data_mutex_);
return elements_.end() != get_iterator_of_key(key);
}
private:
RCLCPP_DISABLE_COPY(MappedRingBuffer<T, Alloc>);
RCLCPP_DISABLE_COPY(MappedRingBuffer<T, Alloc>)
struct element
{
@@ -213,7 +228,7 @@ private:
typename std::vector<element, VectorAlloc>::iterator
get_iterator_of_key(uint64_t key)
{
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
auto it = std::find_if(elements_.begin(), elements_.end(), [key](element & e) -> bool {
return e.key == key && e.in_use;
});
@@ -224,6 +239,7 @@ private:
std::vector<element, VectorAlloc> elements_;
size_t head_;
std::shared_ptr<ElemAlloc> allocator_;
std::mutex data_mutex_;
};
} // namespace mapped_ring_buffer

View File

@@ -15,20 +15,17 @@
#ifndef RCLCPP__MEMORY_STRATEGIES_HPP_
#define RCLCPP__MEMORY_STRATEGIES_HPP_
#include <rclcpp/memory_strategy.hpp>
#include <rclcpp/strategies/allocator_memory_strategy.hpp>
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace memory_strategies
{
using rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy;
static memory_strategy::MemoryStrategy::SharedPtr create_default_strategy()
{
return std::make_shared<AllocatorMemoryStrategy<>>();
}
RCLCPP_PUBLIC
memory_strategy::MemoryStrategy::SharedPtr
create_default_strategy();
} // namespace memory_strategies
} // namespace rclcpp

View File

@@ -18,9 +18,13 @@
#include <memory>
#include <vector>
#include "rcl/allocator.h"
#include "rcl/wait.h"
#include "rclcpp/any_executable.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
@@ -33,223 +37,77 @@ namespace memory_strategy
* the rmw implementation after the executor waits for work, based on the number of entities that
* come through.
*/
class MemoryStrategy
class RCLCPP_PUBLIC MemoryStrategy
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy);
using WeakNodeVector = std::vector<std::weak_ptr<node::Node>>;
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy)
using WeakNodeVector = std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>;
// return the new number of subscribers
virtual size_t fill_subscriber_handles(void ** & ptr) = 0;
// return the new number of services
virtual size_t fill_service_handles(void ** & ptr) = 0;
// return the new number of clients
virtual size_t fill_client_handles(void ** & ptr) = 0;
virtual void clear_active_entities() = 0;
virtual void clear_handles() = 0;
virtual void revalidate_handles() = 0;
virtual bool collect_entities(const WeakNodeVector & weak_nodes) = 0;
virtual size_t number_of_ready_subscriptions() const = 0;
virtual size_t number_of_ready_services() const = 0;
virtual size_t number_of_ready_clients() const = 0;
virtual size_t number_of_ready_timers() const = 0;
virtual size_t number_of_guard_conditions() const = 0;
virtual bool add_handles_to_waitset(rcl_wait_set_t * wait_set) = 0;
virtual void clear_handles() = 0;
virtual void remove_null_handles(rcl_wait_set_t * wait_set) = 0;
/// Provide a newly initialized AnyExecutable object.
// \return Shared pointer to the fresh executable.
virtual executor::AnyExecutable::SharedPtr instantiate_next_executable() = 0;
virtual rclcpp::executor::AnyExecutable::SharedPtr instantiate_next_executable() = 0;
virtual void add_guard_condition(const rcl_guard_condition_t * guard_condition) = 0;
virtual void remove_guard_condition(const rcl_guard_condition_t * guard_condition) = 0;
virtual void
get_next_subscription(executor::AnyExecutable::SharedPtr any_exec,
get_next_subscription(rclcpp::executor::AnyExecutable::SharedPtr any_exec,
const WeakNodeVector & weak_nodes) = 0;
virtual void
get_next_service(executor::AnyExecutable::SharedPtr any_exec,
get_next_service(rclcpp::executor::AnyExecutable::SharedPtr any_exec,
const WeakNodeVector & weak_nodes) = 0;
virtual void
get_next_client(executor::AnyExecutable::SharedPtr any_exec,
get_next_client(rclcpp::executor::AnyExecutable::SharedPtr any_exec,
const WeakNodeVector & weak_nodes) = 0;
virtual rcl_allocator_t
get_allocator() = 0;
static rclcpp::subscription::SubscriptionBase::SharedPtr
get_subscription_by_handle(void * subscriber_handle,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & weak_subscription : group->get_subscription_ptrs()) {
auto subscription = weak_subscription.lock();
if (subscription) {
if (subscription->get_subscription_handle()->data == subscriber_handle) {
return subscription;
}
if (subscription->get_intra_process_subscription_handle() &&
subscription->get_intra_process_subscription_handle()->data == subscriber_handle)
{
return subscription;
}
}
}
}
}
return nullptr;
}
get_subscription_by_handle(const rcl_subscription_t * subscriber_handle,
const WeakNodeVector & weak_nodes);
static rclcpp::service::ServiceBase::SharedPtr
get_service_by_handle(void * service_handle, const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & service : group->get_service_ptrs()) {
if (service->get_service_handle()->data == service_handle) {
return service;
}
}
}
}
return nullptr;
}
get_service_by_handle(const rcl_service_t * service_handle, const WeakNodeVector & weak_nodes);
static rclcpp::client::ClientBase::SharedPtr
get_client_by_handle(void * client_handle, const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & client : group->get_client_ptrs()) {
if (client->get_client_handle()->data == client_handle) {
return client;
}
}
}
}
return nullptr;
}
get_client_by_handle(const rcl_client_t * client_handle, const WeakNodeVector & weak_nodes);
static rclcpp::node::Node::SharedPtr
static rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group,
const WeakNodeVector & weak_nodes)
{
if (!group) {
return nullptr;
}
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto callback_group = weak_group.lock();
if (callback_group == group) {
return node;
}
}
}
return nullptr;
}
const WeakNodeVector & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & weak_sub : group->get_subscription_ptrs()) {
auto sub = weak_sub.lock();
if (sub == subscription) {
return group;
}
}
}
}
return nullptr;
}
const WeakNodeVector & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_service(
rclcpp::service::ServiceBase::SharedPtr service,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & serv : group->get_service_ptrs()) {
if (serv == service) {
return group;
}
}
}
}
return nullptr;
}
const WeakNodeVector & weak_nodes);
static rclcpp::callback_group::CallbackGroup::SharedPtr
get_group_by_client(rclcpp::client::ClientBase::SharedPtr client,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & cli : group->get_client_ptrs()) {
if (cli == client) {
return group;
}
}
}
}
return nullptr;
}
const WeakNodeVector & weak_nodes);
};
} // namespace memory_strategy
} // namespace rclcpp
#endif // RCLCPP__MEMORY_STRATEGY_HPP_

View File

@@ -16,9 +16,11 @@
#define RCLCPP__MESSAGE_MEMORY_STRATEGY_HPP_
#include <memory>
#include <stdexcept>
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
@@ -26,12 +28,12 @@ namespace message_memory_strategy
{
/// Default allocation strategy for messages received by subscriptions.
// A message memory strategy must be templated on the type of the subscription it belongs to.
/** A message memory strategy must be templated on the type of the subscription it belongs to. */
template<typename MessageT, typename Alloc = std::allocator<void>>
class MessageMemoryStrategy
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MessageMemoryStrategy);
RCLCPP_SMART_PTR_DEFINITIONS(MessageMemoryStrategy)
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
@@ -54,14 +56,14 @@ public:
}
/// By default, dynamically allocate a new message.
// \return Shared pointer to the new message.
/** \return Shared pointer to the new message. */
virtual std::shared_ptr<MessageT> borrow_message()
{
return std::allocate_shared<MessageT, MessageAlloc>(*message_allocator_.get());
}
/// Release ownership of the message, which will deallocate it if it has no more owners.
// \param[in] Shared pointer to the message we are returning.
/** \param[in] msg Shared pointer to the message we are returning. */
virtual void return_message(std::shared_ptr<MessageT> & msg)
{
msg.reset();

View File

@@ -15,84 +15,119 @@
#ifndef RCLCPP__NODE_HPP_
#define RCLCPP__NODE_HPP_
#include <atomic>
#include <condition_variable>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <tuple>
#include <vector>
#include "rcl/error_handling.h"
#include "rcl/node.h"
#include "rcl_interfaces/msg/list_parameters_result.hpp"
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rcl_interfaces/msg/parameter_event.hpp"
#include "rcl_interfaces/msg/set_parameters_result.hpp"
#include "rosidl_generator_cpp/message_type_support.hpp"
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/event.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/timer.hpp"
// Forward declaration of ROS middleware class
namespace rmw
{
struct rmw_node_t;
} // namespace rmw
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node
{
/// Node is the single point of entry for creating publishers and subscribers.
class Node
class Node : public std::enable_shared_from_this<Node>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(Node);
RCLCPP_SMART_PTR_DEFINITIONS(Node)
/// Create a new node with the specified name.
/**
* \param[in] node_name Name of the node.
* \param[in] namespace_ Namespace of the node.
* \param[in] use_intra_process_comms True to use the optimized intra-process communication
* pipeline to pass messages between nodes in the same process using shared memory.
*/
explicit Node(const std::string & node_name, bool use_intra_process_comms = false);
RCLCPP_PUBLIC
explicit Node(
const std::string & node_name,
const std::string & namespace_ = "",
bool use_intra_process_comms = false);
/// Create a node based on the node name and a rclcpp::context::Context.
/**
* \param[in] node_name Name of the node.
* \param[in] namespace_ Namespace of the node.
* \param[in] context The context for the node (usually represents the state of a process).
* \param[in] use_intra_process_comms True to use the optimized intra-process communication
* pipeline to pass messages between nodes in the same process using shared memory.
*/
RCLCPP_PUBLIC
Node(
const std::string & node_name, rclcpp::context::Context::SharedPtr context,
const std::string & node_name,
const std::string & namespace_,
rclcpp::context::Context::SharedPtr context,
bool use_intra_process_comms = false);
RCLCPP_PUBLIC
virtual ~Node();
/// Get the name of the node.
// \return The name of the node.
const std::string &
get_name() const {return name_; }
/** \return The name of the node. */
RCLCPP_PUBLIC
const char *
get_name() const;
/// Get the namespace of the node.
/** \return The namespace of the node. */
RCLCPP_PUBLIC
const char *
get_namespace() const;
/// Create and return a callback group.
RCLCPP_PUBLIC
rclcpp::callback_group::CallbackGroup::SharedPtr
create_callback_group(rclcpp::callback_group::CallbackGroupType group_type);
/// Return the list of callback groups in the node.
RCLCPP_PUBLIC
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
get_callback_groups() const;
/// Create and return a Publisher.
/**
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_history_depth The depth of the publisher message queue.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created publisher.
*/
template<typename MessageT, typename Alloc = std::allocator<void>>
typename rclcpp::publisher::Publisher<MessageT, Alloc>::SharedPtr
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename PublisherT = ::rclcpp::publisher::Publisher<MessageT, Alloc>>
std::shared_ptr<PublisherT>
create_publisher(
const std::string & topic_name, size_t qos_history_depth,
std::shared_ptr<Alloc> allocator = nullptr);
@@ -101,10 +136,13 @@ public:
/**
* \param[in] topic_name The topic for this publisher to publish on.
* \param[in] qos_profile The quality of service profile to pass on to the rmw implementation.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created publisher.
*/
template<typename MessageT, typename Alloc = std::allocator<void>>
typename rclcpp::publisher::Publisher<MessageT, Alloc>::SharedPtr
template<
typename MessageT, typename Alloc = std::allocator<void>,
typename PublisherT = ::rclcpp::publisher::Publisher<MessageT, Alloc>>
std::shared_ptr<PublisherT>
create_publisher(
const std::string & topic_name,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
@@ -118,17 +156,22 @@ public:
* \param[in] group The callback group for this subscription. NULL for no callback group.
* \param[in] ignore_local_publications True to ignore local publications.
* \param[in] msg_mem_strat The message memory strategy to use for allocating messages.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created subscription.
*/
/* TODO(jacquelinekay):
Windows build breaks when static member function passed as default
argument to msg_mem_strat, nullptr is a workaround.
*/
template<typename MessageT, typename CallbackT, typename Alloc = std::allocator<void>>
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
template<
typename MessageT,
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::subscription::Subscription<MessageT, Alloc>>
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
CallbackT callback,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
bool ignore_local_publications = false,
@@ -144,18 +187,23 @@ public:
* \param[in] group The callback group for this subscription. NULL for no callback group.
* \param[in] ignore_local_publications True to ignore local publications.
* \param[in] msg_mem_strat The message memory strategy to use for allocating messages.
* \param[in] allocator Optional custom allocator.
* \return Shared pointer to the created subscription.
*/
/* TODO(jacquelinekay):
Windows build breaks when static member function passed as default
argument to msg_mem_strat, nullptr is a workaround.
*/
template<typename MessageT, typename CallbackT, typename Alloc = std::allocator<void>>
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
template<
typename MessageT,
typename CallbackT,
typename Alloc = std::allocator<void>,
typename SubscriptionT = rclcpp::subscription::Subscription<MessageT, Alloc>>
std::shared_ptr<SubscriptionT>
create_subscription(
const std::string & topic_name,
size_t qos_history_depth,
CallbackT callback,
CallbackT && callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
bool ignore_local_publications = false,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
@@ -168,115 +216,198 @@ public:
* \param[in] callback User-defined callback function.
* \param[in] group Callback group to execute this timer's callback in.
*/
rclcpp::timer::WallTimer::SharedPtr
template<typename DurationT = std::milli, typename CallbackT>
typename rclcpp::timer::WallTimer<CallbackT>::SharedPtr
create_wall_timer(
std::chrono::nanoseconds period,
rclcpp::timer::CallbackType callback,
std::chrono::duration<int64_t, DurationT> period,
CallbackT callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
/// Create a timer with a sub-nanosecond precision update period.
/**
* \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.
*/
// TODO(wjwwood): reenable this once I figure out why the demo doesn't build with it.
// rclcpp::timer::WallTimer::SharedPtr
// create_wall_timer(
// std::chrono::duration<long double, std::nano> period,
// rclcpp::timer::CallbackType callback,
// rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
using CallbackGroup = rclcpp::callback_group::CallbackGroup;
using CallbackGroupWeakPtr = std::weak_ptr<CallbackGroup>;
using CallbackGroupWeakPtrList = std::list<CallbackGroupWeakPtr>;
/* Create and return a Client. */
template<typename ServiceT>
typename rclcpp::client::Client<ServiceT>::SharedPtr
create_client(
const std::string & service_name,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
/* Create and return a Service. */
template<typename ServiceT, typename FunctorT>
template<typename ServiceT, typename CallbackT>
typename rclcpp::service::Service<ServiceT>::SharedPtr
create_service(
const std::string & service_name,
FunctorT callback,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
std::vector<rcl_interfaces::msg::SetParametersResult> set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
rcl_interfaces::msg::SetParametersResult set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
RCLCPP_PUBLIC
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
std::vector<rclcpp::parameter::ParameterVariant> get_parameters(
const std::vector<std::string> & names) const;
template<typename ParameterT>
void
set_parameter_if_not_set(
const std::string & name,
const ParameterT & value);
std::vector<rcl_interfaces::msg::ParameterDescriptor> describe_parameters(
const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
std::vector<rclcpp::parameter::ParameterVariant>
get_parameters(const std::vector<std::string> & names) const;
std::vector<uint8_t> get_parameter_types(
const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
rclcpp::parameter::ParameterVariant
get_parameter(const std::string & name) const;
rcl_interfaces::msg::ListParametersResult list_parameters(
const std::vector<std::string> & prefixes, uint64_t depth) const;
RCLCPP_PUBLIC
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const;
std::map<std::string, std::string> get_topic_names_and_types() const;
/// Assign the value of the parameter if set into the parameter argument.
/**
* If the parameter was not set, then the "parameter" argument is never assigned a value.
*
* \param[in] name The name of the parameter to get.
* \param[out] parameter The output where the value of the parameter should be assigned.
* \returns true if the parameter was set, false otherwise
*/
template<typename ParameterT>
bool
get_parameter(const std::string & name, ParameterT & parameter) const;
size_t count_publishers(const std::string & topic_name) const;
/// Get the parameter value, or the "alternative value" if not set, and assign it to "value".
/**
* If the parameter was not set, then the "value" argument is assigned
* the "alternative_value".
* In all cases, the parameter remains not set after this function is called.
*
* \param[in] name The name of the parameter to get.
* \param[out] value The output where the value of the parameter should be assigned.
* \param[in] alternative_value Value to be stored in output if the parameter was not set.
* \returns true if the parameter was set, false otherwise
*/
template<typename ParameterT>
bool
get_parameter_or(
const std::string & name,
ParameterT & value,
const ParameterT & alternative_value) const;
size_t count_subscribers(const std::string & topic_name) const;
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const;
const CallbackGroupWeakPtrList & get_callback_groups() const;
RCLCPP_PUBLIC
std::vector<uint8_t>
get_parameter_types(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const;
/// Register the callback for parameter changes
/**
* \param[in] callback User defined callback function.
* It is expected to atomically set parameters.
* \note Repeated invocations of this function will overwrite previous callbacks
*/
template<typename CallbackT>
void
register_param_change_callback(CallbackT && callback);
RCLCPP_PUBLIC
std::map<std::string, std::vector<std::string>>
get_topic_names_and_types() const;
RCLCPP_PUBLIC
std::map<std::string, std::vector<std::string>>
get_service_names_and_types() const;
RCLCPP_PUBLIC
size_t
count_publishers(const std::string & topic_name) const;
RCLCPP_PUBLIC
size_t
count_subscribers(const std::string & topic_name) const;
/// Return a graph event, which will be set anytime a graph change occurs.
/* The graph Event object is a loan which must be returned.
* The Event object is scoped and therefore to return the load just let it go
* out of scope.
*/
RCLCPP_PUBLIC
rclcpp::event::Event::SharedPtr
get_graph_event();
/// Wait for a graph event to occur by waiting on an Event to become set.
/**
* The given Event must be acquire through the get_graph_event() method.
*
* \throws InvalidEventError if the given event is nullptr
* \throws EventNotRegisteredError if the given event was not acquired with
* get_graph_event().
*/
RCLCPP_PUBLIC
void
wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout);
/// Return the Node's internal NodeBaseInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_base_interface();
/// Return the Node's internal NodeGraphInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
get_node_graph_interface();
/// Return the Node's internal NodeTimersInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
get_node_timers_interface();
/// Return the Node's internal NodeTopicsInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr
get_node_topics_interface();
/// Return the Node's internal NodeServicesInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
get_node_services_interface();
/// Return the Node's internal NodeParametersInterface implementation.
RCLCPP_PUBLIC
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
get_node_parameters_interface();
private:
RCLCPP_DISABLE_COPY(Node);
static const rosidl_message_type_support_t * ipm_ts_;
RCLCPP_DISABLE_COPY(Node)
RCLCPP_PUBLIC
bool
group_in_node(callback_group::CallbackGroup::SharedPtr group);
std::string name_;
std::shared_ptr<rmw_node_t> node_handle_;
rclcpp::context::Context::SharedPtr context_;
CallbackGroup::SharedPtr default_callback_group_;
CallbackGroupWeakPtrList callback_groups_;
size_t number_of_subscriptions_;
size_t number_of_timers_;
size_t number_of_services_;
size_t number_of_clients_;
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr node_timers_;
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
bool use_intra_process_comms_;
mutable std::mutex mutex_;
std::map<std::string, rclcpp::parameter::ParameterVariant> parameters_;
publisher::Publisher<rcl_interfaces::msg::ParameterEvent>::SharedPtr events_publisher_;
};
const rosidl_message_type_support_t * Node::ipm_ts_ =
rosidl_generator_cpp::get_message_type_support_handle<rcl_interfaces::msg::IntraProcessMessage>();
} /* namespace node */
} /* namespace rclcpp */
#define RCLCPP_REGISTER_NODE(Class) RMW_EXPORT \
rclcpp::node::Node::SharedPtr \
create_node() \
{ \
return rclcpp::node::Node::SharedPtr(new Class( \
rclcpp::contexts::default_context::DefaultContext:: \
make_shared())); \
}
} // namespace node
} // namespace rclcpp
#ifndef RCLCPP__NODE_IMPL_HPP_
// Template implementations

View File

@@ -30,95 +30,30 @@
#include <utility>
#include <vector>
#include "rcl/publisher.h"
#include "rcl/subscription.h"
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rosidl_generator_cpp/message_type_support.hpp"
#include "rosidl_generator_cpp/service_type_support.hpp"
#include "rclcpp/contexts/default_context.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rclcpp/create_subscription.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
#ifndef RCLCPP__NODE_HPP_
#include "node.hpp"
#endif
using namespace rclcpp;
using namespace node;
Node::Node(const std::string & node_name, bool use_intra_process_comms)
: Node(
node_name,
rclcpp::contexts::default_context::get_global_default_context(),
use_intra_process_comms)
{}
Node::Node(
const std::string & node_name,
context::Context::SharedPtr context,
bool use_intra_process_comms)
: name_(node_name), context_(context),
number_of_subscriptions_(0), number_of_timers_(0), number_of_services_(0),
use_intra_process_comms_(use_intra_process_comms)
namespace rclcpp
{
size_t domain_id = 0;
char * ros_domain_id = nullptr;
const char * env_var = "ROS_DOMAIN_ID";
#ifndef _WIN32
ros_domain_id = getenv(env_var);
#else
size_t ros_domain_id_size;
_dupenv_s(&ros_domain_id, &ros_domain_id_size, env_var);
#endif
if (ros_domain_id) {
uint32_t number = strtoul(ros_domain_id, NULL, 0);
if (number == (std::numeric_limits<uint32_t>::max)()) {
throw std::runtime_error("failed to interpret ROS_DOMAIN_ID as integral number");
}
domain_id = static_cast<size_t>(number);
#ifdef _WIN32
free(ros_domain_id);
#endif
}
auto node = rmw_create_node(name_.c_str(), domain_id);
if (!node) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not create node: ") +
rmw_get_error_string_safe());
// *INDENT-ON*
}
// Initialize node handle shared_ptr with custom deleter.
// *INDENT-OFF*
node_handle_.reset(node, [](rmw_node_t * node) {
auto ret = rmw_destroy_node(node);
if (ret != RMW_RET_OK) {
fprintf(
stderr, "Error in destruction of rmw node handle: %s\n", rmw_get_error_string_safe());
}
});
// *INDENT-ON*
using rclcpp::callback_group::CallbackGroupType;
default_callback_group_ = create_callback_group(
CallbackGroupType::MutuallyExclusive);
events_publisher_ = create_publisher<rcl_interfaces::msg::ParameterEvent>(
"parameter_events", rmw_qos_profile_parameter_events);
}
rclcpp::callback_group::CallbackGroup::SharedPtr
Node::create_callback_group(
rclcpp::callback_group::CallbackGroupType group_type)
namespace node
{
using rclcpp::callback_group::CallbackGroup;
using rclcpp::callback_group::CallbackGroupType;
auto group = CallbackGroup::SharedPtr(new CallbackGroup(group_type));
callback_groups_.push_back(group);
return group;
}
template<typename MessageT, typename Alloc>
typename rclcpp::publisher::Publisher<MessageT, Alloc>::SharedPtr
template<typename MessageT, typename Alloc, typename PublisherT>
std::shared_ptr<PublisherT>
Node::create_publisher(
const std::string & topic_name, size_t qos_history_depth,
std::shared_ptr<Alloc> allocator)
@@ -128,11 +63,11 @@ Node::create_publisher(
}
rmw_qos_profile_t qos = rmw_qos_profile_default;
qos.depth = qos_history_depth;
return this->create_publisher<MessageT, Alloc>(topic_name, qos, allocator);
return this->create_publisher<MessageT, Alloc, PublisherT>(topic_name, qos, allocator);
}
template<typename MessageT, typename Alloc>
typename publisher::Publisher<MessageT, Alloc>::SharedPtr
template<typename MessageT, typename Alloc, typename PublisherT>
std::shared_ptr<PublisherT>
Node::create_publisher(
const std::string & topic_name, const rmw_qos_profile_t & qos_profile,
std::shared_ptr<Alloc> allocator)
@@ -140,199 +75,53 @@ Node::create_publisher(
if (!allocator) {
allocator = std::make_shared<Alloc>();
}
using rosidl_generator_cpp::get_message_type_support_handle;
auto type_support_handle = get_message_type_support_handle<MessageT>();
rmw_publisher_t * publisher_handle = rmw_create_publisher(
node_handle_.get(), type_support_handle, topic_name.c_str(), qos_profile);
if (!publisher_handle) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
std::string("could not create publisher: ") +
rmw_get_error_string_safe());
// *INDENT-ON*
}
auto publisher = publisher::Publisher<MessageT, Alloc>::make_shared(
node_handle_, publisher_handle, topic_name, qos_profile.depth, allocator);
if (use_intra_process_comms_) {
rmw_publisher_t * intra_process_publisher_handle = rmw_create_publisher(
node_handle_.get(), ipm_ts_, (topic_name + "__intra").c_str(), qos_profile);
if (!intra_process_publisher_handle) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
std::string("could not create intra process publisher: ") +
rmw_get_error_string_safe());
// *INDENT-ON*
}
auto intra_process_manager =
context_->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
uint64_t intra_process_publisher_id =
intra_process_manager->add_publisher<MessageT, Alloc>(publisher);
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = intra_process_manager;
// *INDENT-OFF*
auto shared_publish_callback =
[weak_ipm](uint64_t publisher_id, void * msg, const std::type_info & type_info) -> uint64_t
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or maybe return with a logged warning?
throw std::runtime_error(
"intra process publish called after destruction of intra process manager");
}
if (!msg) {
throw std::runtime_error("cannot publisher msg which is a null pointer");
}
auto & message_type_info = typeid(MessageT);
if (message_type_info != type_info) {
throw std::runtime_error(
std::string("published type '") + type_info.name() +
"' is incompatible from the publisher type '" + message_type_info.name() + "'");
}
MessageT * typed_message_ptr = static_cast<MessageT *>(msg);
using MessageDeleter = typename publisher::Publisher<MessageT, Alloc>::MessageDeleter;
std::unique_ptr<MessageT, MessageDeleter> unique_msg(typed_message_ptr);
uint64_t message_seq =
ipm->store_intra_process_message<MessageT, Alloc>(publisher_id, unique_msg);
return message_seq;
};
// *INDENT-ON*
publisher->setup_intra_process(
intra_process_publisher_id,
shared_publish_callback,
intra_process_publisher_handle);
}
return publisher;
return rclcpp::create_publisher<MessageT, Alloc, PublisherT>(
this->node_topics_.get(),
topic_name,
qos_profile,
use_intra_process_comms_,
allocator);
}
bool
Node::group_in_node(callback_group::CallbackGroup::SharedPtr group)
{
bool group_belongs_to_this_node = false;
for (auto & weak_group : this->callback_groups_) {
auto cur_group = weak_group.lock();
if (cur_group && (cur_group == group)) {
group_belongs_to_this_node = true;
}
}
return group_belongs_to_this_node;
}
template<typename MessageT, typename CallbackT, typename Alloc>
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
std::shared_ptr<SubscriptionT>
Node::create_subscription(
const std::string & topic_name,
CallbackT callback,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
msg_mem_strat,
typename std::shared_ptr<Alloc> allocator)
std::shared_ptr<Alloc> allocator)
{
if (!allocator) {
allocator = std::make_shared<Alloc>();
}
rclcpp::subscription::AnySubscriptionCallback<MessageT,
Alloc> any_subscription_callback(allocator);
any_subscription_callback.set(callback);
using rosidl_generator_cpp::get_message_type_support_handle;
if (!msg_mem_strat) {
msg_mem_strat =
rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::create_default();
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
msg_mem_strat = MessageMemoryStrategy<MessageT, Alloc>::create_default();
}
auto type_support_handle = get_message_type_support_handle<MessageT>();
rmw_subscription_t * subscriber_handle = rmw_create_subscription(
node_handle_.get(), type_support_handle,
topic_name.c_str(), qos_profile, ignore_local_publications);
if (!subscriber_handle) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
std::string("could not create subscription: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
using namespace rclcpp::subscription;
auto sub = Subscription<MessageT, Alloc>::make_shared(
node_handle_,
subscriber_handle,
return rclcpp::create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
this->node_topics_.get(),
topic_name,
std::forward<CallbackT>(callback),
qos_profile,
group,
ignore_local_publications,
any_subscription_callback,
msg_mem_strat);
auto sub_base_ptr = std::dynamic_pointer_cast<SubscriptionBase>(sub);
// Setup intra process.
if (use_intra_process_comms_) {
rmw_subscription_t * intra_process_subscriber_handle = rmw_create_subscription(
node_handle_.get(), ipm_ts_,
(topic_name + "__intra").c_str(), qos_profile, false);
if (!subscriber_handle) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
std::string("could not create intra process subscription: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
auto intra_process_manager =
context_->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = intra_process_manager;
uint64_t intra_process_subscription_id =
intra_process_manager->add_subscription(sub_base_ptr);
// *INDENT-OFF*
sub->setup_intra_process(
intra_process_subscription_id,
intra_process_subscriber_handle,
[weak_ipm](
uint64_t publisher_id,
uint64_t message_sequence,
uint64_t subscription_id,
typename Subscription<MessageT, Alloc>::MessageUniquePtr & message)
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or maybe return with a logged warning?
throw std::runtime_error(
"intra process take called after destruction of intra process manager");
}
ipm->take_intra_process_message<MessageT, Alloc>(
publisher_id, message_sequence, subscription_id, message);
},
[weak_ipm](const rmw_gid_t * sender_gid) -> bool {
auto ipm = weak_ipm.lock();
if (!ipm) {
throw std::runtime_error(
"intra process publisher check called after destruction of intra process manager");
}
return ipm->matches_any_publishers(sender_gid);
}
);
// *INDENT-ON*
}
// Assign to a group.
if (group) {
if (!group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create subscription, group not in node.");
}
group->add_subscription(sub_base_ptr);
} else {
default_callback_group_->add_subscription(sub_base_ptr);
}
number_of_subscriptions_++;
return sub;
use_intra_process_comms_,
msg_mem_strat,
allocator);
}
template<typename MessageT, typename CallbackT, typename Alloc>
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
std::shared_ptr<SubscriptionT>
Node::create_subscription(
const std::string & topic_name,
size_t qos_history_depth,
CallbackT callback,
CallbackT && callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group,
bool ignore_local_publications,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
@@ -341,9 +130,9 @@ Node::create_subscription(
{
rmw_qos_profile_t qos = rmw_qos_profile_default;
qos.depth = qos_history_depth;
return this->create_subscription<MessageT, CallbackT, Alloc>(
return this->create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
topic_name,
callback,
std::forward<CallbackT>(callback),
qos,
group,
ignore_local_publications,
@@ -351,326 +140,115 @@ Node::create_subscription(
allocator);
}
rclcpp::timer::WallTimer::SharedPtr
template<typename DurationT, typename CallbackT>
typename rclcpp::timer::WallTimer<CallbackT>::SharedPtr
Node::create_wall_timer(
std::chrono::nanoseconds period,
rclcpp::timer::CallbackType callback,
std::chrono::duration<int64_t, DurationT> period,
CallbackT callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
auto timer = rclcpp::timer::WallTimer::make_shared(period, callback);
if (group) {
if (!group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create timer, group not in node.");
}
group->add_timer(timer);
} else {
default_callback_group_->add_timer(timer);
}
number_of_timers_++;
auto timer = rclcpp::timer::WallTimer<CallbackT>::make_shared(
std::chrono::duration_cast<std::chrono::nanoseconds>(period),
std::move(callback));
node_timers_->add_timer(timer, group);
return timer;
}
// TODO(wjwwood): reenable this once I figure out why the demo doesn't build with it.
// rclcpp::timer::WallTimer::SharedPtr
// Node::create_wall_timer(
// std::chrono::duration<long double, std::nano> period,
// rclcpp::timer::CallbackType callback,
// rclcpp::callback_group::CallbackGroup::SharedPtr group)
// {
// return create_wall_timer(
// std::chrono::duration_cast<std::chrono::nanoseconds>(period),
// callback,
// group);
// }
template<typename ServiceT>
typename client::Client<ServiceT>::SharedPtr
Node::create_client(
const std::string & service_name,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
using rosidl_generator_cpp::get_service_type_support_handle;
auto service_type_support_handle =
get_service_type_support_handle<ServiceT>();
rcl_client_options_t options = rcl_client_get_default_options();
options.qos = qos_profile;
rmw_client_t * client_handle = rmw_create_client(
this->node_handle_.get(), service_type_support_handle, service_name.c_str());
if (!client_handle) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
std::string("could not create client: ") +
rmw_get_error_string_safe());
// *INDENT-ON*
}
using namespace rclcpp::client;
using rclcpp::client::Client;
using rclcpp::client::ClientBase;
auto cli = Client<ServiceT>::make_shared(
node_handle_,
client_handle,
service_name);
node_base_.get(),
node_graph_,
service_name,
options);
auto cli_base_ptr = std::dynamic_pointer_cast<ClientBase>(cli);
if (group) {
if (!group_in_node(group)) {
// TODO(esteve): use custom exception
throw std::runtime_error("Cannot create client, group not in node.");
}
group->add_client(cli_base_ptr);
} else {
default_callback_group_->add_client(cli_base_ptr);
}
number_of_clients_++;
node_services_->add_client(cli_base_ptr, group);
return cli;
}
template<typename ServiceT, typename FunctorT>
template<typename ServiceT, typename CallbackT>
typename rclcpp::service::Service<ServiceT>::SharedPtr
Node::create_service(
const std::string & service_name,
FunctorT callback,
CallbackT && callback,
const rmw_qos_profile_t & qos_profile,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
using rosidl_generator_cpp::get_service_type_support_handle;
auto service_type_support_handle =
get_service_type_support_handle<ServiceT>();
rclcpp::service::AnyServiceCallback<ServiceT> any_service_callback;
any_service_callback.set(callback);
any_service_callback.set(std::forward<CallbackT>(callback));
rmw_service_t * service_handle = rmw_create_service(
node_handle_.get(), service_type_support_handle, service_name.c_str());
if (!service_handle) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
std::string("could not create service: ") +
rmw_get_error_string_safe());
// *INDENT-ON*
}
rcl_service_options_t service_options = rcl_service_get_default_options();
service_options.qos = qos_profile;
auto serv = service::Service<ServiceT>::make_shared(
node_handle_, service_handle, service_name, any_service_callback);
node_base_->get_shared_rcl_node_handle(),
service_name, any_service_callback, service_options);
auto serv_base_ptr = std::dynamic_pointer_cast<service::ServiceBase>(serv);
if (group) {
if (!group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create service, group not in node.");
}
group->add_service(serv_base_ptr);
} else {
default_callback_group_->add_service(serv_base_ptr);
}
number_of_services_++;
node_services_->add_service(serv_base_ptr, group);
return serv;
}
std::vector<rcl_interfaces::msg::SetParametersResult>
Node::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
template<typename CallbackT>
void
Node::register_param_change_callback(CallbackT && callback)
{
std::vector<rcl_interfaces::msg::SetParametersResult> results;
for (auto p : parameters) {
auto result = set_parameters_atomically({{p}});
results.push_back(result);
}
return results;
this->node_parameters_->register_param_change_callback(std::forward<CallbackT>(callback));
}
rcl_interfaces::msg::SetParametersResult
Node::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
template<typename ParameterT>
void
Node::set_parameter_if_not_set(
const std::string & name,
const ParameterT & value)
{
std::lock_guard<std::mutex> lock(mutex_);
std::map<std::string, rclcpp::parameter::ParameterVariant> tmp_map;
auto parameter_event = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
for (auto p : parameters) {
if (parameters_.find(p.get_name()) == parameters_.end()) {
if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
parameter_event->new_parameters.push_back(p.to_parameter());
}
} else if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
parameter_event->changed_parameters.push_back(p.to_parameter());
} else {
parameter_event->deleted_parameters.push_back(p.to_parameter());
}
tmp_map[p.get_name()] = p;
rclcpp::parameter::ParameterVariant parameter_variant;
if (!this->get_parameter(name, parameter_variant)) {
this->set_parameters({
rclcpp::parameter::ParameterVariant(name, value),
});
}
// std::map::insert will not overwrite elements, so we'll keep the new
// ones and add only those that already exist in the Node's internal map
tmp_map.insert(parameters_.begin(), parameters_.end());
std::swap(tmp_map, parameters_);
}
// TODO(jacquelinekay): handle parameter constraints
rcl_interfaces::msg::SetParametersResult result;
result.successful = true;
events_publisher_->publish(parameter_event);
template<typename ParameterT>
bool
Node::get_parameter(const std::string & name, ParameterT & value) const
{
rclcpp::parameter::ParameterVariant parameter_variant;
bool result = get_parameter(name, parameter_variant);
if (result) {
value = parameter_variant.get_value<ParameterT>();
}
return result;
}
std::vector<rclcpp::parameter::ParameterVariant>
Node::get_parameters(
const std::vector<std::string> & names) const
template<typename ParameterT>
bool
Node::get_parameter_or(
const std::string & name,
ParameterT & value,
const ParameterT & alternative_value) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<rclcpp::parameter::ParameterVariant> results;
for (auto & name : names) {
if (std::any_of(parameters_.cbegin(), parameters_.cend(),
[&name](const std::pair<std::string, rclcpp::parameter::ParameterVariant> & kv) {
return name == kv.first;
}))
{
results.push_back(parameters_.at(name));
}
bool got_parameter = get_parameter(name, value);
if (!got_parameter) {
value = alternative_value;
}
return results;
return got_parameter;
}
std::vector<rcl_interfaces::msg::ParameterDescriptor>
Node::describe_parameters(
const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<rcl_interfaces::msg::ParameterDescriptor> results;
for (auto & kv : parameters_) {
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
return name == kv.first;
}))
{
rcl_interfaces::msg::ParameterDescriptor parameter_descriptor;
parameter_descriptor.name = kv.first;
parameter_descriptor.type = kv.second.get_type();
results.push_back(parameter_descriptor);
}
}
return results;
}
std::vector<uint8_t>
Node::get_parameter_types(
const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<uint8_t> results;
for (auto & kv : parameters_) {
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
return name == kv.first;
}))
{
results.push_back(kv.second.get_type());
} else {
results.push_back(rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET);
}
}
return results;
}
rcl_interfaces::msg::ListParametersResult
Node::list_parameters(
const std::vector<std::string> & prefixes, uint64_t depth) const
{
std::lock_guard<std::mutex> lock(mutex_);
rcl_interfaces::msg::ListParametersResult result;
// TODO(esteve): define parameter separator, use "." for now
for (auto & kv : parameters_) {
if (std::any_of(prefixes.cbegin(), prefixes.cend(), [&kv, &depth](const std::string & prefix) {
if (kv.first == prefix) {
return true;
} else if (kv.first.find(prefix + ".") == 0) {
size_t length = prefix.length();
std::string substr = kv.first.substr(length);
// Cast as unsigned integer to avoid warning
return static_cast<uint64_t>(std::count(substr.begin(), substr.end(), '.')) < depth;
}
return false;
}))
{
result.names.push_back(kv.first);
size_t last_separator = kv.first.find_last_of('.');
if (std::string::npos != last_separator) {
std::string prefix = kv.first.substr(0, last_separator);
if (std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
result.prefixes.cend())
{
result.prefixes.push_back(prefix);
}
}
}
}
return result;
}
std::map<std::string, std::string>
Node::get_topic_names_and_types() const
{
rmw_topic_names_and_types_t topic_names_and_types;
topic_names_and_types.topic_count = 0;
topic_names_and_types.topic_names = nullptr;
topic_names_and_types.type_names = nullptr;
auto ret = rmw_get_topic_names_and_types(node_handle_.get(), &topic_names_and_types);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not get topic names and types: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
std::map<std::string, std::string> topics;
for (size_t i = 0; i < topic_names_and_types.topic_count; ++i) {
topics[topic_names_and_types.topic_names[i]] = topic_names_and_types.type_names[i];
}
ret = rmw_destroy_topic_names_and_types(&topic_names_and_types);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not destroy topic names and types: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
return topics;
}
size_t
Node::count_publishers(const std::string & topic_name) const
{
size_t count;
auto ret = rmw_count_publishers(node_handle_.get(), topic_name.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count publishers: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
return count;
}
size_t
Node::count_subscribers(const std::string & topic_name) const
{
size_t count;
auto ret = rmw_count_subscribers(node_handle_.get(), topic_name.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count subscribers: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
return count;
}
const Node::CallbackGroupWeakPtrList &
Node::get_callback_groups() const
{
return callback_groups_;
}
} // namespace node
} // namespace rclcpp
#endif // RCLCPP__NODE_IMPL_HPP_

View File

@@ -0,0 +1,139 @@
// Copyright 2016 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_BASE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_BASE_HPP_
#include <memory>
#include <string>
#include <vector>
#include "rclcpp/context.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Implementation of the NodeBase part of the Node API.
class NodeBase : public NodeBaseInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
RCLCPP_PUBLIC
NodeBase(
const std::string & node_name,
const std::string & namespace_,
rclcpp::context::Context::SharedPtr context);
RCLCPP_PUBLIC
virtual
~NodeBase();
RCLCPP_PUBLIC
virtual
const char *
get_name() const;
RCLCPP_PUBLIC
virtual
const char *
get_namespace() const;
RCLCPP_PUBLIC
virtual
rclcpp::context::Context::SharedPtr
get_context();
RCLCPP_PUBLIC
virtual
rcl_node_t *
get_rcl_node_handle();
RCLCPP_PUBLIC
virtual
const rcl_node_t *
get_rcl_node_handle() const;
RCLCPP_PUBLIC
virtual
std::shared_ptr<rcl_node_t>
get_shared_rcl_node_handle();
RCLCPP_PUBLIC
virtual
std::shared_ptr<const rcl_node_t>
get_shared_rcl_node_handle() const;
RCLCPP_PUBLIC
virtual
rclcpp::callback_group::CallbackGroup::SharedPtr
create_callback_group(rclcpp::callback_group::CallbackGroupType group_type);
RCLCPP_PUBLIC
virtual
rclcpp::callback_group::CallbackGroup::SharedPtr
get_default_callback_group();
RCLCPP_PUBLIC
virtual
bool
callback_group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group);
RCLCPP_PUBLIC
virtual
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
get_callback_groups() const;
RCLCPP_PUBLIC
virtual
std::atomic_bool &
get_associated_with_executor_atomic();
RCLCPP_PUBLIC
virtual
rcl_guard_condition_t *
get_notify_guard_condition();
RCLCPP_PUBLIC
virtual
std::unique_lock<std::recursive_mutex>
acquire_notify_guard_condition_lock() const;
private:
RCLCPP_DISABLE_COPY(NodeBase)
rclcpp::context::Context::SharedPtr context_;
std::shared_ptr<rcl_node_t> node_handle_;
rclcpp::callback_group::CallbackGroup::SharedPtr default_callback_group_;
std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> callback_groups_;
std::atomic_bool associated_with_executor_;
/// Guard condition for notifying the Executor of changes to this node.
mutable std::recursive_mutex notify_guard_condition_mutex_;
rcl_guard_condition_t notify_guard_condition_ = rcl_get_zero_initialized_guard_condition();
bool notify_guard_condition_is_valid_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_BASE_HPP_

View File

@@ -0,0 +1,146 @@
// Copyright 2016 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_BASE_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_BASE_INTERFACE_HPP_
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "rcl/node.h"
#include "rclcpp/callback_group.hpp"
#include "rclcpp/context.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeBase part of the Node API.
class NodeBaseInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
/// Return the name of the node.
/** \return The name of the node. */
RCLCPP_PUBLIC
virtual
const char *
get_name() const = 0;
/// Return the namespace of the node.
/** \return The namespace of the node. */
RCLCPP_PUBLIC
virtual
const char *
get_namespace() const = 0;
/// Return the context of the node.
/** \return SharedPtr to the node's context. */
RCLCPP_PUBLIC
virtual
rclcpp::context::Context::SharedPtr
get_context() = 0;
/// Return the rcl_node_t node handle (non-const version).
RCLCPP_PUBLIC
virtual
rcl_node_t *
get_rcl_node_handle() = 0;
/// Return the rcl_node_t node handle (const version).
RCLCPP_PUBLIC
virtual
const rcl_node_t *
get_rcl_node_handle() const = 0;
/// Return the rcl_node_t node handle in a std::shared_ptr.
/**
* This handle remains valid after the Node is destroyed.
* The actual rcl node is not finalized until it is out of scope everywhere.
*/
RCLCPP_PUBLIC
virtual
std::shared_ptr<rcl_node_t>
get_shared_rcl_node_handle() = 0;
/// Return the rcl_node_t node handle in a std::shared_ptr.
/**
* This handle remains valid after the Node is destroyed.
* The actual rcl node is not finalized until it is out of scope everywhere.
*/
RCLCPP_PUBLIC
virtual
std::shared_ptr<const rcl_node_t>
get_shared_rcl_node_handle() const = 0;
/// Create and return a callback group.
RCLCPP_PUBLIC
virtual
rclcpp::callback_group::CallbackGroup::SharedPtr
create_callback_group(rclcpp::callback_group::CallbackGroupType group_type) = 0;
/// Return the default callback group.
RCLCPP_PUBLIC
virtual
rclcpp::callback_group::CallbackGroup::SharedPtr
get_default_callback_group() = 0;
/// Return true if the given callback group is associated with this node.
RCLCPP_PUBLIC
virtual
bool
callback_group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group) = 0;
/// Return list of callback groups associated with this node.
RCLCPP_PUBLIC
virtual
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
get_callback_groups() const = 0;
/// Return the atomic bool which is used to ensure only one executor is used.
RCLCPP_PUBLIC
virtual
std::atomic_bool &
get_associated_with_executor_atomic() = 0;
/// Return 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 rcl_guard_condition_t if it is valid, else nullptr
*/
RCLCPP_PUBLIC
virtual
rcl_guard_condition_t *
get_notify_guard_condition() = 0;
/// Acquire and return a scoped lock that protects the notify guard condition.
/** This should be used when triggering the notify guard condition. */
RCLCPP_PUBLIC
virtual
std::unique_lock<std::recursive_mutex>
acquire_notify_guard_condition_lock() const = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_BASE_INTERFACE_HPP_

View File

@@ -0,0 +1,139 @@
// Copyright 2016-2017 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_GRAPH_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_GRAPH_HPP_
#include <chrono>
#include <map>
#include <memory>
#include <mutex>
#include <string>
#include <vector>
#include "rcl/guard_condition.h"
#include "rclcpp/event.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace graph_listener
{
class GraphListener;
} // namespace graph_listener
namespace node_interfaces
{
/// Implementation the NodeGraph part of the Node API.
class NodeGraph : public NodeGraphInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeGraph)
RCLCPP_PUBLIC
explicit NodeGraph(rclcpp::node_interfaces::NodeBaseInterface * node_base);
RCLCPP_PUBLIC
virtual
~NodeGraph();
RCLCPP_PUBLIC
virtual
std::map<std::string, std::vector<std::string>>
get_topic_names_and_types(bool no_demangle = false) const;
RCLCPP_PUBLIC
virtual
std::map<std::string, std::vector<std::string>>
get_service_names_and_types() const;
RCLCPP_PUBLIC
virtual
std::vector<std::string>
get_node_names() const;
RCLCPP_PUBLIC
virtual
size_t
count_publishers(const std::string & topic_name) const;
RCLCPP_PUBLIC
virtual
size_t
count_subscribers(const std::string & topic_name) const;
RCLCPP_PUBLIC
virtual
const rcl_guard_condition_t *
get_graph_guard_condition() const;
RCLCPP_PUBLIC
virtual
void
notify_graph_change();
RCLCPP_PUBLIC
virtual
void
notify_shutdown();
RCLCPP_PUBLIC
virtual
rclcpp::event::Event::SharedPtr
get_graph_event();
RCLCPP_PUBLIC
virtual
void
wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout);
RCLCPP_PUBLIC
virtual
size_t
count_graph_users();
private:
RCLCPP_DISABLE_COPY(NodeGraph)
/// Handle to the NodeBaseInterface given in the constructor.
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
/// Graph Listener which waits on graph changes for the node and is shared across nodes.
std::shared_ptr<rclcpp::graph_listener::GraphListener> graph_listener_;
/// Whether or not this node needs to be added to the graph listener.
std::atomic_bool should_add_to_graph_listener_;
/// Mutex to guard the graph event related data structures.
mutable std::mutex graph_mutex_;
/// For notifying waiting threads (wait_for_graph_change()) on changes (notify_graph_change()).
std::condition_variable graph_cv_;
/// Weak references to graph events out on loan.
std::vector<rclcpp::event::Event::WeakPtr> graph_events_;
/// Number of graph events out on loan, used to determine if the graph should be monitored.
/** graph_users_count_ is atomic so that it can be accessed without acquiring the graph_mutex_ */
std::atomic_size_t graph_users_count_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_GRAPH_HPP_

View File

@@ -0,0 +1,147 @@
// Copyright 2016-2017 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_GRAPH_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_
#include <chrono>
#include <map>
#include <string>
#include <vector>
#include "rcl/guard_condition.h"
#include "rclcpp/event.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeGraph part of the Node API.
class NodeGraphInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeGraphInterface)
/// Return a map of existing topic names to list of topic types.
/**
* A topic is considered to exist when at least one publisher or subscriber
* exists for it, whether they be local or remote to this process.
*
* \param[in] no_demangle if true, topic names and types are not demangled
*/
RCLCPP_PUBLIC
virtual
std::map<std::string, std::vector<std::string>>
get_topic_names_and_types(bool no_demangle = false) const = 0;
/// Return a map of existing service names to list of service types.
/**
* A service is considered to exist when at least one service server or
* service client exists for it, whether they be local or remote to this
* process.
*/
RCLCPP_PUBLIC
virtual
std::map<std::string, std::vector<std::string>>
get_service_names_and_types() const = 0;
/// Return a vector of existing node names (string).
RCLCPP_PUBLIC
virtual
std::vector<std::string>
get_node_names() const = 0;
/// Return the number of publishers that are advertised on a given topic.
RCLCPP_PUBLIC
virtual
size_t
count_publishers(const std::string & topic_name) const = 0;
/// Return the number of subscribers who have created a subscription for a given topic.
RCLCPP_PUBLIC
virtual
size_t
count_subscribers(const std::string & topic_name) const = 0;
/// Return the rcl guard condition which is triggered when the ROS graph changes.
RCLCPP_PUBLIC
virtual
const rcl_guard_condition_t *
get_graph_guard_condition() const = 0;
/// Notify threads waiting on graph changes.
/**
* Affects threads waiting on the notify guard condition, see:
* get_notify_guard_condition(), as well as the threads waiting on graph
* changes using a graph Event, see: wait_for_graph_change().
*
* This is typically only used by the rclcpp::graph_listener::GraphListener.
*
* \throws RCLBaseError (a child of that exception) when an rcl error occurs
*/
RCLCPP_PUBLIC
virtual
void
notify_graph_change() = 0;
/// Notify any and all blocking node actions that shutdown has occurred.
RCLCPP_PUBLIC
virtual
void
notify_shutdown() = 0;
/// Return a graph event, which will be set anytime a graph change occurs.
/**
* The graph Event object is a loan which must be returned.
* The Event object is scoped and therefore to return the load just let it go
* out of scope.
*/
RCLCPP_PUBLIC
virtual
rclcpp::event::Event::SharedPtr
get_graph_event() = 0;
/// Wait for a graph event to occur by waiting on an Event to become set.
/**
* The given Event must be acquire through the get_graph_event() method.
*
* \throws InvalidEventError if the given event is nullptr
* \throws EventNotRegisteredError if the given event was not acquired with
* get_graph_event().
*/
RCLCPP_PUBLIC
virtual
void
wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout) = 0;
/// Return the number of on loan graph events, see get_graph_event().
/**
* This is typically only used by the rclcpp::graph_listener::GraphListener.
*/
RCLCPP_PUBLIC
virtual
size_t
count_graph_users() = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_GRAPH_INTERFACE_HPP_

View File

@@ -0,0 +1,122 @@
// Copyright 2016 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_PARAMETERS_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_HPP_
#include <map>
#include <string>
#include <vector>
#include "rcl_interfaces/msg/list_parameters_result.hpp"
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rcl_interfaces/msg/parameter_event.hpp"
#include "rcl_interfaces/msg/set_parameters_result.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Implementation of the NodeParameters part of the Node API.
class NodeParameters : public NodeParametersInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeParameters)
RCLCPP_PUBLIC
NodeParameters(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
bool use_intra_process);
RCLCPP_PUBLIC
virtual
~NodeParameters();
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::parameter::ParameterVariant>
get_parameters(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
virtual
rclcpp::parameter::ParameterVariant
get_parameter(const std::string & name) const;
RCLCPP_PUBLIC
virtual
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const;
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
virtual
std::vector<uint8_t>
get_parameter_types(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const;
RCLCPP_PUBLIC
virtual
void
register_param_change_callback(ParametersCallbackFunction callback);
private:
RCLCPP_DISABLE_COPY(NodeParameters)
rclcpp::node_interfaces::NodeTopicsInterface * node_topics_;
mutable std::mutex mutex_;
ParametersCallbackFunction parameters_callback_ = nullptr;
std::map<std::string, rclcpp::parameter::ParameterVariant> parameters_;
publisher::Publisher<rcl_interfaces::msg::ParameterEvent>::SharedPtr events_publisher_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_HPP_

View File

@@ -0,0 +1,97 @@
// Copyright 2016 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_PARAMETERS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_
#include <string>
#include <vector>
#include "rcl_interfaces/msg/list_parameters_result.hpp"
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rcl_interfaces/msg/set_parameters_result.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeParameters part of the Node API.
class NodeParametersInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeParametersInterface)
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters) = 0;
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters) = 0;
RCLCPP_PUBLIC
virtual
std::vector<rclcpp::parameter::ParameterVariant>
get_parameters(const std::vector<std::string> & names) const = 0;
RCLCPP_PUBLIC
virtual
rclcpp::parameter::ParameterVariant
get_parameter(const std::string & name) const = 0;
RCLCPP_PUBLIC
virtual
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const = 0;
RCLCPP_PUBLIC
virtual
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const = 0;
RCLCPP_PUBLIC
virtual
std::vector<uint8_t>
get_parameter_types(const std::vector<std::string> & names) const = 0;
RCLCPP_PUBLIC
virtual
rcl_interfaces::msg::ListParametersResult
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const = 0;
using ParametersCallbackFunction =
std::function<rcl_interfaces::msg::SetParametersResult(
const std::vector<rclcpp::parameter::ParameterVariant> &)>;
RCLCPP_PUBLIC
virtual
void
register_param_change_callback(ParametersCallbackFunction callback) = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_

View File

@@ -0,0 +1,67 @@
// Copyright 2016 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_SERVICES_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_SERVICES_HPP_
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_services_interface.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Implementation of the NodeServices part of the Node API.
class NodeServices : public NodeServicesInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeServices)
RCLCPP_PUBLIC
explicit NodeServices(rclcpp::node_interfaces::NodeBaseInterface * node_base);
RCLCPP_PUBLIC
virtual
~NodeServices();
RCLCPP_PUBLIC
virtual
void
add_client(
rclcpp::client::ClientBase::SharedPtr client_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group);
RCLCPP_PUBLIC
virtual
void
add_service(
rclcpp::service::ServiceBase::SharedPtr service_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group);
private:
RCLCPP_DISABLE_COPY(NodeServices)
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_SERVICES_HPP_

View File

@@ -0,0 +1,53 @@
// Copyright 2016 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_SERVICES_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeServices part of the Node API.
class NodeServicesInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeServicesInterface)
RCLCPP_PUBLIC
virtual
void
add_client(
rclcpp::client::ClientBase::SharedPtr client_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group) = 0;
RCLCPP_PUBLIC
virtual
void
add_service(
rclcpp::service::ServiceBase::SharedPtr service_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group) = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_SERVICES_INTERFACE_HPP_

View File

@@ -0,0 +1,60 @@
// Copyright 2016 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_TIMERS_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_TIMERS_HPP_
#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/timer.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Implementation of the NodeTimers part of the Node API.
class NodeTimers : public NodeTimersInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTimers)
RCLCPP_PUBLIC
explicit NodeTimers(rclcpp::node_interfaces::NodeBaseInterface * node_base);
RCLCPP_PUBLIC
virtual
~NodeTimers();
/// Add a timer to the node.
RCLCPP_PUBLIC
virtual
void
add_timer(
rclcpp::timer::TimerBase::SharedPtr timer,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group);
private:
RCLCPP_DISABLE_COPY(NodeTimers)
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_TIMERS_HPP_

View File

@@ -0,0 +1,46 @@
// Copyright 2016 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_TIMERS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_TIMERS_INTERFACE_HPP_
#include "rclcpp/callback_group.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/timer.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeTimers part of the Node API.
class NodeTimersInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTimersInterface)
/// Add a timer to the node.
RCLCPP_PUBLIC
virtual
void
add_timer(
rclcpp::timer::TimerBase::SharedPtr timer,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_TIMERS_INTERFACE_HPP_

View File

@@ -0,0 +1,88 @@
// Copyright 2016 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_TOPICS_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_TOPICS_HPP_
#include <string>
#include "rcl/publisher.h"
#include "rcl/subscription.h"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/publisher_factory.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Implementation of the NodeTopics part of the Node API.
class NodeTopics : public NodeTopicsInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTopicsInterface)
RCLCPP_PUBLIC
explicit NodeTopics(rclcpp::node_interfaces::NodeBaseInterface * node_base);
RCLCPP_PUBLIC
virtual
~NodeTopics();
RCLCPP_PUBLIC
virtual
rclcpp::publisher::PublisherBase::SharedPtr
create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
bool use_intra_process);
RCLCPP_PUBLIC
virtual
void
add_publisher(
rclcpp::publisher::PublisherBase::SharedPtr publisher);
RCLCPP_PUBLIC
virtual
rclcpp::subscription::SubscriptionBase::SharedPtr
create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
bool use_intra_process);
RCLCPP_PUBLIC
virtual
void
add_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group);
private:
RCLCPP_DISABLE_COPY(NodeTopics)
NodeBaseInterface * node_base_;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_TOPICS_HPP_

View File

@@ -0,0 +1,78 @@
// Copyright 2016 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_TOPICS_INTERFACE_HPP_
#define RCLCPP__NODE_INTERFACES__NODE_TOPICS_INTERFACE_HPP_
#include <functional>
#include <memory>
#include <string>
#include "rcl/publisher.h"
#include "rcl/subscription.h"
#include "rclcpp/macros.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/publisher_factory.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/subscription_factory.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node_interfaces
{
/// Pure virtual interface class for the NodeTopics part of the Node API.
class NodeTopicsInterface
{
public:
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTopicsInterface)
RCLCPP_PUBLIC
virtual
rclcpp::publisher::PublisherBase::SharedPtr
create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
bool use_intra_process) = 0;
RCLCPP_PUBLIC
virtual
void
add_publisher(
rclcpp::publisher::PublisherBase::SharedPtr publisher) = 0;
RCLCPP_PUBLIC
virtual
rclcpp::subscription::SubscriptionBase::SharedPtr
create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
bool use_intra_process) = 0;
RCLCPP_PUBLIC
virtual
void
add_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group) = 0;
};
} // namespace node_interfaces
} // namespace rclcpp
#endif // RCLCPP__NODE_INTERFACES__NODE_TOPICS_INTERFACE_HPP_

View File

@@ -12,253 +12,203 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_PARAMETER_HPP_
#define RCLCPP_RCLCPP_PARAMETER_HPP_
#ifndef RCLCPP__PARAMETER_HPP_
#define RCLCPP__PARAMETER_HPP_
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
#include <rmw/rmw.h>
#include <rcl_interfaces/msg/parameter.hpp>
#include <rcl_interfaces/msg/parameter_type.hpp>
#include <rcl_interfaces/msg/parameter_value.hpp>
#include "rcl_interfaces/msg/parameter.hpp"
#include "rcl_interfaces/msg/parameter_type.hpp"
#include "rcl_interfaces/msg/parameter_value.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/rmw.h"
namespace rclcpp
{
namespace parameter
{
enum ParameterType
{
PARAMETER_NOT_SET=rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET,
PARAMETER_BOOL=rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
PARAMETER_INTEGER=rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
PARAMETER_DOUBLE=rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
PARAMETER_STRING=rcl_interfaces::msg::ParameterType::PARAMETER_STRING,
PARAMETER_BYTES=rcl_interfaces::msg::ParameterType::PARAMETER_BYTES,
PARAMETER_NOT_SET = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET,
PARAMETER_BOOL = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
PARAMETER_INTEGER = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
PARAMETER_DOUBLE = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
PARAMETER_STRING = rcl_interfaces::msg::ParameterType::PARAMETER_STRING,
PARAMETER_BYTES = rcl_interfaces::msg::ParameterType::PARAMETER_BYTES,
};
// Structure to store an arbitrary parameter with templated get/set methods
class ParameterVariant
{
public:
ParameterVariant()
: name_("")
{
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET;
}
explicit ParameterVariant(const std::string & name, const bool bool_value)
: name_(name)
{
value_.bool_value = bool_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL;
}
explicit ParameterVariant(const std::string & name, const int int_value)
: name_(name)
{
value_.integer_value = int_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
}
explicit ParameterVariant(const std::string & name, const int64_t int_value)
: name_(name)
{
value_.integer_value = int_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
}
explicit ParameterVariant(const std::string & name, const float double_value)
: name_(name)
{
value_.double_value = double_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
}
explicit ParameterVariant(const std::string & name, const double double_value)
: name_(name)
{
value_.double_value = double_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
}
explicit ParameterVariant(const std::string & name, const std::string & string_value)
: name_(name)
{
value_.string_value = string_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_STRING;
}
explicit ParameterVariant(const std::string & name, const char * string_value)
: ParameterVariant(name, std::string(string_value)) {}
explicit ParameterVariant(const std::string & name, const std::vector<uint8_t> & bytes_value)
: name_(name)
{
value_.bytes_value = bytes_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BYTES;
}
RCLCPP_PUBLIC
ParameterVariant();
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const bool bool_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const int int_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const int64_t int_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const float double_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const double double_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const std::string & string_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const char * string_value);
RCLCPP_PUBLIC
explicit ParameterVariant(const std::string & name, const std::vector<uint8_t> & bytes_value);
inline ParameterType get_type() const {return static_cast<ParameterType>(value_.type); }
RCLCPP_PUBLIC
ParameterType
get_type() const;
inline std::string get_type_name() const
{
switch (get_type()) {
case rclcpp::parameter::ParameterType::PARAMETER_BOOL:
return "bool";
case rclcpp::parameter::ParameterType::PARAMETER_INTEGER:
return "integer";
case rclcpp::parameter::ParameterType::PARAMETER_DOUBLE:
return "double";
case rclcpp::parameter::ParameterType::PARAMETER_STRING:
return "string";
case rclcpp::parameter::ParameterType::PARAMETER_BYTES:
return "bytes";
case rclcpp::parameter::ParameterType::PARAMETER_NOT_SET:
return "not set";
default:
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
"Unexpected type from ParameterVariant: " + std::to_string(get_type()));
// *INDENT-ON*
}
}
RCLCPP_PUBLIC
std::string
get_type_name() const;
inline std::string get_name() const & {return name_; }
RCLCPP_PUBLIC
const std::string &
get_name() const;
inline rcl_interfaces::msg::ParameterValue get_parameter_value() const
{
return value_;
}
RCLCPP_PUBLIC
rcl_interfaces::msg::ParameterValue
get_parameter_value() const;
// The following get_value() variants require the use of ParameterType
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_INTEGER, int64_t>::type
get_value() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER) {
// TODO: use custom exception
// TODO(wjwwood): use custom exception
throw std::runtime_error("Invalid type");
}
return value_.integer_value;
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_DOUBLE, double>::type
get_value() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
// TODO: use custom exception
// TODO(wjwwood): use custom exception
throw std::runtime_error("Invalid type");
}
return value_.double_value;
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_STRING, const std::string &>::type
get_value() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_STRING) {
// TODO: use custom exception
// TODO(wjwwood): use custom exception
throw std::runtime_error("Invalid type");
}
return value_.string_value;
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_BOOL, bool>::type
get_value() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_BOOL) {
// TODO: use custom exception
// TODO(wjwwood): use custom exception
throw std::runtime_error("Invalid type");
}
return value_.bool_value;
}
template<ParameterType type>
typename std::enable_if<type == ParameterType::PARAMETER_BYTES,
const std::vector<uint8_t> &>::type
typename std::enable_if<
type == ParameterType::PARAMETER_BYTES, const std::vector<uint8_t> &>::type
get_value() const
{
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_BYTES) {
// TODO: use custom exception
// TODO(wjwwood): use custom exception
throw std::runtime_error("Invalid type");
}
return value_.bytes_value;
}
int64_t as_int() const {return get_value<ParameterType::PARAMETER_INTEGER>(); }
// The following get_value() variants allow the use of primitive types
double as_double() const {return get_value<ParameterType::PARAMETER_DOUBLE>(); }
template<typename type>
typename std::enable_if<
std::is_integral<type>::value && !std::is_same<type, bool>::value, int64_t>::type
get_value() const
{
return get_value<ParameterType::PARAMETER_INTEGER>();
}
const std::string & as_string() const {return get_value<ParameterType::PARAMETER_STRING>(); }
template<typename type>
typename std::enable_if<std::is_floating_point<type>::value, double>::type
get_value() const
{
return get_value<ParameterType::PARAMETER_DOUBLE>();
}
bool as_bool() const {return get_value<ParameterType::PARAMETER_BOOL>(); }
template<typename type>
typename std::enable_if<std::is_convertible<type, std::string>::value, const std::string &>::type
get_value() const
{
return get_value<ParameterType::PARAMETER_STRING>();
}
const std::vector<uint8_t> & as_bytes() const
template<typename type>
typename std::enable_if<std::is_same<type, bool>::value, bool>::type
get_value() const
{
return get_value<ParameterType::PARAMETER_BOOL>();
}
template<typename type>
typename std::enable_if<
std::is_convertible<
type, const std::vector<uint8_t> &>::value, const std::vector<uint8_t> &>::type
get_value() const
{
return get_value<ParameterType::PARAMETER_BYTES>();
}
static ParameterVariant from_parameter(const rcl_interfaces::msg::Parameter & parameter)
{
switch (parameter.value.type) {
case PARAMETER_BOOL:
return ParameterVariant(parameter.name, parameter.value.bool_value);
case PARAMETER_INTEGER:
return ParameterVariant(parameter.name, parameter.value.integer_value);
case PARAMETER_DOUBLE:
return ParameterVariant(parameter.name, parameter.value.double_value);
case PARAMETER_STRING:
return ParameterVariant(parameter.name, parameter.value.string_value);
case PARAMETER_BYTES:
return ParameterVariant(parameter.name, parameter.value.bytes_value);
case PARAMETER_NOT_SET:
throw std::runtime_error("Type from ParameterValue is not set");
default:
// TODO(wjwwood): use custom exception
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
"Unexpected type from ParameterVariant: " + std::to_string(parameter.value.type));
// *INDENT-ON*
}
}
RCLCPP_PUBLIC
int64_t
as_int() const;
rcl_interfaces::msg::Parameter to_parameter()
{
rcl_interfaces::msg::Parameter parameter;
parameter.name = name_;
parameter.value = value_;
return parameter;
}
RCLCPP_PUBLIC
double
as_double() const;
std::string value_to_string() const
{
switch (get_type()) {
case rclcpp::parameter::ParameterType::PARAMETER_BOOL:
return as_bool() ? "true" : "false";
case rclcpp::parameter::ParameterType::PARAMETER_INTEGER:
return std::to_string(as_int());
case rclcpp::parameter::ParameterType::PARAMETER_DOUBLE:
return std::to_string(as_double());
case rclcpp::parameter::ParameterType::PARAMETER_STRING:
return as_string();
case rclcpp::parameter::ParameterType::PARAMETER_BYTES:
{
std::stringstream bytes;
bool first_byte = true;
bytes << "[" << std::hex;
for (auto & byte : as_bytes()) {
bytes << "0x" << byte;
if (!first_byte) {
bytes << ", ";
} else {
first_byte = false;
}
}
return bytes.str();
}
case rclcpp::parameter::ParameterType::PARAMETER_NOT_SET:
return "not set";
default:
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
"Unexpected type from ParameterVariant: " + std::to_string(get_type()));
// *INDENT-ON*
}
}
RCLCPP_PUBLIC
const std::string &
as_string() const;
RCLCPP_PUBLIC
bool
as_bool() const;
RCLCPP_PUBLIC
const std::vector<uint8_t> &
as_bytes() const;
RCLCPP_PUBLIC
static ParameterVariant
from_parameter(const rcl_interfaces::msg::Parameter & parameter);
RCLCPP_PUBLIC
rcl_interfaces::msg::Parameter
to_parameter();
RCLCPP_PUBLIC
std::string
value_to_string() const;
private:
std::string name_;
@@ -266,72 +216,35 @@ private:
};
/* Return a json encoded version of the parameter intended for a dict. */
std::string _to_json_dict_entry(const ParameterVariant & param)
{
std::stringstream ss;
ss << "\"" << param.get_name() << "\": ";
ss << "{\"type\": \"" << param.get_type_name() << "\", ";
ss << "\"value\": \"" << param.value_to_string() << "\"}";
return ss.str();
}
/// Return a json encoded version of the parameter intended for a dict.
RCLCPP_PUBLIC
std::string
_to_json_dict_entry(const ParameterVariant & param);
RCLCPP_PUBLIC
std::ostream &
operator<<(std::ostream & os, const rclcpp::parameter::ParameterVariant & pv);
} /* namespace parameter */
RCLCPP_PUBLIC
std::ostream &
operator<<(std::ostream & os, const std::vector<ParameterVariant> & parameters);
} /* namespace rclcpp */
} // namespace parameter
} // namespace rclcpp
namespace std
{
/* Return a json encoded version of the parameter intended for a list. */
inline std::string to_string(const rclcpp::parameter::ParameterVariant & param)
{
std::stringstream ss;
ss << "{\"name\": \"" << param.get_name() << "\", ";
ss << "\"type\": \"" << param.get_type_name() << "\", ";
ss << "\"value\": \"" << param.value_to_string() << "\"}";
return ss.str();
}
/* Return a json encoded version of a vector of parameters, as a string*/
inline std::string to_string(const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
std::stringstream ss;
ss << "{";
bool first = true;
for (const auto & pv : parameters) {
if (first == false) {
ss << ", ";
} else {
first = false;
}
ss << rclcpp::parameter::_to_json_dict_entry(pv);
}
ss << "}";
return ss.str();
}
/// Return a json encoded version of the parameter intended for a list.
RCLCPP_PUBLIC
std::string
to_string(const rclcpp::parameter::ParameterVariant & param);
} /* namespace std */
/// Return a json encoded version of a vector of parameters, as a string.
RCLCPP_PUBLIC
std::string
to_string(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
namespace rclcpp
{
namespace parameter
{
} // namespace std
std::ostream & operator<<(std::ostream & os, const rclcpp::parameter::ParameterVariant & pv)
{
os << std::to_string(pv);
return os;
}
std::ostream & operator<<(std::ostream & os, const std::vector<ParameterVariant> & parameters)
{
os << std::to_string(parameters);
return os;
}
} /* namespace parameter */
} /* namespace rclcpp */
#endif /* RCLCPP_RCLCPP_PARAMETER_HPP_ */
#endif // RCLCPP__PARAMETER_HPP_

View File

@@ -12,227 +12,93 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_PARAMETER_CLIENT_HPP_
#define RCLCPP_RCLCPP_PARAMETER_CLIENT_HPP_
#ifndef RCLCPP__PARAMETER_CLIENT_HPP_
#define RCLCPP__PARAMETER_CLIENT_HPP_
#include <string>
#include <utility>
#include <vector>
#include <rmw/rmw.h>
#include <rclcpp/executors.hpp>
#include <rclcpp/macros.hpp>
#include <rclcpp/node.hpp>
#include <rclcpp/parameter.hpp>
#include <rcl_interfaces/msg/parameter.hpp>
#include <rcl_interfaces/msg/parameter_event.hpp>
#include <rcl_interfaces/msg/parameter_value.hpp>
#include <rcl_interfaces/srv/describe_parameters.hpp>
#include <rcl_interfaces/srv/get_parameters.hpp>
#include <rcl_interfaces/srv/get_parameter_types.hpp>
#include <rcl_interfaces/srv/list_parameters.hpp>
#include <rcl_interfaces/srv/set_parameters.hpp>
#include <rcl_interfaces/srv/set_parameters_atomically.hpp>
#include "rcl_interfaces/msg/parameter.hpp"
#include "rcl_interfaces/msg/parameter_event.hpp"
#include "rcl_interfaces/msg/parameter_value.hpp"
#include "rcl_interfaces/srv/describe_parameters.hpp"
#include "rcl_interfaces/srv/get_parameter_types.hpp"
#include "rcl_interfaces/srv/get_parameters.hpp"
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "rcl_interfaces/srv/set_parameters.hpp"
#include "rcl_interfaces/srv/set_parameters_atomically.hpp"
#include "rclcpp/executors.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/rmw.h"
namespace rclcpp
{
namespace parameter_client
{
class AsyncParametersClient
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(AsyncParametersClient);
RCLCPP_SMART_PTR_DEFINITIONS(AsyncParametersClient)
AsyncParametersClient(const rclcpp::node::Node::SharedPtr node,
const std::string & remote_node_name = "")
: node_(node)
{
if (remote_node_name != "") {
remote_node_name_ = remote_node_name;
} else {
remote_node_name_ = node_->get_name();
}
get_parameters_client_ = node_->create_client<rcl_interfaces::srv::GetParameters>(
remote_node_name_ + "__get_parameters");
get_parameter_types_client_ = node_->create_client<rcl_interfaces::srv::GetParameterTypes>(
remote_node_name_ + "__get_parameter_types");
set_parameters_client_ = node_->create_client<rcl_interfaces::srv::SetParameters>(
remote_node_name_ + "__set_parameters");
list_parameters_client_ = node_->create_client<rcl_interfaces::srv::ListParameters>(
remote_node_name_ + "__list_parameters");
describe_parameters_client_ = node_->create_client<rcl_interfaces::srv::DescribeParameters>(
remote_node_name_ + "__describe_parameters");
}
RCLCPP_PUBLIC
AsyncParametersClient(
const rclcpp::node::Node::SharedPtr node,
const std::string & remote_node_name = "",
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
RCLCPP_PUBLIC
std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>
get_parameters(
const std::vector<std::string> & names,
std::function<void(
std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>)> callback = nullptr)
{
auto promise_result =
std::make_shared<std::promise<std::vector<rclcpp::parameter::ParameterVariant>>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::GetParameters::Request>();
request->names = names;
get_parameters_client_->async_send_request(
request,
[request, promise_result, future_result, &callback](
rclcpp::client::Client<rcl_interfaces::srv::GetParameters>::SharedFuture cb_f) {
std::vector<rclcpp::parameter::ParameterVariant> parameter_variants;
auto & pvalues = cb_f.get()->values;
for (auto & pvalue : pvalues) {
auto i = &pvalue - &pvalues[0];
rcl_interfaces::msg::Parameter parameter;
parameter.name = request->names[i];
parameter.value = pvalue;
parameter_variants.push_back(rclcpp::parameter::ParameterVariant::from_parameter(
parameter));
}
promise_result->set_value(parameter_variants);
if (callback != nullptr) {
callback(future_result);
}
}
);
return future_result;
}
std::function<
void(std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>)
> callback = nullptr);
RCLCPP_PUBLIC
std::shared_future<std::vector<rclcpp::parameter::ParameterType>>
get_parameter_types(
const std::vector<std::string> & names,
std::function<void(
std::shared_future<std::vector<rclcpp::parameter::ParameterType>>)> callback = nullptr)
{
auto promise_result =
std::make_shared<std::promise<std::vector<rclcpp::parameter::ParameterType>>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::GetParameterTypes::Request>();
request->names = names;
get_parameter_types_client_->async_send_request(
request,
[promise_result, future_result, &callback](
rclcpp::client::Client<rcl_interfaces::srv::GetParameterTypes>::SharedFuture cb_f) {
std::vector<rclcpp::parameter::ParameterType> types;
auto & pts = cb_f.get()->types;
for (auto & pt : pts) {
pts.push_back(static_cast<rclcpp::parameter::ParameterType>(pt));
}
promise_result->set_value(types);
if (callback != nullptr) {
callback(future_result);
}
}
);
return future_result;
}
std::function<
void(std::shared_future<std::vector<rclcpp::parameter::ParameterType>>)
> callback = nullptr);
RCLCPP_PUBLIC
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
std::function<void(std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>)> callback =
nullptr)
{
auto promise_result =
std::make_shared<std::promise<std::vector<rcl_interfaces::msg::SetParametersResult>>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::SetParameters::Request>();
std::transform(parameters.begin(), parameters.end(), std::back_inserter(
request->parameters), [](
rclcpp::parameter::ParameterVariant p) {return p.to_parameter(); });
set_parameters_client_->async_send_request(
request,
[promise_result, future_result, &callback](
rclcpp::client::Client<rcl_interfaces::srv::SetParameters>::SharedFuture cb_f) {
promise_result->set_value(cb_f.get()->results);
if (callback != nullptr) {
callback(future_result);
}
}
);
return future_result;
}
std::function<
void(std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>)
> callback = nullptr);
RCLCPP_PUBLIC
std::shared_future<rcl_interfaces::msg::SetParametersResult>
set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
std::function<void(std::shared_future<rcl_interfaces::msg::SetParametersResult>)> callback =
nullptr)
{
auto promise_result =
std::make_shared<std::promise<rcl_interfaces::msg::SetParametersResult>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::SetParametersAtomically::Request>();
std::transform(parameters.begin(), parameters.end(), std::back_inserter(
request->parameters), [](
rclcpp::parameter::ParameterVariant p) {return p.to_parameter(); });
set_parameters_atomically_client_->async_send_request(
request,
[promise_result, future_result, &callback](
rclcpp::client::Client<rcl_interfaces::srv::SetParametersAtomically>::SharedFuture cb_f) {
promise_result->set_value(cb_f.get()->result);
if (callback != nullptr) {
callback(future_result);
}
}
);
return future_result;
}
std::function<
void(std::shared_future<rcl_interfaces::msg::SetParametersResult>)
> callback = nullptr);
RCLCPP_PUBLIC
std::shared_future<rcl_interfaces::msg::ListParametersResult>
list_parameters(
const std::vector<std::string> & prefixes,
uint64_t depth,
std::function<void(std::shared_future<rcl_interfaces::msg::ListParametersResult>)> callback =
nullptr)
{
auto promise_result =
std::make_shared<std::promise<rcl_interfaces::msg::ListParametersResult>>();
auto future_result = promise_result->get_future().share();
std::function<
void(std::shared_future<rcl_interfaces::msg::ListParametersResult>)
> callback = nullptr);
auto request = std::make_shared<rcl_interfaces::srv::ListParameters::Request>();
request->prefixes = prefixes;
request->depth = depth;
list_parameters_client_->async_send_request(
request,
[promise_result, future_result, &callback](
rclcpp::client::Client<rcl_interfaces::srv::ListParameters>::SharedFuture cb_f) {
promise_result->set_value(cb_f.get()->result);
if (callback != nullptr) {
callback(future_result);
}
}
);
return future_result;
}
template<typename FunctorT>
template<typename CallbackT>
typename rclcpp::subscription::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
on_parameter_event(FunctorT callback)
on_parameter_event(CallbackT && callback)
{
return node_->create_subscription<rcl_interfaces::msg::ParameterEvent>(
"parameter_events", callback, rmw_qos_profile_parameter_events);
"parameter_events", std::forward<CallbackT>(callback), rmw_qos_profile_parameter_events);
}
private:
@@ -252,98 +118,85 @@ private:
class SyncParametersClient
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient);
RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient)
SyncParametersClient(
rclcpp::node::Node::SharedPtr node)
: node_(node)
{
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
async_parameters_client_ = std::make_shared<AsyncParametersClient>(node);
}
RCLCPP_PUBLIC
explicit SyncParametersClient(
rclcpp::node::Node::SharedPtr node,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
RCLCPP_PUBLIC
SyncParametersClient(
rclcpp::executor::Executor::SharedPtr executor,
rclcpp::node::Node::SharedPtr node)
: executor_(executor), node_(node)
{
async_parameters_client_ = std::make_shared<AsyncParametersClient>(node);
}
rclcpp::node::Node::SharedPtr node,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
RCLCPP_PUBLIC
std::vector<rclcpp::parameter::ParameterVariant>
get_parameters(const std::vector<std::string> & parameter_names)
get_parameters(const std::vector<std::string> & parameter_names);
RCLCPP_PUBLIC
bool
has_parameter(const std::string & parameter_name);
template<typename T>
T
get_parameter_impl(
const std::string & parameter_name, std::function<T()> parameter_not_found_handler)
{
auto f = async_parameters_client_->get_parameters(parameter_names);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
rclcpp::executors::FutureReturnCode::SUCCESS)
{
return f.get();
std::vector<std::string> names;
names.push_back(parameter_name);
auto vars = get_parameters(names);
if ((vars.size() != 1) || (vars[0].get_type() == rclcpp::parameter::PARAMETER_NOT_SET)) {
return parameter_not_found_handler();
} else {
return static_cast<T>(vars[0].get_value<T>());
}
// Return an empty vector if unsuccessful
return std::vector<rclcpp::parameter::ParameterVariant>();
}
template<typename T>
T
get_parameter(const std::string & parameter_name, const T & default_value)
{
// *INDENT-OFF*
return get_parameter_impl(parameter_name,
std::function<T()>([&default_value]() -> T {return default_value; }));
// *INDENT-ON*
}
template<typename T>
T
get_parameter(const std::string & parameter_name)
{
// *INDENT-OFF*
return get_parameter_impl(parameter_name,
std::function<T()>([]() -> T {throw std::runtime_error("Parameter not set"); }));
// *INDENT-ON*
}
RCLCPP_PUBLIC
std::vector<rclcpp::parameter::ParameterType>
get_parameter_types(const std::vector<std::string> & parameter_names)
{
auto f = async_parameters_client_->get_parameter_types(parameter_names);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
rclcpp::executors::FutureReturnCode::SUCCESS)
{
return f.get();
}
return std::vector<rclcpp::parameter::ParameterType>();
}
get_parameter_types(const std::vector<std::string> & parameter_names);
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::SetParametersResult>
set_parameters(const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
auto f = async_parameters_client_->set_parameters(parameters);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
rclcpp::executors::FutureReturnCode::SUCCESS)
{
return f.get();
}
return std::vector<rcl_interfaces::msg::SetParametersResult>();
}
set_parameters(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
RCLCPP_PUBLIC
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
auto f = async_parameters_client_->set_parameters_atomically(parameters);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
rclcpp::executors::FutureReturnCode::SUCCESS)
{
return f.get();
}
throw std::runtime_error("Unable to get result of set parameters service call.");
}
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
RCLCPP_PUBLIC
rcl_interfaces::msg::ListParametersResult
list_parameters(
const std::vector<std::string> & parameter_prefixes,
uint64_t depth)
{
auto f = async_parameters_client_->list_parameters(parameter_prefixes, depth);
uint64_t depth);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
rclcpp::executors::FutureReturnCode::SUCCESS)
{
return f.get();
}
throw std::runtime_error("Unable to get result of list parameters service call.");
}
template<typename FunctorT>
template<typename CallbackT>
typename rclcpp::subscription::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
on_parameter_event(FunctorT callback)
on_parameter_event(CallbackT && callback)
{
return async_parameters_client_->on_parameter_event(callback);
return async_parameters_client_->on_parameter_event(std::forward<CallbackT>(callback));
}
private:
@@ -352,8 +205,7 @@ private:
AsyncParametersClient::SharedPtr async_parameters_client_;
};
} /* namespace parameter_client */
} // namespace parameter_client
} // namespace rclcpp
} /* namespace rclcpp */
#endif /* RCLCPP_RCLCPP_PARAMETER_CLIENT_HPP_ */
#endif // RCLCPP__PARAMETER_CLIENT_HPP_

View File

@@ -12,150 +12,38 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_PARAMETER_SERVICE_HPP_
#define RCLCPP_RCLCPP_PARAMETER_SERVICE_HPP_
#ifndef RCLCPP__PARAMETER_SERVICE_HPP_
#define RCLCPP__PARAMETER_SERVICE_HPP_
#include <string>
#include <rmw/rmw.h>
#include <rclcpp/executors.hpp>
#include <rclcpp/macros.hpp>
#include <rclcpp/node.hpp>
#include <rclcpp/parameter.hpp>
#include <rcl_interfaces/srv/describe_parameters.hpp>
#include <rcl_interfaces/srv/get_parameters.hpp>
#include <rcl_interfaces/srv/get_parameter_types.hpp>
#include <rcl_interfaces/srv/list_parameters.hpp>
#include <rcl_interfaces/srv/set_parameters.hpp>
#include <rcl_interfaces/srv/set_parameters_atomically.hpp>
#include "rcl_interfaces/srv/describe_parameters.hpp"
#include "rcl_interfaces/srv/get_parameter_types.hpp"
#include "rcl_interfaces/srv/get_parameters.hpp"
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "rcl_interfaces/srv/set_parameters.hpp"
#include "rcl_interfaces/srv/set_parameters_atomically.hpp"
#include "rclcpp/executors.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/rmw.h"
namespace rclcpp
{
namespace parameter_service
{
class ParameterService
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(ParameterService);
RCLCPP_SMART_PTR_DEFINITIONS(ParameterService)
ParameterService(const rclcpp::node::Node::SharedPtr node)
: node_(node)
{
std::weak_ptr<rclcpp::node::Node> captured_node = node_;
get_parameters_service_ = node_->create_service<rcl_interfaces::srv::GetParameters>(
node_->get_name() + "__get_parameters", [captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::GetParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::GetParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto values = node->get_parameters(request->names);
for (auto & pvariant : values) {
response->values.push_back(pvariant.get_parameter_value());
}
}
);
get_parameter_types_service_ = node_->create_service<rcl_interfaces::srv::GetParameterTypes>(
node_->get_name() + "__get_parameter_types", [captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Request> request,
std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto types = node->get_parameter_types(request->names);
std::transform(types.cbegin(), types.cend(),
std::back_inserter(response->types), [](const uint8_t & type) {
return static_cast<rclcpp::parameter::ParameterType>(type);
});
}
);
set_parameters_service_ = node_->create_service<rcl_interfaces::srv::SetParameters>(
node_->get_name() + "__set_parameters", [captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::SetParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::SetParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
std::vector<rclcpp::parameter::ParameterVariant> pvariants;
for (auto & p : request->parameters) {
pvariants.push_back(rclcpp::parameter::ParameterVariant::from_parameter(p));
}
auto results = node->set_parameters(pvariants);
response->results = results;
}
);
set_parameters_atomically_service_ =
node_->create_service<rcl_interfaces::srv::SetParametersAtomically>(
node_->get_name() + "__set_parameters_atomically", [captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Request> request,
std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
std::vector<rclcpp::parameter::ParameterVariant> pvariants;
std::transform(request->parameters.cbegin(), request->parameters.cend(),
std::back_inserter(pvariants),
[](const rcl_interfaces::msg::Parameter & p) {
return rclcpp::parameter::ParameterVariant::
from_parameter(p);
});
auto result = node->set_parameters_atomically(pvariants);
response->result = result;
}
);
describe_parameters_service_ = node_->create_service<rcl_interfaces::srv::DescribeParameters>(
node_->get_name() + "__describe_parameters", [captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto descriptors = node->describe_parameters(request->names);
response->descriptors = descriptors;
}
);
list_parameters_service_ = node_->create_service<rcl_interfaces::srv::ListParameters>(
node_->get_name() + "__list_parameters", [captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::ListParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::ListParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto result = node->list_parameters(request->prefixes, request->depth);
response->result = result;
}
);
}
RCLCPP_PUBLIC
explicit ParameterService(
const rclcpp::node::Node::SharedPtr node,
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
private:
const rclcpp::node::Node::SharedPtr node_;
@@ -170,8 +58,7 @@ private:
rclcpp::service::Service<rcl_interfaces::srv::ListParameters>::SharedPtr list_parameters_service_;
};
} /* namespace parameter_service */
} // namespace parameter_service
} // namespace rclcpp
} /* namespace rclcpp */
#endif /* RCLCPP_RCLCPP_PARAMETER_SERVICE_HPP_ */
#endif // RCLCPP__PARAMETER_SERVICE_HPP_

View File

@@ -18,114 +18,85 @@
#include <rmw/error_handling.h>
#include <rmw/rmw.h>
#include <functional>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rmw/impl/cpp/demangle.hpp"
#include "rcl/error_handling.h"
#include "rcl/publisher.h"
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
// Forward declaration for friend statement
namespace node
// Forward declaration is used for friend statement.
namespace node_interfaces
{
class Node;
} // namespace node
class NodeTopicsInterface;
}
namespace publisher
{
class PublisherBase
{
friend rclcpp::node_interfaces::NodeTopicsInterface;
public:
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase);
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
/// Default constructor.
/**
* Typically, a publisher is not created through this method, but instead is created through a
* call to `Node::create_publisher`.
* \param[in] node_handle The corresponding rmw representation of the owner node.
* \param[in] publisher_handle The rmw publisher handle corresponding to this publisher.
* \param[in] node_base A pointer to the NodeBaseInterface for the parent node.
* \param[in] topic The topic that this publisher publishes on.
* \param[in] queue_size The maximum number of unpublished messages to queue.
* \param[in] type_support The type support structure for the type to be published.
* \param[in] publisher_options QoS settings for this publisher.
*/
RCLCPP_PUBLIC
PublisherBase(
std::shared_ptr<rmw_node_t> node_handle,
rmw_publisher_t * publisher_handle,
std::string topic,
size_t queue_size)
: node_handle_(node_handle), publisher_handle_(publisher_handle),
intra_process_publisher_handle_(nullptr),
topic_(topic), queue_size_(queue_size),
intra_process_publisher_id_(0), store_intra_process_message_(nullptr)
{
// Life time of this object is tied to the publisher handle.
if (rmw_get_gid_for_publisher(publisher_handle_, &rmw_gid_) != RMW_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
std::string("failed to get publisher gid: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
}
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);
/// Default destructor.
virtual ~PublisherBase()
{
if (intra_process_publisher_handle_) {
if (rmw_destroy_publisher(node_handle_.get(), intra_process_publisher_handle_)) {
fprintf(
stderr,
"Error in destruction of intra process rmw publisher handle: %s\n",
rmw_get_error_string_safe());
}
}
if (publisher_handle_) {
if (rmw_destroy_publisher(node_handle_.get(), publisher_handle_) != RMW_RET_OK) {
fprintf(
stderr,
"Error in destruction of rmw publisher handle: %s\n",
rmw_get_error_string_safe());
}
}
}
RCLCPP_PUBLIC
virtual ~PublisherBase();
/// Get the topic that this publisher publishes on.
// \return The topic name.
const std::string &
get_topic_name() const
{
return topic_;
}
/** \return The topic name. */
RCLCPP_PUBLIC
const char *
get_topic_name() const;
/// Get the queue size for this publisher.
// \return The queue size.
/** \return The queue size. */
RCLCPP_PUBLIC
size_t
get_queue_size() const
{
return queue_size_;
}
get_queue_size() const;
/// Get the global identifier for this publisher (used in rmw and by DDS).
// \return The gid.
/** \return The gid. */
RCLCPP_PUBLIC
const rmw_gid_t &
get_gid() const
{
return rmw_gid_;
}
get_gid() const;
/// Get the global identifier for this publisher used by intra-process communication.
// \return The intra-process gid.
/** \return The intra-process gid. */
RCLCPP_PUBLIC
const rmw_gid_t &
get_intra_process_gid() const
{
return intra_process_rmw_gid_;
}
get_intra_process_gid() const;
/// Compare this publisher to a gid.
/**
@@ -133,11 +104,9 @@ public:
* \param[in] gid Reference to a gid.
* \return True if the publisher's gid matches the input.
*/
RCLCPP_PUBLIC
bool
operator==(const rmw_gid_t & gid) const
{
return *this == &gid;
}
operator==(const rmw_gid_t & gid) const;
/// Compare this publisher to a pointer gid.
/**
@@ -145,98 +114,69 @@ public:
* \param[in] gid A pointer to a gid.
* \return True if this publisher's gid matches the input.
*/
RCLCPP_PUBLIC
bool
operator==(const rmw_gid_t * gid) const
{
bool result = false;
auto ret = rmw_compare_gids_equal(gid, &this->get_gid(), &result);
if (ret != RMW_RET_OK) {
throw std::runtime_error(
std::string("failed to compare gids: ") + rmw_get_error_string_safe());
}
if (!result) {
ret = rmw_compare_gids_equal(gid, &this->get_intra_process_gid(), &result);
if (ret != RMW_RET_OK) {
throw std::runtime_error(
std::string("failed to compare gids: ") + rmw_get_error_string_safe());
}
}
return result;
}
operator==(const rmw_gid_t * gid) const;
typedef std::function<uint64_t(uint64_t, void *, const std::type_info &)> StoreMessageCallbackT;
using StoreMessageCallbackT = std::function<uint64_t(uint64_t, void *, const std::type_info &)>;
protected:
/// Implementation utility function used to setup intra process publishing after creation.
RCLCPP_PUBLIC
void
setup_intra_process(
uint64_t intra_process_publisher_id,
StoreMessageCallbackT callback,
rmw_publisher_t * intra_process_publisher_handle)
{
intra_process_publisher_id_ = intra_process_publisher_id;
store_intra_process_message_ = callback;
intra_process_publisher_handle_ = intra_process_publisher_handle;
// Life time of this object is tied to the publisher handle.
auto ret = rmw_get_gid_for_publisher(intra_process_publisher_handle_, &intra_process_rmw_gid_);
if (ret != RMW_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
std::string("failed to create intra process publisher gid: ") +
rmw_get_error_string_safe());
// *INDENT-ON*
}
}
const rcl_publisher_options_t & intra_process_options);
std::shared_ptr<rmw_node_t> node_handle_;
protected:
std::shared_ptr<rcl_node_t> rcl_node_handle_;
rmw_publisher_t * publisher_handle_;
rmw_publisher_t * intra_process_publisher_handle_;
std::string topic_;
size_t queue_size_;
rcl_publisher_t publisher_handle_ = rcl_get_zero_initialized_publisher();
rcl_publisher_t intra_process_publisher_handle_ = rcl_get_zero_initialized_publisher();
uint64_t intra_process_publisher_id_;
StoreMessageCallbackT store_intra_process_message_;
rmw_gid_t rmw_gid_;
rmw_gid_t intra_process_rmw_gid_;
std::mutex intra_process_publish_mutex_;
};
/// A publisher publishes messages of any type to a topic.
template<typename MessageT, typename Alloc = std::allocator<void>>
class Publisher : public PublisherBase
{
friend rclcpp::node::Node;
public:
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
using MessageAlloc = typename MessageAllocTraits::allocator_type;
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, Alloc>);
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, Alloc>)
Publisher(
std::shared_ptr<rmw_node_t> node_handle,
rmw_publisher_t * publisher_handle,
std::string topic,
size_t queue_size,
std::shared_ptr<Alloc> allocator)
: PublisherBase(node_handle, publisher_handle, topic, queue_size)
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rcl_publisher_options_t & publisher_options,
const std::shared_ptr<MessageAlloc> & allocator)
: PublisherBase(
node_base,
topic,
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
publisher_options),
message_allocator_(allocator)
{
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
}
virtual ~Publisher()
{}
/// Send a message to the topic for this publisher.
/**
* This function is templated on the input message type, MessageT.
* \param[in] msg A shared pointer to the message to send.
*/
void
virtual void
publish(std::unique_ptr<MessageT, MessageDeleter> & msg)
{
this->do_inter_process_publish(msg.get());
@@ -250,20 +190,16 @@ public:
// See: http://stackoverflow.com/questions/11002641/dynamic-casting-for-unique-ptr
MessageT * msg_ptr = msg.get();
msg.release();
uint64_t message_seq;
{
std::lock_guard<std::mutex> lock(intra_process_publish_mutex_);
message_seq =
store_intra_process_message_(intra_process_publisher_id_, msg_ptr, typeid(MessageT));
}
uint64_t message_seq =
store_intra_process_message_(intra_process_publisher_id_, msg_ptr, typeid(MessageT));
rcl_interfaces::msg::IntraProcessMessage ipm;
ipm.publisher_id = intra_process_publisher_id_;
ipm.message_sequence = message_seq;
auto status = rmw_publish(intra_process_publisher_handle_, &ipm);
if (status != RMW_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
auto status = rcl_publish(&intra_process_publisher_handle_, &ipm);
if (status != RCL_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to publish intra process message: ") + rmw_get_error_string_safe());
std::string("failed to publish intra process message: ") + rcl_get_error_string_safe());
// *INDENT-ON*
}
} else {
@@ -272,7 +208,7 @@ public:
}
}
void
virtual void
publish(const std::shared_ptr<MessageT> & msg)
{
// Avoid allocating when not using intra process.
@@ -291,7 +227,7 @@ public:
return this->publish(unique_msg);
}
void
virtual void
publish(std::shared_ptr<const MessageT> msg)
{
// Avoid allocating when not using intra process.
@@ -310,7 +246,7 @@ public:
return this->publish(unique_msg);
}
void
virtual void
publish(const MessageT & msg)
{
// Avoid allocating when not using intra process.
@@ -325,6 +261,15 @@ public:
return this->publish(unique_msg);
}
virtual void
publish(const MessageT * msg)
{
if (!msg) {
throw std::runtime_error("msg argument is nullptr");
}
return this->publish(*msg);
}
std::shared_ptr<MessageAlloc> get_allocator() const
{
return message_allocator_;
@@ -334,11 +279,11 @@ protected:
void
do_inter_process_publish(const MessageT * msg)
{
auto status = rmw_publish(publisher_handle_, msg);
if (status != RMW_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
auto status = rcl_publish(&publisher_handle_, msg);
if (status != RCL_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to publish message: ") + rmw_get_error_string_safe());
std::string("failed to publish message: ") + rcl_get_error_string_safe());
// *INDENT-ON*
}
}
@@ -349,6 +294,7 @@ protected:
};
} // namespace publisher
} // namespace rclcpp
#endif // RCLCPP__PUBLISHER_HPP_

View File

@@ -0,0 +1,148 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__PUBLISHER_FACTORY_HPP_
#define RCLCPP__PUBLISHER_FACTORY_HPP_
#include <functional>
#include <memory>
#include <string>
#include "rcl/publisher.h"
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rclcpp/publisher.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// Factory with functions used to create a MessageT specific PublisherT.
/**
* This factory class is used to encapsulate the template generated functions
* which are used during the creation of a Message type specific publisher
* within a non-templated class.
*
* It is created using the create_publisher_factory function, which is usually
* called from a templated "create_publisher" method on the Node class, and
* is passed to the non-templated "create_publisher" method on the NodeTopics
* class where it is used to create and setup the Publisher.
*/
struct PublisherFactory
{
// Creates a PublisherT<MessageT, ...> publisher object and returns it as a PublisherBase.
using PublisherFactoryFunction = std::function<
rclcpp::publisher::PublisherBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_publisher_options_t & publisher_options)>;
PublisherFactoryFunction create_typed_publisher;
// Adds the PublisherBase to the intraprocess manager with the correctly
// templated call to IntraProcessManager::store_intra_process_message.
using AddPublisherToIntraProcessManagerFunction = std::function<
uint64_t(
rclcpp::intra_process_manager::IntraProcessManager * ipm,
rclcpp::publisher::PublisherBase::SharedPtr publisher)>;
AddPublisherToIntraProcessManagerFunction add_publisher_to_intra_process_manager;
// Creates the callback function which is called on each
// PublisherT::publish() and which handles the intra process transmission of
// the message being published.
using SharedPublishCallbackFactoryFunction = std::function<
rclcpp::publisher::PublisherBase::StoreMessageCallbackT(
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm)>;
SharedPublishCallbackFactoryFunction create_shared_publish_callback;
};
/// Return a PublisherFactory with functions setup for creating a PublisherT<MessageT, Alloc>.
template<typename MessageT, typename Alloc, typename PublisherT>
PublisherFactory
create_publisher_factory(std::shared_ptr<Alloc> allocator)
{
PublisherFactory factory;
// factory function that creates a MessageT specific PublisherT
factory.create_typed_publisher =
[allocator](
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_publisher_options_t & publisher_options) -> std::shared_ptr<PublisherT>
{
auto message_alloc = std::make_shared<typename PublisherT::MessageAlloc>(*allocator.get());
publisher_options.allocator = allocator::get_rcl_allocator<MessageT>(*message_alloc.get());
return std::make_shared<PublisherT>(node_base, topic_name, publisher_options, message_alloc);
};
// function to add a publisher to the intra process manager
factory.add_publisher_to_intra_process_manager =
[](
rclcpp::intra_process_manager::IntraProcessManager * ipm,
rclcpp::publisher::PublisherBase::SharedPtr publisher) -> uint64_t
{
return ipm->add_publisher<MessageT, Alloc>(std::dynamic_pointer_cast<PublisherT>(publisher));
};
// function to create a shared publish callback std::function
using StoreMessageCallbackT = rclcpp::publisher::PublisherBase::StoreMessageCallbackT;
factory.create_shared_publish_callback =
[](rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm) -> StoreMessageCallbackT
{
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = ipm;
// this function is called on each call to publish() and handles storing
// of the published message in the intra process manager
auto shared_publish_callback =
[weak_ipm](uint64_t publisher_id, void * msg, const std::type_info & type_info) -> uint64_t
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or maybe return with a warning?
throw std::runtime_error(
"intra process publish called after destruction of intra process manager");
}
if (!msg) {
throw std::runtime_error("cannot publisher msg which is a null pointer");
}
auto & message_type_info = typeid(MessageT);
if (message_type_info != type_info) {
throw std::runtime_error(
std::string("published type '") + type_info.name() +
"' is incompatible from the publisher type '" + message_type_info.name() + "'");
}
MessageT * typed_message_ptr = static_cast<MessageT *>(msg);
using MessageDeleter = typename publisher::Publisher<MessageT, Alloc>::MessageDeleter;
std::unique_ptr<MessageT, MessageDeleter> unique_msg(typed_message_ptr);
uint64_t message_seq =
ipm->store_intra_process_message<MessageT, Alloc>(publisher_id, unique_msg);
return message_seq;
};
return shared_publish_callback;
};
// return the factory now that it is populated
return factory;
}
} // namespace rclcpp
#endif // RCLCPP__PUBLISHER_FACTORY_HPP_

View File

@@ -12,15 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_RATE_HPP_
#define RCLCPP_RCLCPP_RATE_HPP_
#ifndef RCLCPP__RATE_HPP_
#define RCLCPP__RATE_HPP_
#include <chrono>
#include <memory>
#include <thread>
#include <rclcpp/macros.hpp>
#include <rclcpp/utilities.hpp>
#include "rclcpp/macros.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
@@ -30,10 +31,10 @@ namespace rate
class RateBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(RateBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(RateBase)
virtual bool sleep() = 0;
virtual bool is_steady() = 0;
virtual bool is_steady() const = 0;
virtual void reset() = 0;
};
@@ -45,13 +46,13 @@ template<class Clock = std::chrono::high_resolution_clock>
class GenericRate : public RateBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate);
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate)
GenericRate(double rate)
explicit GenericRate(double rate)
: GenericRate<Clock>(
duration_cast<nanoseconds>(duration<double>(1.0 / rate)))
{}
GenericRate(std::chrono::nanoseconds period)
explicit GenericRate(std::chrono::nanoseconds period)
: period_(period), last_interval_(Clock::now())
{}
@@ -88,7 +89,7 @@ public:
}
virtual bool
is_steady()
is_steady() const
{
return Clock::is_steady;
}
@@ -105,17 +106,17 @@ public:
}
private:
RCLCPP_DISABLE_COPY(GenericRate);
RCLCPP_DISABLE_COPY(GenericRate)
std::chrono::nanoseconds period_;
std::chrono::time_point<Clock> last_interval_;
using ClockDurationNano = std::chrono::duration<typename Clock::rep, std::nano>;
std::chrono::time_point<Clock, ClockDurationNano> last_interval_;
};
using Rate = GenericRate<std::chrono::system_clock>;
using WallRate = GenericRate<std::chrono::steady_clock>;
} // namespace rate
} // namespace rclcpp
} // namespace rate
} // namespace rclcpp
#endif /* RCLCPP_RCLCPP_RATE_HPP_ */
#endif // RCLCPP__RATE_HPP_

View File

@@ -12,58 +12,131 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_RCLCPP_HPP_
#define RCLCPP_RCLCPP_RCLCPP_HPP_
/** \mainpage rclcpp: ROS Client Library for C++
*
* `rclcpp` provides the canonical C++ API for interacting with ROS.
* It consists of these main components:
*
* - Nodes
* - rclcpp::node::Node
* - rclcpp/node.hpp
* - Publisher
* - rclcpp::node::Node::create_publisher()
* - rclcpp::publisher::Publisher
* - rclcpp::publisher::Publisher::publish()
* - rclcpp/publisher.hpp
* - Subscription
* - rclcpp::node::Node::create_subscription()
* - rclcpp::subscription::Subscription
* - rclcpp/subscription.hpp
* - Service Client
* - rclcpp::node::Node::create_client()
* - rclcpp::client::Client
* - rclcpp/client.hpp
* - Service Server
* - rclcpp::node::Node::create_service()
* - rclcpp::service::Service
* - rclcpp/service.hpp
* - Timer
* - rclcpp::node::Node::create_wall_timer()
* - rclcpp::timer::WallTimer
* - rclcpp::timer::TimerBase
* - rclcpp/timer.hpp
* - Parameters:
* - rclcpp::node::Node::set_parameters()
* - rclcpp::node::Node::get_parameters()
* - rclcpp::node::Node::get_parameter()
* - rclcpp::node::Node::describe_parameters()
* - rclcpp::node::Node::list_parameters()
* - rclcpp::node::Node::register_param_change_callback()
* - rclcpp::parameter::ParameterVariant
* - rclcpp::parameter_client::AsyncParametersClient
* - rclcpp::parameter_client::SyncParametersClient
* - rclcpp/parameter.hpp
* - rclcpp/parameter_client.hpp
* - rclcpp/parameter_service.hpp
* - Rate:
* - rclcpp::rate::Rate
* - rclcpp::rate::WallRate
* - rclcpp/rate.hpp
*
* There are also some components which help control the execution of callbacks:
*
* - Executors (responsible for execution of callbacks through a blocking spin):
* - rclcpp::spin()
* - rclcpp::spin_some()
* - rclcpp::spin_until_future_complete()
* - rclcpp::executors::single_threaded_executor::SingleThreadedExecutor
* - rclcpp::executors::single_threaded_executor::SingleThreadedExecutor::add_node()
* - rclcpp::executors::single_threaded_executor::SingleThreadedExecutor::spin()
* - rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor
* - rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor::add_node()
* - rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor::spin()
* - rclcpp/executor.hpp
* - rclcpp/executors.hpp
* - rclcpp/executors/single_threaded_executor.hpp
* - rclcpp/executors/multi_threaded_executor.hpp
* - CallbackGroups (mechanism for enforcing concurrency rules for callbacks):
* - rclcpp::node::Node::create_callback_group()
* - rclcpp::callback_group::CallbackGroup
* - rclcpp/callback_group.hpp
*
* Additionally, there are some methods for introspecting the ROS graph:
*
* - Graph Events (a waitable event object that wakes up when the graph changes):
* - rclcpp::node::Node::get_graph_event()
* - rclcpp::node::Node::wait_for_graph_change()
* - rclcpp::event::Event
* - List topic names and types:
* - rclcpp::node::Node::get_topic_names_and_types()
* - Get the number of publishers or subscribers on a topic:
* - rclcpp::node::Node::count_publishers()
* - rclcpp::node::Node::count_subscribers()
*
* Finally, there are many internal API's and utilities:
*
* - Exceptions:
* - rclcpp/exceptions.hpp
* - Allocator related items:
* - rclcpp/allocator/allocator_common.hpp
* - rclcpp/allocator/allocator_deleter.hpp
* - Memory management tools:
* - rclcpp/memory_strategies.hpp
* - rclcpp/memory_strategy.hpp
* - rclcpp/message_memory_strategy.hpp
* - rclcpp/strategies/allocator_memory_strategy.hpp
* - rclcpp/strategies/message_pool_memory_strategy.hpp
* - Context object which is shared amongst multiple Nodes:
* - rclcpp::context::Context
* - rclcpp/context.hpp
* - rclcpp/contexts/default_context.hpp
* - Various utilities:
* - rclcpp/function_traits.hpp
* - rclcpp/macros.hpp
* - rclcpp/scope_exit.hpp
* - rclcpp/time.hpp
* - rclcpp/utilities.hpp
* - rclcpp/visibility_control.hpp
*/
#ifndef RCLCPP__RCLCPP_HPP_
#define RCLCPP__RCLCPP_HPP_
#include <csignal>
#include <memory>
#include <rclcpp/node.hpp>
#include <rclcpp/parameter.hpp>
#include <rclcpp/parameter_client.hpp>
#include <rclcpp/parameter_service.hpp>
#include <rclcpp/executors.hpp>
#include <rclcpp/rate.hpp>
#include <rclcpp/utilities.hpp>
#include "rclcpp/executors.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/parameter.hpp"
#include "rclcpp/parameter_client.hpp"
#include "rclcpp/parameter_service.hpp"
#include "rclcpp/rate.hpp"
#include "rclcpp/time.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
// NOLINTNEXTLINE(runtime/int)
const std::chrono::seconds operator"" _s(unsigned long long s)
{
return std::chrono::seconds(s);
}
const std::chrono::nanoseconds operator"" _s(long double s)
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::duration<long double>(s));
}
const std::chrono::nanoseconds
// NOLINTNEXTLINE(runtime/int)
operator"" _ms(unsigned long long ms)
{
return std::chrono::milliseconds(ms);
}
const std::chrono::nanoseconds
operator"" _ms(long double ms)
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::duration<long double, std::milli>(ms));
}
const std::chrono::nanoseconds
// NOLINTNEXTLINE(runtime/int)
operator"" _ns(unsigned long long ns)
{
return std::chrono::nanoseconds(ns);
}
const std::chrono::nanoseconds
operator"" _ns(long double ns)
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::duration<long double, std::nano>(ns));
}
// Namespace escalations.
// For example, this next line escalates type "rclcpp:node::Node" to "rclcpp::Node"
@@ -82,34 +155,6 @@ using rclcpp::utilities::shutdown;
using rclcpp::utilities::init;
using rclcpp::utilities::sleep_for;
/// Create a default single-threaded executor and execute any immediately available work.
// \param[in] node_ptr Shared pointer to the node to spin.
void spin_some(Node::SharedPtr node_ptr)
{
rclcpp::executors::SingleThreadedExecutor executor;
executor.spin_node_some(node_ptr);
}
} // namespace rclcpp
/// Create a default single-threaded executor and spin the specified node.
// \param[in] node_ptr Shared pointer to the node to spin.
void spin(Node::SharedPtr node_ptr)
{
rclcpp::executors::SingleThreadedExecutor executor;
executor.add_node(node_ptr);
executor.spin();
}
template<typename FutureT, typename TimeT = std::milli>
rclcpp::executors::FutureReturnCode
spin_until_future_complete(
Node::SharedPtr node_ptr, std::shared_future<FutureT> & future,
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
{
rclcpp::executors::SingleThreadedExecutor executor;
return rclcpp::executors::spin_node_until_future_complete<FutureT>(
executor, node_ptr, future, timeout);
}
} /* namespace rclcpp */
#endif /* RCLCPP_RCLCPP_RCLCPP_HPP_ */
#endif // RCLCPP__RCLCPP_HPP_

View File

@@ -0,0 +1,52 @@
// 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_
#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

@@ -12,8 +12,8 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_SERVICE_HPP_
#define RCLCPP_RCLCPP_SERVICE_HPP_
#ifndef RCLCPP__SERVICE_HPP_
#define RCLCPP__SERVICE_HPP_
#include <functional>
#include <iostream>
@@ -21,70 +21,69 @@
#include <sstream>
#include <string>
#include <rmw/error_handling.h>
#include <rmw/rmw.h>
#include "rcl/error_handling.h"
#include "rcl/service.h"
#include <rclcpp/macros.hpp>
#include <rclcpp/any_service_callback.hpp>
#include "rclcpp/any_service_callback.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 "rmw/error_handling.h"
#include "rmw/rmw.h"
namespace rclcpp
{
namespace service
{
class ServiceBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ServiceBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ServiceBase)
RCLCPP_PUBLIC
ServiceBase(
std::shared_ptr<rmw_node_t> node_handle,
rmw_service_t * service_handle,
const std::string service_name)
: node_handle_(node_handle), service_handle_(service_handle), service_name_(service_name)
{}
std::shared_ptr<rcl_node_t> node_handle,
const std::string & service_name);
virtual ~ServiceBase()
{
if (service_handle_) {
if (rmw_destroy_service(service_handle_) != RMW_RET_OK) {
std::stringstream ss;
ss << "Error in destruction of rmw service_handle_ handle: " <<
rmw_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
}
}
}
RCLCPP_PUBLIC
explicit ServiceBase(
std::shared_ptr<rcl_node_t> node_handle);
std::string get_service_name()
{
return this->service_name_;
}
RCLCPP_PUBLIC
virtual ~ServiceBase();
const rmw_service_t * get_service_handle()
{
return this->service_handle_;
}
RCLCPP_PUBLIC
std::string
get_service_name();
RCLCPP_PUBLIC
const rcl_service_t *
get_service_handle();
virtual std::shared_ptr<void> create_request() = 0;
virtual std::shared_ptr<void> create_request_header() = 0;
virtual std::shared_ptr<rmw_request_id_t> create_request_header() = 0;
virtual void handle_request(
std::shared_ptr<void> request_header,
std::shared_ptr<rmw_request_id_t> request_header,
std::shared_ptr<void> request) = 0;
private:
RCLCPP_DISABLE_COPY(ServiceBase);
protected:
RCLCPP_DISABLE_COPY(ServiceBase)
std::shared_ptr<rmw_node_t> node_handle_;
RCLCPP_PUBLIC
rcl_node_t *
get_rcl_node_handle() const;
rmw_service_t * service_handle_;
std::shared_ptr<rcl_node_t> node_handle_;
rcl_service_t * service_handle_ = nullptr;
std::string service_name_;
bool owns_rcl_handle_ = true;
};
using namespace any_service_callback;
using any_service_callback::AnyServiceCallback;
template<typename ServiceT>
class Service : public ServiceBase
@@ -100,59 +99,121 @@ public:
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<typename ServiceT::Request>,
std::shared_ptr<typename ServiceT::Response>)>;
RCLCPP_SMART_PTR_DEFINITIONS(Service);
RCLCPP_SMART_PTR_DEFINITIONS(Service)
Service(
std::shared_ptr<rmw_node_t> node_handle,
rmw_service_t * service_handle,
std::shared_ptr<rcl_node_t> node_handle,
const std::string & service_name,
AnyServiceCallback<ServiceT> any_callback,
rcl_service_options_t & service_options)
: ServiceBase(node_handle, service_name), any_callback_(any_callback)
{
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_ = new rcl_service_t;
*service_handle_ = rcl_get_zero_initialized_service();
rcl_ret_t ret = rcl_service_init(
service_handle_,
node_handle.get(),
service_type_support_handle,
service_name.c_str(),
&service_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_SERVICE_NAME_INVALID) {
auto rcl_node_handle = get_rcl_node_handle();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
service_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle),
true);
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
}
}
Service(
std::shared_ptr<rcl_node_t> node_handle,
rcl_service_t * service_handle,
AnyServiceCallback<ServiceT> any_callback)
: ServiceBase(node_handle, service_handle, service_name), any_callback_(any_callback)
{}
: ServiceBase(node_handle),
any_callback_(any_callback)
{
// check if service handle was initialized
// TODO(karsten1987): Take this verification
// directly in rcl_*_t
// see: https://github.com/ros2/rcl/issues/81
if (!service_handle->impl) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("rcl_service_t in constructor argument must be initialized beforehand."));
// *INDENT-ON*
}
service_handle_ = service_handle;
service_name_ = std::string(rcl_service_get_service_name(service_handle));
owns_rcl_handle_ = false;
}
Service() = delete;
virtual ~Service()
{
// check if you have ownership of the handle
if (owns_rcl_handle_) {
if (rcl_service_fini(service_handle_, node_handle_.get()) != RCL_RET_OK) {
std::stringstream ss;
ss << "Error in destruction of rcl service_handle_ handle: " <<
rcl_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
rcl_reset_error();
}
}
}
std::shared_ptr<void> create_request()
{
return std::shared_ptr<void>(new typename ServiceT::Request());
}
std::shared_ptr<void> create_request_header()
std::shared_ptr<rmw_request_id_t> create_request_header()
{
// TODO(wjwwood): This should probably use rmw_request_id's allocator.
// (since it is a C type)
return std::shared_ptr<void>(new rmw_request_id_t);
return std::shared_ptr<rmw_request_id_t>(new rmw_request_id_t);
}
void handle_request(std::shared_ptr<void> request_header, std::shared_ptr<void> request)
void handle_request(std::shared_ptr<rmw_request_id_t> request_header,
std::shared_ptr<void> request)
{
auto typed_request = std::static_pointer_cast<typename ServiceT::Request>(request);
auto typed_request_header = std::static_pointer_cast<rmw_request_id_t>(request_header);
auto response = std::shared_ptr<typename ServiceT::Response>(new typename ServiceT::Response);
any_callback_.dispatch(typed_request_header, typed_request, response);
send_response(typed_request_header, response);
any_callback_.dispatch(request_header, typed_request, response);
send_response(request_header, response);
}
void send_response(
std::shared_ptr<rmw_request_id_t> req_id,
std::shared_ptr<typename ServiceT::Response> response)
{
rmw_ret_t status = rmw_send_response(get_service_handle(), req_id.get(), response.get());
if (status != RMW_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
std::string("failed to send response: ") + rmw_get_error_string_safe());
// *INDENT-ON*
rcl_ret_t status = rcl_send_response(get_service_handle(), req_id.get(), response.get());
if (status != RCL_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(status, "failed to send response");
}
}
private:
RCLCPP_DISABLE_COPY(Service);
RCLCPP_DISABLE_COPY(Service)
AnyServiceCallback<ServiceT> any_callback_;
};
} /* namespace service */
} /* namespace rclcpp */
} // namespace service
} // namespace rclcpp
#endif /* RCLCPP_RCLCPP_SERVICE_HPP_ */
#endif // RCLCPP__SERVICE_HPP_

View File

@@ -18,16 +18,19 @@
#include <memory>
#include <vector>
#include "rcl/allocator.h"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rmw/types.h"
namespace rclcpp
{
namespace memory_strategies
{
namespace allocator_memory_strategy
{
@@ -41,7 +44,7 @@ template<typename Alloc = std::allocator<void>>
class AllocatorMemoryStrategy : public memory_strategy::MemoryStrategy
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(AllocatorMemoryStrategy<Alloc>);
RCLCPP_SMART_PTR_DEFINITIONS(AllocatorMemoryStrategy<Alloc>)
using ExecAllocTraits = allocator::AllocRebind<executor::AnyExecutable, Alloc>;
using ExecAlloc = typename ExecAllocTraits::allocator_type;
@@ -49,8 +52,6 @@ public:
using VoidAllocTraits = typename allocator::AllocRebind<void *, Alloc>;
using VoidAlloc = typename VoidAllocTraits::allocator_type;
using WeakNodeVector = std::vector<std::weak_ptr<node::Node>>;
explicit AllocatorMemoryStrategy(std::shared_ptr<Alloc> allocator)
{
executable_allocator_ = std::make_shared<ExecAlloc>(*allocator.get());
@@ -63,81 +64,76 @@ public:
allocator_ = std::make_shared<VoidAlloc>();
}
size_t fill_subscriber_handles(void ** & ptr)
void add_guard_condition(const rcl_guard_condition_t * guard_condition)
{
for (auto & subscription : subscriptions_) {
subscriber_handles_.push_back(subscription->get_subscription_handle()->data);
if (subscription->get_intra_process_subscription_handle()) {
subscriber_handles_.push_back(subscription->get_intra_process_subscription_handle()->data);
for (const auto & existing_guard_condition : guard_conditions_) {
if (existing_guard_condition == guard_condition) {
return;
}
}
ptr = subscriber_handles_.data();
return subscriber_handles_.size();
guard_conditions_.push_back(guard_condition);
}
// return the new number of services
size_t fill_service_handles(void ** & ptr)
void remove_guard_condition(const rcl_guard_condition_t * guard_condition)
{
for (auto & service : services_) {
service_handles_.push_back(service->get_service_handle()->data);
for (auto it = guard_conditions_.begin(); it != guard_conditions_.end(); ++it) {
if (*it == guard_condition) {
guard_conditions_.erase(it);
break;
}
}
ptr = service_handles_.data();
return service_handles_.size();
}
// return the new number of clients
size_t fill_client_handles(void ** & ptr)
{
for (auto & client : clients_) {
client_handles_.push_back(client->get_client_handle()->data);
}
ptr = client_handles_.data();
return client_handles_.size();
}
void clear_active_entities()
{
subscriptions_.clear();
services_.clear();
clients_.clear();
}
void clear_handles()
{
subscriber_handles_.clear();
subscription_handles_.clear();
service_handles_.clear();
client_handles_.clear();
timer_handles_.clear();
}
void revalidate_handles()
virtual void remove_null_handles(rcl_wait_set_t * wait_set)
{
{
VectorRebind<void *> temp;
for (auto & subscriber_handle : subscriber_handles_) {
if (subscriber_handle) {
temp.push_back(subscriber_handle);
}
for (size_t i = 0; i < wait_set->size_of_subscriptions; ++i) {
if (!wait_set->subscriptions[i]) {
subscription_handles_[i] = nullptr;
}
subscriber_handles_.swap(temp);
}
{
VectorRebind<void *> temp;
for (auto & service_handle : service_handles_) {
if (service_handle) {
temp.push_back(service_handle);
}
for (size_t i = 0; i < wait_set->size_of_services; ++i) {
if (!wait_set->services[i]) {
service_handles_[i] = nullptr;
}
service_handles_.swap(temp);
}
{
VectorRebind<void *> temp;
for (auto & client_handle : client_handles_) {
if (client_handle) {
temp.push_back(client_handle);
}
for (size_t i = 0; i < wait_set->size_of_clients; ++i) {
if (!wait_set->clients[i]) {
client_handles_[i] = nullptr;
}
client_handles_.swap(temp);
}
for (size_t i = 0; i < wait_set->size_of_timers; ++i) {
if (!wait_set->timers[i]) {
timer_handles_[i] = nullptr;
}
}
subscription_handles_.erase(
std::remove(subscription_handles_.begin(), subscription_handles_.end(), nullptr),
subscription_handles_.end()
);
service_handles_.erase(
std::remove(service_handles_.begin(), service_handles_.end(), nullptr),
service_handles_.end()
);
client_handles_.erase(
std::remove(client_handles_.begin(), client_handles_.end(), nullptr),
client_handles_.end()
);
timer_handles_.erase(
std::remove(timer_handles_.begin(), timer_handles_.end(), nullptr),
timer_handles_.end()
);
}
bool collect_entities(const WeakNodeVector & weak_nodes)
@@ -146,7 +142,7 @@ public:
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
has_invalid_weak_nodes = false;
has_invalid_weak_nodes = true;
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
@@ -157,17 +153,29 @@ public:
for (auto & weak_subscription : group->get_subscription_ptrs()) {
auto subscription = weak_subscription.lock();
if (subscription) {
subscriptions_.push_back(subscription);
subscription_handles_.push_back(subscription->get_subscription_handle());
if (subscription->get_intra_process_subscription_handle()) {
subscription_handles_.push_back(
subscription->get_intra_process_subscription_handle());
}
}
}
for (auto & service : group->get_service_ptrs()) {
for (auto & weak_service : group->get_service_ptrs()) {
auto service = weak_service.lock();
if (service) {
services_.push_back(service);
service_handles_.push_back(service->get_service_handle());
}
}
for (auto & client : group->get_client_ptrs()) {
for (auto & weak_client : group->get_client_ptrs()) {
auto client = weak_client.lock();
if (client) {
clients_.push_back(client);
client_handles_.push_back(client->get_client_handle());
}
}
for (auto & weak_timer : group->get_timer_ptrs()) {
auto timer = weak_timer.lock();
if (timer) {
timer_handles_.push_back(timer->get_timer_handle());
}
}
}
@@ -175,6 +183,45 @@ public:
return has_invalid_weak_nodes;
}
bool add_handles_to_waitset(rcl_wait_set_t * wait_set)
{
for (auto subscription : subscription_handles_) {
if (rcl_wait_set_add_subscription(wait_set, subscription) != RCL_RET_OK) {
fprintf(stderr, "Couldn't add subscription to waitset: %s\n", rcl_get_error_string_safe());
return false;
}
}
for (auto client : client_handles_) {
if (rcl_wait_set_add_client(wait_set, client) != RCL_RET_OK) {
fprintf(stderr, "Couldn't add client to waitset: %s\n", rcl_get_error_string_safe());
return false;
}
}
for (auto service : service_handles_) {
if (rcl_wait_set_add_service(wait_set, service) != RCL_RET_OK) {
fprintf(stderr, "Couldn't add service to waitset: %s\n", rcl_get_error_string_safe());
return false;
}
}
for (auto timer : timer_handles_) {
if (rcl_wait_set_add_timer(wait_set, timer) != RCL_RET_OK) {
fprintf(stderr, "Couldn't add timer to waitset: %s\n", rcl_get_error_string_safe());
return false;
}
}
for (auto guard_condition : guard_conditions_) {
if (rcl_wait_set_add_guard_condition(wait_set, guard_condition) != RCL_RET_OK) {
fprintf(stderr, "Couldn't add guard_condition to waitset: %s\n",
rcl_get_error_string_safe());
return false;
}
}
return true;
}
/// Provide a newly initialized AnyExecutable object.
// \return Shared pointer to the fresh executable.
@@ -187,21 +234,21 @@ public:
get_next_subscription(executor::AnyExecutable::SharedPtr any_exec,
const WeakNodeVector & weak_nodes)
{
auto it = subscriber_handles_.begin();
while (it != subscriber_handles_.end()) {
auto it = subscription_handles_.begin();
while (it != subscription_handles_.end()) {
auto subscription = get_subscription_by_handle(*it, weak_nodes);
if (subscription) {
// Figure out if this is for intra-process or not.
bool is_intra_process = false;
if (subscription->get_intra_process_subscription_handle()) {
is_intra_process = subscription->get_intra_process_subscription_handle()->data == *it;
is_intra_process = subscription->get_intra_process_subscription_handle() == *it;
}
// Find the group for this handle and see if it can be serviced
auto group = get_group_by_subscription(subscription, weak_nodes);
if (!group) {
// Group was not found, meaning the subscription is not valid...
// Remove it from the ready list and continue looking
subscriber_handles_.erase(it);
subscription_handles_.erase(it);
continue;
}
if (!group->can_be_taken_from().load()) {
@@ -217,12 +264,12 @@ public:
any_exec->subscription = subscription;
}
any_exec->callback_group = group;
any_exec->node = get_node_by_group(group, weak_nodes);
subscriber_handles_.erase(it);
any_exec->node_base = get_node_by_group(group, weak_nodes);
subscription_handles_.erase(it);
return;
}
// Else, the subscription is no longer valid, remove it and continue
subscriber_handles_.erase(it);
subscription_handles_.erase(it);
}
}
@@ -251,7 +298,7 @@ public:
// Otherwise it is safe to set and return the any_exec
any_exec->service = service;
any_exec->callback_group = group;
any_exec->node = get_node_by_group(group, weak_nodes);
any_exec->node_base = get_node_by_group(group, weak_nodes);
service_handles_.erase(it);
return;
}
@@ -284,7 +331,7 @@ public:
// Otherwise it is safe to set and return the any_exec
any_exec->client = client;
any_exec->callback_group = group;
any_exec->node = get_node_by_group(group, weak_nodes);
any_exec->node_base = get_node_by_group(group, weak_nodes);
client_handles_.erase(it);
return;
}
@@ -293,18 +340,47 @@ public:
}
}
virtual rcl_allocator_t get_allocator()
{
return rclcpp::allocator::get_rcl_allocator<void *, VoidAlloc>(*allocator_.get());
}
size_t number_of_ready_subscriptions() const
{
return subscription_handles_.size();
}
size_t number_of_ready_services() const
{
return service_handles_.size();
}
size_t number_of_ready_clients() const
{
return client_handles_.size();
}
size_t number_of_guard_conditions() const
{
return guard_conditions_.size();
}
size_t number_of_ready_timers() const
{
return timer_handles_.size();
}
private:
template<typename T>
using VectorRebind =
std::vector<T, typename std::allocator_traits<Alloc>::template rebind_alloc<T>>;
VectorRebind<rclcpp::subscription::SubscriptionBase::SharedPtr> subscriptions_;
VectorRebind<rclcpp::service::ServiceBase::SharedPtr> services_;
VectorRebind<rclcpp::client::ClientBase::SharedPtr> clients_;
VectorRebind<const rcl_guard_condition_t *> guard_conditions_;
VectorRebind<void *> subscriber_handles_;
VectorRebind<void *> service_handles_;
VectorRebind<void *> client_handles_;
VectorRebind<const rcl_subscription_t *> subscription_handles_;
VectorRebind<const rcl_service_t *> service_handles_;
VectorRebind<const rcl_client_t *> client_handles_;
VectorRebind<const rcl_timer_t *> timer_handles_;
std::shared_ptr<ExecAlloc> executable_allocator_;
std::shared_ptr<VoidAlloc> allocator_;
@@ -312,7 +388,6 @@ private:
} // namespace allocator_memory_strategy
} // namespace memory_strategies
} // namespace rclcpp
#endif // RCLCPP__STRATEGIES__ALLOCATOR_MEMORY_STRATEGY_HPP_

View File

@@ -15,8 +15,11 @@
#ifndef RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_
#define RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_
#include <rclcpp/macros.hpp>
#include <rclcpp/message_memory_strategy.hpp>
#include <memory>
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
@@ -32,14 +35,18 @@ namespace message_pool_memory_strategy
* The size of the message pool should be at least the largest number of concurrent accesses to
* the subscription (usually the number of threads).
*/
template<typename MessageT, size_t Size,
typename std::enable_if<rosidl_generator_traits::has_fixed_size<MessageT>::value>::type * =
nullptr>
template<
typename MessageT,
size_t Size,
typename std::enable_if<
rosidl_generator_traits::has_fixed_size<MessageT>::value
>::type * = nullptr
>
class MessagePoolMemoryStrategy
: public message_memory_strategy::MessageMemoryStrategy<MessageT>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MessagePoolMemoryStrategy);
RCLCPP_SMART_PTR_DEFINITIONS(MessagePoolMemoryStrategy)
/// Default constructor
MessagePoolMemoryStrategy()
@@ -101,4 +108,5 @@ protected:
} // namespace message_pool_memory_strategy
} // namespace strategies
} // namespace rclcpp
#endif // RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_

View File

@@ -24,19 +24,26 @@
#include <sstream>
#include <string>
#include "rcl/error_handling.h"
#include "rcl/subscription.h"
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/any_subscription_callback.hpp"
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace node
namespace node_interfaces
{
class Node;
} // namespace node
class NodeTopicsInterface;
} // namespace node_interfaces
namespace subscription
{
@@ -46,107 +53,77 @@ namespace subscription
class SubscriptionBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
/// Default constructor.
/**
* \param[in] node_handle The rmw representation of the node that owns this subscription.
* \param[in] node_handle The rcl representation of the node that owns this subscription.
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] ignore_local_publications True to ignore local publications (unused).
* \param[in] subscription_options options for the subscription.
*/
RCLCPP_PUBLIC
SubscriptionBase(
std::shared_ptr<rmw_node_t> node_handle,
rmw_subscription_t * subscription_handle,
std::shared_ptr<rcl_node_t> node_handle,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
bool ignore_local_publications)
: intra_process_subscription_handle_(nullptr),
node_handle_(node_handle),
subscription_handle_(subscription_handle),
topic_name_(topic_name),
ignore_local_publications_(ignore_local_publications)
{
// To avoid unused private member warnings.
(void)ignore_local_publications_;
}
const rcl_subscription_options_t & subscription_options);
/// Default destructor.
virtual ~SubscriptionBase()
{
if (subscription_handle_) {
if (rmw_destroy_subscription(node_handle_.get(), subscription_handle_) != RMW_RET_OK) {
std::stringstream ss;
ss << "Error in destruction of rmw subscription handle: " <<
rmw_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
}
}
if (intra_process_subscription_handle_) {
auto ret = rmw_destroy_subscription(node_handle_.get(), intra_process_subscription_handle_);
if (ret != RMW_RET_OK) {
std::stringstream ss;
ss << "Error in destruction of rmw intra process subscription handle: " <<
rmw_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
}
}
}
RCLCPP_PUBLIC
virtual ~SubscriptionBase();
/// Get the topic that this subscription is subscribed on.
const std::string & get_topic_name() const
{
return this->topic_name_;
}
RCLCPP_PUBLIC
const char *
get_topic_name() const;
const rmw_subscription_t * get_subscription_handle() const
{
return subscription_handle_;
}
RCLCPP_PUBLIC
const rcl_subscription_t *
get_subscription_handle() const;
const rmw_subscription_t * get_intra_process_subscription_handle() const
{
return intra_process_subscription_handle_;
}
RCLCPP_PUBLIC
virtual const rcl_subscription_t *
get_intra_process_subscription_handle() const;
/// Borrow a new message.
// \return Shared pointer to the fresh message.
virtual std::shared_ptr<void> create_message() = 0;
/** \return Shared pointer to the fresh message. */
virtual std::shared_ptr<void>
create_message() = 0;
/// Check if we need to handle the message, and execute the callback if we do.
/**
* \param[in] message Shared pointer to the message to handle.
* \param[in] message_info Metadata associated with this message.
*/
virtual void handle_message(
std::shared_ptr<void> & message,
const rmw_message_info_t & message_info) = 0;
virtual void
handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info) = 0;
/// Return the message borrowed in create_message.
// \param[in] Shared pointer to the returned message.
virtual void return_message(std::shared_ptr<void> & message) = 0;
virtual void handle_intra_process_message(
/** \param[in] message Shared pointer to the returned message. */
virtual void
return_message(std::shared_ptr<void> & message) = 0;
virtual void
handle_intra_process_message(
rcl_interfaces::msg::IntraProcessMessage & ipm,
const rmw_message_info_t & message_info) = 0;
protected:
rmw_subscription_t * intra_process_subscription_handle_;
rcl_subscription_t intra_process_subscription_handle_ = rcl_get_zero_initialized_subscription();
rcl_subscription_t subscription_handle_ = rcl_get_zero_initialized_subscription();
std::shared_ptr<rcl_node_t> node_handle_;
private:
RCLCPP_DISABLE_COPY(SubscriptionBase);
std::shared_ptr<rmw_node_t> node_handle_;
rmw_subscription_t * subscription_handle_;
std::string topic_name_;
bool ignore_local_publications_;
RCLCPP_DISABLE_COPY(SubscriptionBase)
};
using namespace any_subscription_callback;
using any_subscription_callback::AnySubscriptionCallback;
/// Subscription implementation, templated on the type of message this subscription receives.
template<typename MessageT, typename Alloc = std::allocator<void>>
class Subscription : public SubscriptionBase
{
friend class rclcpp::node::Node;
friend class rclcpp::node_interfaces::NodeTopicsInterface;
public:
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
@@ -154,34 +131,36 @@ public:
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
RCLCPP_SMART_PTR_DEFINITIONS(Subscription);
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
/// Default constructor.
/**
* The constructor for a subscription is almost never called directly. Instead, subscriptions
* should be instantiated through Node::create_subscription.
* \param[in] node_handle rmw representation of the node that owns this subscription.
* \param[in] node_handle rcl representation of the node that owns this subscription.
* \param[in] topic_name Name of the topic to subscribe to.
* \param[in] ignore_local_publications True to ignore local publications (unused).
* \param[in] callback User-defined callback to call when a message is received.
* \param[in] subscription_options options for the subscription.
* \param[in] callback User defined callback to call when a message is received.
* \param[in] memory_strategy The memory strategy to be used for managing message memory.
*/
Subscription(
std::shared_ptr<rmw_node_t> node_handle,
rmw_subscription_t * subscription_handle,
std::shared_ptr<rcl_node_t> node_handle,
const std::string & topic_name,
bool ignore_local_publications,
const rcl_subscription_options_t & subscription_options,
AnySubscriptionCallback<MessageT, Alloc> callback,
typename message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr memory_strategy =
message_memory_strategy::MessageMemoryStrategy<MessageT,
typename message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
memory_strategy = message_memory_strategy::MessageMemoryStrategy<MessageT,
Alloc>::create_default())
: SubscriptionBase(node_handle, subscription_handle, topic_name, ignore_local_publications),
: SubscriptionBase(
node_handle,
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
topic_name,
subscription_options),
any_callback_(callback),
message_memory_strategy_(memory_strategy),
get_intra_process_message_callback_(nullptr),
matches_any_intra_process_publishers_(nullptr)
{
}
{}
/// Support dynamically setting the message memory strategy.
/**
@@ -216,6 +195,8 @@ public:
any_callback_.dispatch(typed_message, message_info);
}
/// Return the loaned message.
/** \param message message to be returned */
void return_message(std::shared_ptr<void> & message)
{
auto typed_message = std::static_pointer_cast<MessageT>(message);
@@ -248,26 +229,55 @@ public:
any_callback_.dispatch_intra_process(msg, message_info);
}
private:
typedef
std::function<
void (uint64_t, uint64_t, uint64_t, MessageUniquePtr &)
> GetMessageCallbackType;
typedef std::function<bool (const rmw_gid_t *)> MatchesAnyPublishersCallbackType;
using GetMessageCallbackType =
std::function<void(uint64_t, uint64_t, uint64_t, MessageUniquePtr &)>;
using MatchesAnyPublishersCallbackType = std::function<bool(const rmw_gid_t *)>;
/// Implemenation detail.
void setup_intra_process(
uint64_t intra_process_subscription_id,
rmw_subscription_t * intra_process_subscription,
GetMessageCallbackType get_message_callback,
MatchesAnyPublishersCallbackType matches_any_publisher_callback)
MatchesAnyPublishersCallbackType matches_any_publisher_callback,
const rcl_subscription_options_t & intra_process_options)
{
std::string intra_process_topic_name = std::string(get_topic_name()) + "/_intra";
rcl_ret_t ret = rcl_subscription_init(
&intra_process_subscription_handle_,
node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
intra_process_topic_name.c_str(),
&intra_process_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
auto rcl_node_handle = node_handle_.get();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
intra_process_topic_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle));
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create intra process subscription");
}
intra_process_subscription_id_ = intra_process_subscription_id;
intra_process_subscription_handle_ = intra_process_subscription;
get_intra_process_message_callback_ = get_message_callback;
matches_any_intra_process_publishers_ = matches_any_publisher_callback;
}
RCLCPP_DISABLE_COPY(Subscription);
/// Implemenation detail.
const rcl_subscription_t *
get_intra_process_subscription_handle() const
{
if (!get_intra_process_message_callback_) {
return nullptr;
}
return &intra_process_subscription_handle_;
}
private:
RCLCPP_DISABLE_COPY(Subscription)
AnySubscriptionCallback<MessageT, Alloc> any_callback_;
typename message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr

View File

@@ -0,0 +1,171 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__SUBSCRIPTION_FACTORY_HPP_
#define RCLCPP__SUBSCRIPTION_FACTORY_HPP_
#include <functional>
#include <memory>
#include <string>
#include <utility>
#include "rcl/subscription.h"
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rclcpp/subscription.hpp"
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
/// Factory with functions used to create a Subscription<MessageT>.
/**
* This factory class is used to encapsulate the template generated functions
* which are used during the creation of a Message type specific subscription
* within a non-templated class.
*
* It is created using the create_subscription_factory function, which is
* usually called from a templated "create_subscription" method of the Node
* class, and is passed to the non-templated "create_subscription" method of
* the NodeTopics class where it is used to create and setup the Subscription.
*/
struct SubscriptionFactory
{
// Creates a Subscription<MessageT> object and returns it as a SubscriptionBase.
using SubscriptionFactoryFunction = std::function<
rclcpp::subscription::SubscriptionBase::SharedPtr(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_subscription_options_t & subscription_options)>;
SubscriptionFactoryFunction create_typed_subscription;
// Function that takes a MessageT from the intra process manager
using SetupIntraProcessFunction = std::function<void(
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm,
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
const rcl_subscription_options_t & subscription_options)>;
SetupIntraProcessFunction setup_intra_process;
};
/// Return a SubscriptionFactory with functions for creating a SubscriptionT<MessageT, Alloc>.
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
SubscriptionFactory
create_subscription_factory(
CallbackT && callback,
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
msg_mem_strat,
std::shared_ptr<Alloc> allocator)
{
SubscriptionFactory factory;
using rclcpp::subscription::AnySubscriptionCallback;
AnySubscriptionCallback<MessageT, Alloc> any_subscription_callback(allocator);
any_subscription_callback.set(std::forward<CallbackT>(callback));
auto message_alloc =
std::make_shared<typename subscription::Subscription<MessageT, Alloc>::MessageAlloc>();
// factory function that creates a MessageT specific SubscriptionT
factory.create_typed_subscription =
[allocator, msg_mem_strat, any_subscription_callback, message_alloc](
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic_name,
rcl_subscription_options_t & subscription_options
) -> rclcpp::subscription::SubscriptionBase::SharedPtr
{
subscription_options.allocator =
rclcpp::allocator::get_rcl_allocator<MessageT>(*message_alloc.get());
using rclcpp::subscription::Subscription;
using rclcpp::subscription::SubscriptionBase;
auto sub = Subscription<MessageT, Alloc>::make_shared(
node_base->get_shared_rcl_node_handle(),
topic_name,
subscription_options,
any_subscription_callback,
msg_mem_strat);
auto sub_base_ptr = std::dynamic_pointer_cast<SubscriptionBase>(sub);
return sub_base_ptr;
};
// function that will setup intra process communications for the subscription
factory.setup_intra_process =
[message_alloc](
rclcpp::intra_process_manager::IntraProcessManager::SharedPtr ipm,
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
const rcl_subscription_options_t & subscription_options)
{
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = ipm;
uint64_t intra_process_subscription_id = ipm->add_subscription(subscription);
auto intra_process_options = rcl_subscription_get_default_options();
intra_process_options.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(
*message_alloc.get());
intra_process_options.qos = subscription_options.qos;
intra_process_options.ignore_local_publications = false;
// function that will be called to take a MessageT from the intra process manager
auto take_intra_process_message_func =
[weak_ipm](
uint64_t publisher_id,
uint64_t message_sequence,
uint64_t subscription_id,
typename rclcpp::subscription::Subscription<MessageT, Alloc>::MessageUniquePtr & message)
{
auto ipm = weak_ipm.lock();
if (!ipm) {
// TODO(wjwwood): should this just return silently? Or return with a logged warning?
throw std::runtime_error(
"intra process take called after destruction of intra process manager");
}
ipm->take_intra_process_message<MessageT, Alloc>(
publisher_id, message_sequence, subscription_id, message);
};
// function that is called to see if the publisher id matches any local publishers
auto matches_any_publisher_func =
[weak_ipm](const rmw_gid_t * sender_gid) -> bool
{
auto ipm = weak_ipm.lock();
if (!ipm) {
throw std::runtime_error(
"intra process publisher check called "
"after destruction of intra process manager");
}
return ipm->matches_any_publishers(sender_gid);
};
auto typed_sub_ptr = std::dynamic_pointer_cast<SubscriptionT>(subscription);
typed_sub_ptr->setup_intra_process(
intra_process_subscription_id,
take_intra_process_message_func,
matches_any_publisher_func,
intra_process_options
);
};
// end definition of factory function to setup intra process
// return the factory now that it is populated
return factory;
}
} // namespace rclcpp
#endif // RCLCPP__SUBSCRIPTION_FACTORY_HPP_

View File

@@ -0,0 +1,72 @@
// Copyright 2017 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__TIME_HPP_
#define RCLCPP__TIME_HPP_
#include <utility>
#include "builtin_interfaces/msg/time.hpp"
#include "rcl/time.h"
#include "rcutils/time.h"
#include "rclcpp/exceptions.hpp"
namespace rclcpp
{
class Time
{
public:
template<rcl_time_source_type_t ClockT = RCL_SYSTEM_TIME>
static Time
now()
{
rcutils_time_point_value_t rcutils_now = 0;
rcutils_ret_t ret = RCUTILS_RET_ERROR;
if (ClockT == RCL_ROS_TIME) {
throw std::runtime_error("RCL_ROS_TIME is currently not implemented.");
ret = false;
} else if (ClockT == RCL_SYSTEM_TIME) {
ret = rcutils_system_time_now(&rcutils_now);
} else if (ClockT == RCL_STEADY_TIME) {
ret = rcutils_steady_time_now(&rcutils_now);
}
if (ret != RCUTILS_RET_OK) {
rclcpp::exceptions::throw_from_rcl_error(
ret, "Could not get current time: ");
}
return Time(std::move(rcutils_now));
}
operator builtin_interfaces::msg::Time() const
{
builtin_interfaces::msg::Time msg_time;
msg_time.sec = static_cast<std::int32_t>(RCL_NS_TO_S(rcl_time_));
msg_time.nanosec = static_cast<std::uint32_t>(rcl_time_ % (1000 * 1000 * 1000));
return msg_time;
}
private:
rcl_time_point_value_t rcl_time_;
explicit Time(rcl_time_point_value_t && rcl_time)
: rcl_time_(std::forward<decltype(rcl_time)>(rcl_time))
{}
};
} // namespace rclcpp
#endif // RCLCPP__TIME_HPP_

View File

@@ -12,107 +12,111 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_TIMER_HPP_
#define RCLCPP_RCLCPP_TIMER_HPP_
#ifndef RCLCPP__TIMER_HPP_
#define RCLCPP__TIMER_HPP_
#include <chrono>
#include <functional>
#include <memory>
#include <sstream>
#include <thread>
#include <type_traits>
#include <utility>
#include <rmw/error_handling.h>
#include <rmw/rmw.h>
#include "rclcpp/function_traits.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/rate.hpp"
#include "rclcpp/utilities.hpp"
#include "rclcpp/visibility_control.hpp"
#include <rclcpp/macros.hpp>
#include <rclcpp/rate.hpp>
#include <rclcpp/utilities.hpp>
#include "rcl/error_handling.h"
#include "rcl/timer.h"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
namespace rclcpp
{
namespace timer
{
using CallbackType = std::function<void()>;
class TimerBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimerBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimerBase)
TimerBase(std::chrono::nanoseconds period, CallbackType callback)
: period_(period),
callback_(callback),
canceled_(false)
{
}
RCLCPP_PUBLIC
explicit TimerBase(std::chrono::nanoseconds period);
virtual ~TimerBase()
{
}
RCLCPP_PUBLIC
~TimerBase();
RCLCPP_PUBLIC
void
cancel()
{
this->canceled_ = true;
}
cancel();
void execute_callback() const
{
callback_();
}
RCLCPP_PUBLIC
void
reset();
const CallbackType & get_callback() const
{
return callback_;
}
RCLCPP_PUBLIC
virtual void
execute_callback() = 0;
RCLCPP_PUBLIC
const rcl_timer_t *
get_timer_handle();
/// Check how long the timer has until its next scheduled callback.
// \return A std::chrono::duration representing the relative time until the next callback.
virtual std::chrono::nanoseconds
time_until_trigger() = 0;
/** \return A std::chrono::duration representing the relative time until the next callback. */
RCLCPP_PUBLIC
std::chrono::nanoseconds
time_until_trigger();
/// Is the clock steady (i.e. is the time between ticks constant?)
// \return True if the clock used by this timer is steady.
/** \return True if the clock used by this timer is steady. */
virtual bool is_steady() = 0;
/// Check if the timer needs to trigger the callback.
/// Check if the timer is ready to trigger the callback.
/**
* This function expects its caller to immediately trigger the callback after this function,
* since it maintains the last time the callback was triggered.
* \return True if the timer needs to trigger.
*/
virtual bool check_and_trigger() = 0;
RCLCPP_PUBLIC
bool is_ready();
protected:
std::chrono::nanoseconds period_;
CallbackType callback_;
bool canceled_;
rcl_timer_t timer_handle_ = rcl_get_zero_initialized_timer();
};
using VoidCallbackType = std::function<void()>;
using TimerCallbackType = std::function<void(TimerBase &)>;
/// Generic timer templated on the clock type. Periodically executes a user-specified callback.
template<class Clock = std::chrono::high_resolution_clock>
template<
typename FunctorT,
class Clock,
typename std::enable_if<
(rclcpp::function_traits::same_arguments<FunctorT, VoidCallbackType>::value ||
rclcpp::function_traits::same_arguments<FunctorT, TimerCallbackType>::value) &&
Clock::is_steady
>::type * = nullptr
>
class GenericTimer : public TimerBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(GenericTimer);
RCLCPP_SMART_PTR_DEFINITIONS(GenericTimer)
/// Default constructor.
/**
* \param[in] period The interval at which the timer fires.
* \param[in] callback User-specified callback function.
*/
GenericTimer(std::chrono::nanoseconds period, CallbackType callback)
: TimerBase(period, callback), loop_rate_(period)
GenericTimer(std::chrono::nanoseconds period, FunctorT && callback)
: TimerBase(period), callback_(std::forward<FunctorT>(callback))
{
/* Subtracting the loop rate period ensures that the callback gets triggered
on the first call to check_and_trigger. */
last_triggered_time_ = Clock::now() - period;
}
/// Default destructor.
@@ -120,39 +124,47 @@ public:
{
// Stop the timer from running.
cancel();
if (rcl_timer_fini(&timer_handle_) != RCL_RET_OK) {
fprintf(stderr, "Failed to clean up rcl timer handle: %s\n", rcl_get_error_string_safe());
}
}
bool
check_and_trigger()
void
execute_callback()
{
if (canceled_) {
return false;
rcl_ret_t ret = rcl_timer_call(&timer_handle_);
if (ret == RCL_RET_TIMER_CANCELED) {
return;
}
if (Clock::now() < last_triggered_time_) {
return false;
if (ret != RCL_RET_OK) {
throw std::runtime_error("Failed to notify timer that callback occurred");
}
if (std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now() - last_triggered_time_) >=
loop_rate_.period())
{
last_triggered_time_ = Clock::now();
return true;
}
return false;
execute_callback_delegate<>();
}
std::chrono::nanoseconds
time_until_trigger()
// void specialization
template<
typename CallbackT = FunctorT,
typename std::enable_if<
rclcpp::function_traits::same_arguments<CallbackT, VoidCallbackType>::value
>::type * = nullptr
>
void
execute_callback_delegate()
{
std::chrono::nanoseconds time_until_trigger;
// Calculate the time between the next trigger and the current time
if (last_triggered_time_ + loop_rate_.period() < Clock::now()) {
// time is overdue, need to trigger immediately
time_until_trigger = std::chrono::nanoseconds::zero();
} else {
time_until_trigger = std::chrono::duration_cast<std::chrono::nanoseconds>(
last_triggered_time_ - Clock::now()) + loop_rate_.period();
}
return time_until_trigger;
callback_();
}
template<
typename CallbackT = FunctorT,
typename std::enable_if<
rclcpp::function_traits::same_arguments<CallbackT, TimerCallbackType>::value
>::type * = nullptr
>
void
execute_callback_delegate()
{
callback_(*this);
}
virtual bool
@@ -161,17 +173,16 @@ public:
return Clock::is_steady;
}
private:
RCLCPP_DISABLE_COPY(GenericTimer);
rclcpp::rate::GenericRate<Clock> loop_rate_;
std::chrono::time_point<Clock> last_triggered_time_;
protected:
RCLCPP_DISABLE_COPY(GenericTimer)
FunctorT callback_;
};
using WallTimer = GenericTimer<std::chrono::steady_clock>;
template<typename CallbackType>
using WallTimer = GenericTimer<CallbackType, std::chrono::steady_clock>;
} /* namespace timer */
} /* namespace rclcpp */
} // namespace timer
} // namespace rclcpp
#endif /* RCLCPP_RCLCPP_TIMER_HPP_ */
#endif // RCLCPP__TIMER_HPP_

View File

@@ -0,0 +1,78 @@
// Copyright 2014 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP__TYPE_SUPPORT_DECL_HPP_
#define RCLCPP__TYPE_SUPPORT_DECL_HPP_
#include "rosidl_generator_cpp/message_type_support_decl.hpp"
#include "rosidl_generator_cpp/service_type_support_decl.hpp"
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rosidl_typesupport_cpp/service_type_support.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rclcpp
{
namespace type_support
{
RCLCPP_PUBLIC
const rosidl_message_type_support_t *
get_intra_process_message_msg_type_support();
RCLCPP_PUBLIC
const rosidl_message_type_support_t *
get_parameter_event_msg_type_support();
RCLCPP_PUBLIC
const rosidl_message_type_support_t *
get_set_parameters_result_msg_type_support();
RCLCPP_PUBLIC
const rosidl_message_type_support_t *
get_parameter_descriptor_msg_type_support();
RCLCPP_PUBLIC
const rosidl_message_type_support_t *
get_list_parameters_result_msg_type_support();
RCLCPP_PUBLIC
const rosidl_service_type_support_t *
get_get_parameters_srv_type_support();
RCLCPP_PUBLIC
const rosidl_service_type_support_t *
get_get_parameter_types_srv_type_support();
RCLCPP_PUBLIC
const rosidl_service_type_support_t *
get_set_parameters_srv_type_support();
RCLCPP_PUBLIC
const rosidl_service_type_support_t *
get_list_parameters_srv_type_support();
RCLCPP_PUBLIC
const rosidl_service_type_support_t *
get_describe_parameters_srv_type_support();
RCLCPP_PUBLIC
const rosidl_service_type_support_t *
get_set_parameters_atomically_srv_type_support();
} // namespace type_support
} // namespace rclcpp
#endif // RCLCPP__TYPE_SUPPORT_DECL_HPP_

View File

@@ -12,100 +12,38 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#ifndef RCLCPP_RCLCPP_UTILITIES_HPP_
#define RCLCPP_RCLCPP_UTILITIES_HPP_
#ifndef RCLCPP__UTILITIES_HPP_
#define RCLCPP__UTILITIES_HPP_
// TODO(wjwwood): remove
#include <iostream>
#include <cerrno>
#include <chrono>
#include <condition_variable>
#include <csignal>
#include <cstring>
#include <mutex>
#include <string.h>
#include <thread>
#include <functional>
#include <rmw/error_handling.h>
#include <rmw/macros.h>
#include <rmw/rmw.h>
#include "rclcpp/visibility_control.hpp"
// Determine if sigaction is available
#if __APPLE__ || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
#define HAS_SIGACTION
#endif
#include "rcl/guard_condition.h"
#include "rcl/wait.h"
namespace
#include "rmw/macros.h"
#include "rmw/rmw.h"
#ifdef ANDROID
#include <string>
#include <sstream>
namespace std
{
/// Represent the status of the global interrupt signal.
volatile sig_atomic_t g_signal_status = 0;
/// Guard condition for interrupting the rmw implementation when the global interrupt signal fired.
rmw_guard_condition_t * g_sigint_guard_cond_handle = \
rmw_create_guard_condition();
/// Condition variable for timed sleep (see sleep_for).
std::condition_variable g_interrupt_condition_variable;
std::atomic<bool> g_is_interrupted(false);
/// Mutex for protecting the global condition variable.
std::mutex g_interrupt_mutex;
#ifdef HAS_SIGACTION
struct sigaction old_action;
#else
void (* old_signal_handler)(int) = 0;
#endif
/// Handle the interrupt signal.
/** When the interrupt signal fires, the signal handler notifies the condition variable to wake up
* and triggers the interrupt guard condition, so that all global threads managed by rclcpp
* are interrupted.
*/
void
#ifdef HAS_SIGACTION
signal_handler(int signal_value, siginfo_t * siginfo, void * context)
#else
signal_handler(int signal_value)
#endif
template<typename T>
std::string to_string(T value)
{
// TODO(wjwwood): remove
std::cout << "signal_handler(" << signal_value << ")" << std::endl;
#ifdef HAS_SIGACTION
if (old_action.sa_flags & SA_SIGINFO) {
if (old_action.sa_sigaction != NULL) {
old_action.sa_sigaction(signal_value, siginfo, context);
}
} else {
// *INDENT-OFF*
if (
old_action.sa_handler != NULL && // Is set
old_action.sa_handler != SIG_DFL && // Is not default
old_action.sa_handler != SIG_IGN) // Is not ignored
// *INDENT-ON*
{
old_action.sa_handler(signal_value);
}
}
#else
if (old_signal_handler) {
old_signal_handler(signal_value);
}
#endif
g_signal_status = signal_value;
rmw_ret_t status = rmw_trigger_guard_condition(g_sigint_guard_cond_handle);
if (status != RMW_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to trigger guard condition: %s\n", rmw_get_error_string_safe());
}
g_is_interrupted.store(true);
g_interrupt_condition_variable.notify_all();
std::ostringstream os;
os << value;
return os.str();
}
} // namespace
}
#endif
namespace rclcpp
{
RMW_THREAD_LOCAL size_t thread_id = 0;
namespace utilities
{
@@ -114,113 +52,66 @@ namespace utilities
* \param[in] argc Number of arguments.
* \param[in] argv Argument vector. Will eventually be used for passing options to rclcpp.
*/
RCLCPP_PUBLIC
void
init(int argc, char * argv[])
{
(void)argc;
(void)argv;
g_is_interrupted.store(false);
rmw_ret_t status = rmw_init();
if (status != RMW_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
throw std::runtime_error(
std::string("failed to initialize rmw implementation: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
#ifdef HAS_SIGACTION
struct sigaction action;
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_sigaction = ::signal_handler;
action.sa_flags = SA_SIGINFO;
ssize_t ret = sigaction(SIGINT, &action, &old_action);
if (ret == -1)
#else
::old_signal_handler = std::signal(SIGINT, ::signal_handler);
if (::old_signal_handler == SIG_ERR)
#endif
{
const size_t error_length = 1024;
char error_string[error_length];
#ifndef _WIN32
auto rc = strerror_r(errno, error_string, error_length);
if (rc) {
// *INDENT-OFF*
throw std::runtime_error(
"Failed to set SIGINT signal handler: (" + std::to_string(errno) +
") unable to retrieve error string");
// *INDENT-ON*
}
#else
strerror_s(error_string, error_length, errno);
#endif
// *INDENT-OFF*
throw std::runtime_error(
std::string("Failed to set SIGINT signal handler: (" + std::to_string(errno) + ")") +
error_string);
// *INDENT-ON*
}
}
init(int argc, char * argv[]);
/// Check rclcpp's status.
// \return True if SIGINT hasn't fired yet, false otherwise.
/** \return True if SIGINT hasn't fired yet, false otherwise. */
RCLCPP_PUBLIC
bool
ok()
{
return ::g_signal_status == 0;
}
ok();
/// Notify the signal handler and rmw that rclcpp is shutting down.
RCLCPP_PUBLIC
void
shutdown()
{
g_signal_status = SIGINT;
rmw_ret_t status = rmw_trigger_guard_condition(g_sigint_guard_cond_handle);
if (status != RMW_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to trigger guard condition: %s\n", rmw_get_error_string_safe());
}
g_is_interrupted.store(true);
g_interrupt_condition_variable.notify_all();
}
shutdown();
/// Register a function to be called when shutdown is called.
/** Calling the callbacks is the last thing shutdown() does. */
RCLCPP_PUBLIC
void
on_shutdown(std::function<void(void)> callback);
/// Get a handle to the rmw guard condition that manages the signal handler.
rmw_guard_condition_t *
get_global_sigint_guard_condition()
{
return ::g_sigint_guard_cond_handle;
}
/**
* The first time that this function is called for a given waitset a new guard
* condition will be created and returned; thereafter the same guard condition
* will be returned for the same waitset. This mechanism is designed to ensure
* that the same guard condition is not reused across waitsets (e.g., when
* using multiple executors in the same process). Will throw an exception if
* initialization of the guard condition fails.
* \param waitset Pointer to the rcl_wait_set_t that will be using the
* resulting guard condition.
* \return Pointer to the guard condition.
*/
RCLCPP_PUBLIC
rcl_guard_condition_t *
get_sigint_guard_condition(rcl_wait_set_t * waitset);
/// Release the previously allocated guard condition that manages the signal handler.
/**
* If you previously called get_sigint_guard_condition() for a given waitset
* to get a sigint guard condition, then you should call release_sigint_guard_condition()
* when you're done, to free that condition. Will throw an exception if
* get_sigint_guard_condition() wasn't previously called for the given waitset.
* \param waitset Pointer to the rcl_wait_set_t that was using the
* resulting guard condition.
*/
RCLCPP_PUBLIC
void
release_sigint_guard_condition(rcl_wait_set_t * waitset);
/// Use the global condition variable to block for the specified amount of time.
/**
* \param[in] nanoseconds A std::chrono::duration representing how long to sleep for.
* \return True if the condition variable did not timeout.
*/
RCLCPP_PUBLIC
bool
sleep_for(const std::chrono::nanoseconds & nanoseconds)
{
// TODO: determine if posix's nanosleep(2) is more efficient here
std::chrono::nanoseconds time_left = nanoseconds;
{
std::unique_lock<std::mutex> lock(::g_interrupt_mutex);
auto start = std::chrono::steady_clock::now();
::g_interrupt_condition_variable.wait_for(lock, nanoseconds);
time_left -= std::chrono::steady_clock::now() - start;
}
if (time_left > std::chrono::nanoseconds::zero() && !g_is_interrupted) {
return sleep_for(time_left);
}
// Return true if the timeout elapsed successfully, otherwise false.
return !g_is_interrupted;
}
sleep_for(const std::chrono::nanoseconds & nanoseconds);
} /* namespace utilities */
} /* namespace rclcpp */
} // namespace utilities
} // namespace rclcpp
#ifdef HAS_SIGACTION
#undef HAS_SIGACTION
#endif
#endif /* RCLCPP_RCLCPP_UTILITIES_HPP_ */
#endif // RCLCPP__UTILITIES_HPP_

View File

@@ -0,0 +1,56 @@
// 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.
/* This header must be included by all rclcpp headers which declare symbols
* which are defined in the rclcpp library. When not building the rclcpp
* library, i.e. when using the headers in other package's code, the contents
* of this header change the visibility of certain symbols which the rclcpp
* library cannot have, but the consuming code must have inorder to link.
*/
#ifndef RCLCPP__VISIBILITY_CONTROL_HPP_
#define RCLCPP__VISIBILITY_CONTROL_HPP_
// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
// https://gcc.gnu.org/wiki/Visibility
#if defined _WIN32 || defined __CYGWIN__
#ifdef __GNUC__
#define RCLCPP_EXPORT __attribute__ ((dllexport))
#define RCLCPP_IMPORT __attribute__ ((dllimport))
#else
#define RCLCPP_EXPORT __declspec(dllexport)
#define RCLCPP_IMPORT __declspec(dllimport)
#endif
#ifdef RCLCPP_BUILDING_LIBRARY
#define RCLCPP_PUBLIC RCLCPP_EXPORT
#else
#define RCLCPP_PUBLIC RCLCPP_IMPORT
#endif
#define RCLCPP_PUBLIC_TYPE RCLCPP_PUBLIC
#define RCLCPP_LOCAL
#else
#define RCLCPP_EXPORT __attribute__ ((visibility("default")))
#define RCLCPP_IMPORT
#if __GNUC__ >= 4
#define RCLCPP_PUBLIC __attribute__ ((visibility("default")))
#define RCLCPP_LOCAL __attribute__ ((visibility("hidden")))
#else
#define RCLCPP_PUBLIC
#define RCLCPP_LOCAL
#endif
#define RCLCPP_PUBLIC_TYPE
#endif
#endif // RCLCPP__VISIBILITY_CONTROL_HPP_

View File

@@ -1,7 +1,8 @@
<?xml version="1.0"?>
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp</name>
<version>0.0.0</version>
<version>0.0.2</version>
<description>The ROS client library in C++.</description>
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
<license>Apache License 2.0</license>
@@ -10,13 +11,27 @@
<build_export_depend>rmw</build_export_depend>
<build_depend>builtin_interfaces</build_depend>
<build_depend>rcl_interfaces</build_depend>
<build_depend>rosidl_generator_cpp</build_depend>
<build_depend>rosidl_typesupport_c</build_depend>
<build_depend>rosidl_typesupport_cpp</build_depend>
<build_export_depend>builtin_interfaces</build_export_depend>
<build_export_depend>rcl_interfaces</build_export_depend>
<build_export_depend>rosidl_generator_cpp</build_export_depend>
<build_export_depend>rosidl_typesupport_c</build_export_depend>
<build_export_depend>rosidl_typesupport_cpp</build_export_depend>
<depend>rcl</depend>
<depend>rmw_implementation</depend>
<exec_depend>ament_cmake</exec_depend>
<test_depend>ament_cmake_gtest</test_depend>
<test_depend>ament_lint_auto</test_depend>
<test_depend>ament_lint_common</test_depend>
<test_depend>rmw</test_depend>
<test_depend>rmw_implementation_cmake</test_depend>
<export>
<build_type>ament_cmake</build_type>

View File

@@ -14,14 +14,16 @@
# copied from rclcpp/rclcpp-extras.cmake
set(rclcpp_node_main_SRC "${rclcpp_DIR}/../../../src/rclcpp/node_main.cpp")
# register ament_package() hook for node plugins once
macro(_rclcpp_register_package_hook)
if(NOT DEFINED _RCLCPP_PACKAGE_HOOK_REGISTERED)
set(_RCLCPP_PACKAGE_HOOK_REGISTERED TRUE)
function(rclcpp_create_node_main node_library_target)
if(NOT TARGET ${node_library_target})
message(FATAL_ERROR "rclcpp_create_node_main() the first argument must be a valid target name")
find_package(ament_cmake_core QUIET REQUIRED)
ament_register_extension("ament_package" "rclcpp"
"rclcpp_package_hook.cmake")
endif()
set(executable_name_ ${node_library_target}_node)
add_executable(${executable_name_} ${rclcpp_node_main_SRC})
target_link_libraries(${executable_name_} ${node_library_target})
install(TARGETS ${executable_name_} DESTINATION bin)
endfunction()
endmacro()
include("${rclcpp_DIR}/rclcpp_create_node_main.cmake")
include("${rclcpp_DIR}/rclcpp_register_node_plugins.cmake")

View File

@@ -0,0 +1,37 @@
// 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.
#include "rclcpp/any_executable.hpp"
using rclcpp::executor::AnyExecutable;
AnyExecutable::AnyExecutable()
: subscription(nullptr),
subscription_intra_process(nullptr),
timer(nullptr),
service(nullptr),
client(nullptr),
callback_group(nullptr),
node_base(nullptr)
{}
AnyExecutable::~AnyExecutable()
{
// Make sure that discarded (taken but not executed) AnyExecutable's have
// their callback groups reset. This can happen when an executor is canceled
// between taking an AnyExecutable and executing it.
if (callback_group) {
callback_group->can_be_taken_from().store(true);
}
}

View File

@@ -0,0 +1,93 @@
// 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.
#include "rclcpp/callback_group.hpp"
#include <vector>
using rclcpp::callback_group::CallbackGroup;
using rclcpp::callback_group::CallbackGroupType;
CallbackGroup::CallbackGroup(CallbackGroupType group_type)
: type_(group_type), can_be_taken_from_(true)
{}
const std::vector<rclcpp::subscription::SubscriptionBase::WeakPtr> &
CallbackGroup::get_subscription_ptrs() const
{
std::lock_guard<std::mutex> lock(mutex_);
return subscription_ptrs_;
}
const std::vector<rclcpp::timer::TimerBase::WeakPtr> &
CallbackGroup::get_timer_ptrs() const
{
std::lock_guard<std::mutex> lock(mutex_);
return timer_ptrs_;
}
const std::vector<rclcpp::service::ServiceBase::WeakPtr> &
CallbackGroup::get_service_ptrs() const
{
std::lock_guard<std::mutex> lock(mutex_);
return service_ptrs_;
}
const std::vector<rclcpp::client::ClientBase::WeakPtr> &
CallbackGroup::get_client_ptrs() const
{
std::lock_guard<std::mutex> lock(mutex_);
return client_ptrs_;
}
std::atomic_bool &
CallbackGroup::can_be_taken_from()
{
return can_be_taken_from_;
}
const CallbackGroupType &
CallbackGroup::type() const
{
return type_;
}
void
CallbackGroup::add_subscription(
const rclcpp::subscription::SubscriptionBase::SharedPtr subscription_ptr)
{
std::lock_guard<std::mutex> lock(mutex_);
subscription_ptrs_.push_back(subscription_ptr);
}
void
CallbackGroup::add_timer(const rclcpp::timer::TimerBase::SharedPtr timer_ptr)
{
std::lock_guard<std::mutex> lock(mutex_);
timer_ptrs_.push_back(timer_ptr);
}
void
CallbackGroup::add_service(const rclcpp::service::ServiceBase::SharedPtr service_ptr)
{
std::lock_guard<std::mutex> lock(mutex_);
service_ptrs_.push_back(service_ptr);
}
void
CallbackGroup::add_client(const rclcpp::client::ClientBase::SharedPtr client_ptr)
{
std::lock_guard<std::mutex> lock(mutex_);
client_ptrs_.push_back(client_ptr);
}

View File

@@ -0,0 +1,121 @@
// 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.
#include "rclcpp/client.hpp"
#include <chrono>
#include <cstdio>
#include <memory>
#include <string>
#include "rcl/graph.h"
#include "rcl/node.h"
#include "rcl/wait.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/node_interfaces/node_base_interface.hpp"
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
#include "rclcpp/utilities.hpp"
using rclcpp::client::ClientBase;
using rclcpp::exceptions::InvalidNodeError;
using rclcpp::exceptions::throw_from_rcl_error;
ClientBase::ClientBase(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name)
: node_graph_(node_graph),
node_handle_(node_base->get_shared_rcl_node_handle()),
service_name_(service_name)
{}
ClientBase::~ClientBase() {}
const std::string &
ClientBase::get_service_name() const
{
return this->service_name_;
}
const rcl_client_t *
ClientBase::get_client_handle() const
{
return &client_handle_;
}
bool
ClientBase::service_is_ready() const
{
bool is_ready;
rcl_ret_t ret =
rcl_service_server_is_available(this->get_rcl_node_handle(), &client_handle_, &is_ready);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "rcl_service_server_is_available failed");
}
return is_ready;
}
bool
ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
{
auto start = std::chrono::steady_clock::now();
// make an event to reuse, rather than create a new one each time
auto node_ptr = node_graph_.lock();
if (!node_ptr) {
throw InvalidNodeError();
}
auto event = node_ptr->get_graph_event();
// check to see if the server is ready immediately
if (this->service_is_ready()) {
return true;
}
if (timeout == std::chrono::nanoseconds(0)) {
// check was non-blocking, return immediately
return false;
}
// update the time even on the first loop to account for time spent in the first call
// to this->server_is_ready()
std::chrono::nanoseconds time_to_wait = timeout - (std::chrono::steady_clock::now() - start);
if (timeout > std::chrono::nanoseconds(0) && time_to_wait < std::chrono::nanoseconds(0)) {
// Do not allow the time_to_wait to become negative when timeout was originally positive.
// Setting time_to_wait to 0 will allow one non-blocking wait because of the do-while.
time_to_wait = std::chrono::nanoseconds(0);
}
// continue forever if timeout is negative, otherwise continue until out of time_to_wait
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
do {
if (!rclcpp::utilities::ok()) {
return false;
}
node_ptr->wait_for_graph_change(event, time_to_wait);
event->check_and_clear(); // reset the event
// always check if the service is ready, even if the graph event wasn't triggered
// this is needed to avoid a race condition that is specific to the Connext RMW implementation
// (see https://github.com/ros2/rmw_connext/issues/201)
if (this->service_is_ready()) {
return true;
}
// server is not ready, loop if there is time left
time_to_wait = timeout - (std::chrono::steady_clock::now() - start);
} while (timeout < std::chrono::nanoseconds(0) || time_to_wait > std::chrono::nanoseconds(0));
// *INDENT-ON*
return false; // timeout exceeded while waiting for the server to be ready
}
rcl_node_t *
ClientBase::get_rcl_node_handle() const
{
return node_handle_.get();
}

View File

@@ -0,0 +1,19 @@
// 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.
#include "rclcpp/context.hpp"
using rclcpp::context::Context;
Context::Context() {}

View File

@@ -1,4 +1,4 @@
// Copyright 2014 Open Source Robotics Foundation, Inc.
// 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.
@@ -12,17 +12,16 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <rclcpp/rclcpp.hpp>
#include "rclcpp/contexts/default_context.hpp"
// This forward declaration is implemented by the RCLCPP_REGISTER_NODE macro
RMW_IMPORT rclcpp::Node::SharedPtr create_node();
using rclcpp::contexts::default_context::DefaultContext;
int main(int argc, char ** argv)
DefaultContext::DefaultContext()
{}
DefaultContext::SharedPtr
rclcpp::contexts::default_context::get_global_default_context()
{
rclcpp::init(argc, argv);
rclcpp::executors::SingleThreadedExecutor executor;
rclcpp::Node::SharedPtr node = create_node();
executor.add_node(node);
executor.spin();
return 0;
static DefaultContext::SharedPtr default_context = DefaultContext::make_shared();
return default_context;
}

View File

@@ -0,0 +1,44 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/event.hpp"
namespace rclcpp
{
namespace event
{
Event::Event()
: state_(false) {}
bool
Event::set()
{
return state_.exchange(true);
}
bool
Event::check()
{
return state_.load();
}
bool
Event::check_and_clear()
{
return state_.exchange(false);
}
} // namespace event
} // namespace rclcpp

View File

@@ -0,0 +1,116 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/exceptions.hpp"
#include <cstdio>
#include <functional>
#include <string>
using namespace std::string_literals;
namespace rclcpp
{
namespace exceptions
{
std::string
NameValidationError::format_error(
const char * name_type,
const char * name,
const char * error_msg,
size_t invalid_index)
{
std::string msg = "";
msg += "Invalid "s + name_type + ": " + error_msg + ":\n";
msg += " '"s + name + "'\n";
msg += " "s + std::string(invalid_index, ' ') + "^\n";
return msg;
}
void
throw_from_rcl_error(
rcl_ret_t ret,
const std::string & prefix,
const rcl_error_state_t * error_state,
void (* reset_error)())
{
if (RCL_RET_OK == ret) {
throw std::invalid_argument("ret is RCL_RET_OK");
}
if (!error_state) {
error_state = rcl_get_error_state();
}
if (!error_state) {
throw std::runtime_error("rcl error state is not set");
}
std::string formated_prefix = prefix;
if (!prefix.empty()) {
formated_prefix += ": ";
}
RCLErrorBase base_exc(ret, error_state);
if (reset_error) {
reset_error();
}
switch (ret) {
case RCL_RET_BAD_ALLOC:
throw RCLBadAlloc(base_exc);
case RCL_RET_INVALID_ARGUMENT:
throw RCLInvalidArgument(base_exc, formated_prefix);
default:
throw RCLError(base_exc, formated_prefix);
}
}
RCLErrorBase::RCLErrorBase(rcl_ret_t ret, const rcl_error_state_t * error_state)
: ret(ret), message(error_state->message), file(error_state->file), line(error_state->line_number),
formatted_message(rcl_get_error_string_safe())
{}
RCLError::RCLError(
rcl_ret_t ret,
const rcl_error_state_t * error_state,
const std::string & prefix)
: RCLError(RCLErrorBase(ret, error_state), prefix)
{}
RCLError::RCLError(
const RCLErrorBase & base_exc,
const std::string & prefix)
: RCLErrorBase(base_exc), std::runtime_error(prefix + base_exc.formatted_message)
{}
RCLBadAlloc::RCLBadAlloc(rcl_ret_t ret, const rcl_error_state_t * error_state)
: RCLBadAlloc(RCLErrorBase(ret, error_state))
{}
RCLBadAlloc::RCLBadAlloc(const RCLErrorBase & base_exc)
: RCLErrorBase(base_exc), std::bad_alloc()
{}
RCLInvalidArgument::RCLInvalidArgument(
rcl_ret_t ret,
const rcl_error_state_t * error_state,
const std::string & prefix)
: RCLInvalidArgument(RCLErrorBase(ret, error_state), prefix)
{}
RCLInvalidArgument::RCLInvalidArgument(
const RCLErrorBase & base_exc,
const std::string & prefix)
: RCLErrorBase(base_exc), std::invalid_argument(prefix + base_exc.formatted_message)
{}
} // namespace exceptions
} // namespace rclcpp

View File

@@ -0,0 +1,594 @@
// 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.
#include <algorithm>
#include <memory>
#include <string>
#include <type_traits>
#include "rcl/allocator.h"
#include "rcl/error_handling.h"
#include "rclcpp/executor.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rclcpp/utilities.hpp"
#include "rcl_interfaces/msg/intra_process_message.hpp"
using rclcpp::executor::AnyExecutable;
using rclcpp::executor::Executor;
using rclcpp::executor::ExecutorArgs;
using rclcpp::executor::FutureReturnCode;
Executor::Executor(const ExecutorArgs & args)
: spinning(false),
memory_strategy_(args.memory_strategy)
{
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
if (rcl_guard_condition_init(
&interrupt_guard_condition_, guard_condition_options) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("Failed to create interrupt guard condition in Executor constructor: ") +
rcl_get_error_string_safe());
}
// The number of guard conditions is always at least 2: 1 for the ctrl-c guard cond,
// and one for the executor's guard cond (interrupt_guard_condition_)
// Put the global ctrl-c guard condition in
memory_strategy_->add_guard_condition(rclcpp::utilities::get_sigint_guard_condition(&waitset_));
// Put the executor's guard condition in
memory_strategy_->add_guard_condition(&interrupt_guard_condition_);
rcl_allocator_t allocator = memory_strategy_->get_allocator();
if (rcl_wait_set_init(
&waitset_, 0, 2, 0, 0, 0, allocator) != RCL_RET_OK)
{
fprintf(stderr,
"[rclcpp::error] failed to create waitset: %s\n", rcl_get_error_string_safe());
if (rcl_guard_condition_fini(&interrupt_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
}
throw std::runtime_error("Failed to create waitset in Executor constructor");
}
}
Executor::~Executor()
{
// Finalize the waitset.
if (rcl_wait_set_fini(&waitset_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy waitset: %s\n", rcl_get_error_string_safe());
}
// Finalize the interrupt guard condition.
if (rcl_guard_condition_fini(&interrupt_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
}
// Remove and release the sigint guard condition
memory_strategy_->remove_guard_condition(
rclcpp::utilities::get_sigint_guard_condition(&waitset_));
rclcpp::utilities::release_sigint_guard_condition(&waitset_);
}
void
Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
// If the node already has an executor
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
if (has_executor.exchange(true)) {
throw std::runtime_error("Node has already been added to an executor.");
}
// Check to ensure node not already added
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (node == node_ptr) {
// TODO(jacquelinekay): Use a different error here?
throw std::runtime_error("Cannot add node to executor, node already added.");
}
}
weak_nodes_.push_back(node_ptr);
if (notify) {
// Interrupt waiting to handle new node
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string_safe());
}
}
// Add the node's notify condition to the guard condition handles
memory_strategy_->add_guard_condition(node_ptr->get_notify_guard_condition());
}
void
Executor::add_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify)
{
this->add_node(node_ptr->get_node_base_interface(), notify);
}
void
Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
bool node_removed = false;
weak_nodes_.erase(
std::remove_if(
weak_nodes_.begin(), weak_nodes_.end(),
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
[&](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr & i)
{
bool matched = (i.lock() == node_ptr);
node_removed |= matched;
return matched;
}
// *INDENT-ON*
)
);
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
has_executor.store(false);
if (notify) {
// If the node was matched and removed, interrupt waiting
if (node_removed) {
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string_safe());
}
}
}
memory_strategy_->remove_guard_condition(node_ptr->get_notify_guard_condition());
}
void
Executor::remove_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify)
{
this->remove_node(node_ptr->get_node_base_interface(), notify);
}
void
Executor::spin_node_once_nanoseconds(
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::nanoseconds timeout)
{
this->add_node(node, false);
// non-blocking = true
spin_once(timeout);
this->remove_node(node, false);
}
void
Executor::spin_node_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node)
{
this->add_node(node, false);
spin_some();
this->remove_node(node, false);
}
void
Executor::spin_node_some(std::shared_ptr<rclcpp::node::Node> node)
{
this->spin_node_some(node->get_node_base_interface());
}
void
Executor::spin_some()
{
if (spinning.exchange(true)) {
throw std::runtime_error("spin_some() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
AnyExecutable::SharedPtr any_exec;
while ((any_exec = get_next_executable(std::chrono::milliseconds::zero())) && spinning.load()) {
execute_any_executable(any_exec);
}
}
void
Executor::spin_once(std::chrono::nanoseconds timeout)
{
if (spinning.exchange(true)) {
throw std::runtime_error("spin_once() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
auto any_exec = get_next_executable(timeout);
if (any_exec) {
execute_any_executable(any_exec);
}
}
void
Executor::cancel()
{
spinning.store(false);
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string_safe());
}
}
void
Executor::set_memory_strategy(rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy)
{
if (memory_strategy == nullptr) {
throw std::runtime_error("Received NULL memory strategy in executor.");
}
memory_strategy_ = memory_strategy;
}
void
Executor::execute_any_executable(AnyExecutable::SharedPtr any_exec)
{
if (!any_exec || !spinning.load()) {
return;
}
if (any_exec->timer) {
execute_timer(any_exec->timer);
}
if (any_exec->subscription) {
execute_subscription(any_exec->subscription);
}
if (any_exec->subscription_intra_process) {
execute_intra_process_subscription(any_exec->subscription_intra_process);
}
if (any_exec->service) {
execute_service(any_exec->service);
}
if (any_exec->client) {
execute_client(any_exec->client);
}
// Reset the callback_group, regardless of type
any_exec->callback_group->can_be_taken_from().store(true);
// Wake the wait, because it may need to be recalculated or work that
// was previously blocked is now available.
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(rcl_get_error_string_safe());
}
}
void
Executor::execute_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription)
{
std::shared_ptr<void> message = subscription->create_message();
rmw_message_info_t message_info;
auto ret = rcl_take(subscription->get_subscription_handle(),
message.get(), &message_info);
if (ret == RCL_RET_OK) {
message_info.from_intra_process = false;
subscription->handle_message(message, message_info);
} else if (ret != RCL_RET_SUBSCRIPTION_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take failed for subscription on topic '%s': %s\n",
subscription->get_topic_name(), rcl_get_error_string_safe());
}
subscription->return_message(message);
}
void
Executor::execute_intra_process_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription)
{
rcl_interfaces::msg::IntraProcessMessage ipm;
rmw_message_info_t message_info;
rcl_ret_t status = rcl_take(
subscription->get_intra_process_subscription_handle(),
&ipm,
&message_info);
if (status == RCL_RET_OK) {
message_info.from_intra_process = true;
subscription->handle_intra_process_message(ipm, message_info);
} else if (status != RCL_RET_SUBSCRIPTION_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take failed for intra process subscription on topic '%s': %s\n",
subscription->get_topic_name(), rcl_get_error_string_safe());
}
}
void
Executor::execute_timer(
rclcpp::timer::TimerBase::SharedPtr timer)
{
timer->execute_callback();
}
void
Executor::execute_service(
rclcpp::service::ServiceBase::SharedPtr service)
{
auto request_header = service->create_request_header();
std::shared_ptr<void> request = service->create_request();
rcl_ret_t status = rcl_take_request(
service->get_service_handle(),
request_header.get(),
request.get());
if (status == RCL_RET_OK) {
service->handle_request(request_header, request);
} else if (status != RCL_RET_SERVICE_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take request failed for server of service '%s': %s\n",
service->get_service_name().c_str(), rcl_get_error_string_safe());
}
}
void
Executor::execute_client(
rclcpp::client::ClientBase::SharedPtr client)
{
auto request_header = client->create_request_header();
std::shared_ptr<void> response = client->create_response();
rcl_ret_t status = rcl_take_response(
client->get_client_handle(),
request_header.get(),
response.get());
if (status == RCL_RET_OK) {
client->handle_response(request_header, response);
} else if (status != RCL_RET_SERVICE_TAKE_FAILED) {
fprintf(stderr,
"[rclcpp::error] take response failed for client of service '%s': %s\n",
client->get_service_name().c_str(), rcl_get_error_string_safe());
}
}
void
Executor::wait_for_work(std::chrono::nanoseconds timeout)
{
// Collect the subscriptions and timers to be waited on
memory_strategy_->clear_handles();
bool has_invalid_weak_nodes = memory_strategy_->collect_entities(weak_nodes_);
// Clean up any invalid nodes, if they were detected
if (has_invalid_weak_nodes) {
weak_nodes_.erase(
remove_if(
weak_nodes_.begin(), weak_nodes_.end(),
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
[](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr i)
{
return i.expired();
}
// *INDENT-ON*
)
);
}
if (rcl_wait_set_resize_subscriptions(
&waitset_, memory_strategy_->number_of_ready_subscriptions()) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("Couldn't resize the number of subscriptions in waitset : ") +
rcl_get_error_string_safe());
}
if (rcl_wait_set_resize_services(
&waitset_, memory_strategy_->number_of_ready_services()) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("Couldn't resize the number of services in waitset : ") +
rcl_get_error_string_safe());
}
if (rcl_wait_set_resize_clients(
&waitset_, memory_strategy_->number_of_ready_clients()) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("Couldn't resize the number of clients in waitset : ") +
rcl_get_error_string_safe());
}
if (rcl_wait_set_resize_guard_conditions(
&waitset_, memory_strategy_->number_of_guard_conditions()) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("Couldn't resize the number of guard_conditions in waitset : ") +
rcl_get_error_string_safe());
}
if (rcl_wait_set_resize_timers(
&waitset_, memory_strategy_->number_of_ready_timers()) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("Couldn't resize the number of timers in waitset : ") +
rcl_get_error_string_safe());
}
if (!memory_strategy_->add_handles_to_waitset(&waitset_)) {
throw std::runtime_error("Couldn't fill waitset");
}
rcl_ret_t status =
rcl_wait(&waitset_, std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count());
if (status == RCL_RET_WAIT_SET_EMPTY) {
fprintf(stderr, "Warning: empty waitset received in rcl_wait(). This should never happen.\n");
} else if (status != RCL_RET_OK && status != RCL_RET_TIMEOUT) {
throw std::runtime_error(std::string("rcl_wait() failed: ") + rcl_get_error_string_safe());
}
// check the null handles in the waitset and remove them from the handles in memory strategy
// for callback-based entities
memory_strategy_->remove_null_handles(&waitset_);
if (rcl_wait_set_clear_subscriptions(&waitset_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear subscriptions from waitset");
}
if (rcl_wait_set_clear_services(&waitset_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear servicess from waitset");
}
if (rcl_wait_set_clear_clients(&waitset_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear clients from waitset");
}
if (rcl_wait_set_clear_guard_conditions(&waitset_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear guard conditions from waitset");
}
if (rcl_wait_set_clear_timers(&waitset_) != RCL_RET_OK) {
throw std::runtime_error("Couldn't clear timers from waitset");
}
}
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
Executor::get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
if (!group) {
return nullptr;
}
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto callback_group = weak_group.lock();
if (callback_group == group) {
return node;
}
}
}
return nullptr;
}
rclcpp::callback_group::CallbackGroup::SharedPtr
Executor::get_group_by_timer(rclcpp::timer::TimerBase::SharedPtr timer)
{
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & weak_timer : group->get_timer_ptrs()) {
auto t = weak_timer.lock();
if (t == timer) {
return group;
}
}
}
}
return rclcpp::callback_group::CallbackGroup::SharedPtr();
}
void
Executor::get_next_timer(AnyExecutable::SharedPtr any_exec)
{
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group || !group->can_be_taken_from().load()) {
continue;
}
for (auto & timer_ref : group->get_timer_ptrs()) {
auto timer = timer_ref.lock();
if (timer && timer->is_ready()) {
any_exec->timer = timer;
any_exec->callback_group = group;
node = get_node_by_group(group);
return;
}
}
}
}
}
AnyExecutable::SharedPtr
Executor::get_next_ready_executable()
{
auto any_exec = memory_strategy_->instantiate_next_executable();
// Check the timers to see if there are any that are ready, if so return
get_next_timer(any_exec);
if (any_exec->timer) {
return any_exec;
}
// Check the subscriptions to see if there are any that are ready
memory_strategy_->get_next_subscription(any_exec, weak_nodes_);
if (any_exec->subscription || any_exec->subscription_intra_process) {
return any_exec;
}
// Check the services to see if there are any that are ready
memory_strategy_->get_next_service(any_exec, weak_nodes_);
if (any_exec->service) {
return any_exec;
}
// Check the clients to see if there are any that are ready
memory_strategy_->get_next_client(any_exec, weak_nodes_);
if (any_exec->client) {
return any_exec;
}
// If there is no ready executable, return a null ptr
return nullptr;
}
AnyExecutable::SharedPtr
Executor::get_next_executable(std::chrono::nanoseconds timeout)
{
// Check to see if there are any subscriptions or timers needing service
// TODO(wjwwood): improve run to run efficiency of this function
auto any_exec = get_next_ready_executable();
// If there are none
if (!any_exec) {
// Wait for subscriptions or timers to work on
wait_for_work(timeout);
if (!spinning.load()) {
return nullptr;
}
// Try again
any_exec = get_next_ready_executable();
}
// At this point any_exec should be valid with either a valid subscription
// or a valid timer, or it should be a null shared_ptr
if (any_exec) {
// If it is valid, check to see if the group is mutually exclusive or
// not, then mark it accordingly
if (any_exec->callback_group && any_exec->callback_group->type() == \
callback_group::CallbackGroupType::MutuallyExclusive)
{
// It should not have been taken otherwise
assert(any_exec->callback_group->can_be_taken_from().load());
// Set to false to indicate something is being run from this group
// This is reset to true either when the any_exec is executed or when the
// any_exec is destructued
any_exec->callback_group->can_be_taken_from().store(false);
}
}
return any_exec;
}
std::ostream &
rclcpp::executor::operator<<(std::ostream & os, const FutureReturnCode & future_return_code)
{
return os << to_string(future_return_code);
}
std::string
rclcpp::executor::to_string(const FutureReturnCode & future_return_code)
{
using enum_type = std::underlying_type<FutureReturnCode>::type;
std::string prefix = "Unknown enum value (";
std::string ret_as_string = std::to_string(static_cast<enum_type>(future_return_code));
switch (future_return_code) {
case FutureReturnCode::SUCCESS:
prefix = "SUCCESS (";
break;
case FutureReturnCode::INTERRUPTED:
prefix = "INTERRUPTED (";
break;
case FutureReturnCode::TIMEOUT:
prefix = "TIMEOUT (";
break;
}
return prefix + ret_as_string + ")";
}

View File

@@ -0,0 +1,43 @@
// 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.
#include "rclcpp/executors.hpp"
void
rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
rclcpp::executors::SingleThreadedExecutor exec;
exec.spin_node_some(node_ptr);
}
void
rclcpp::spin_some(rclcpp::node::Node::SharedPtr node_ptr)
{
rclcpp::spin_some(node_ptr->get_node_base_interface());
}
void
rclcpp::spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
{
rclcpp::executors::SingleThreadedExecutor exec;
exec.add_node(node_ptr);
exec.spin();
exec.remove_node(node_ptr);
}
void
rclcpp::spin(rclcpp::node::Node::SharedPtr node_ptr)
{
rclcpp::spin(node_ptr->get_node_base_interface());
}

View File

@@ -0,0 +1,80 @@
// 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.
#include "rclcpp/executors/multi_threaded_executor.hpp"
#include <chrono>
#include <functional>
#include <vector>
#include "rclcpp/utilities.hpp"
#include "rclcpp/scope_exit.hpp"
using rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor;
MultiThreadedExecutor::MultiThreadedExecutor(const rclcpp::executor::ExecutorArgs & args)
: executor::Executor(args)
{
number_of_threads_ = std::thread::hardware_concurrency();
if (number_of_threads_ == 0) {
number_of_threads_ = 1;
}
}
MultiThreadedExecutor::~MultiThreadedExecutor() {}
void
MultiThreadedExecutor::spin()
{
if (spinning.exchange(true)) {
throw std::runtime_error("spin() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
std::vector<std::thread> threads;
size_t thread_id = 0;
{
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
for (; thread_id < number_of_threads_ - 1; ++thread_id) {
auto func = std::bind(&MultiThreadedExecutor::run, this, thread_id);
threads.emplace_back(func);
}
}
run(thread_id);
for (auto & thread : threads) {
thread.join();
}
}
size_t
MultiThreadedExecutor::get_number_of_threads()
{
return number_of_threads_;
}
void
MultiThreadedExecutor::run(size_t)
{
while (rclcpp::utilities::ok() && spinning.load()) {
executor::AnyExecutable::SharedPtr any_exec;
{
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
if (!rclcpp::utilities::ok() || !spinning.load()) {
return;
}
any_exec = get_next_executable();
}
execute_any_executable(any_exec);
}
}

View File

@@ -0,0 +1,36 @@
// 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.
#include "rclcpp/executors/single_threaded_executor.hpp"
#include "rclcpp/scope_exit.hpp"
using rclcpp::executors::single_threaded_executor::SingleThreadedExecutor;
SingleThreadedExecutor::SingleThreadedExecutor(const rclcpp::executor::ExecutorArgs & args)
: executor::Executor(args) {}
SingleThreadedExecutor::~SingleThreadedExecutor() {}
void
SingleThreadedExecutor::spin()
{
if (spinning.exchange(true)) {
throw std::runtime_error("spin() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
while (rclcpp::utilities::ok() && spinning.load()) {
auto any_exec = get_next_executable();
execute_any_executable(any_exec);
}
}

View File

@@ -0,0 +1,196 @@
// Copyright 2017 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/expand_topic_or_service_name.hpp"
#include <string>
#include "rcl/expand_topic_name.h"
#include "rcl/validate_topic_name.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rcutils/types/string_map.h"
#include "rmw/error_handling.h"
#include "rmw/validate_namespace.h"
#include "rmw/validate_node_name.h"
#include "rmw/validate_full_topic_name.h"
using rclcpp::exceptions::throw_from_rcl_error;
std::string
rclcpp::expand_topic_or_service_name(
const std::string & name,
const std::string & node_name,
const std::string & namespace_,
bool is_service)
{
char * expanded_topic = nullptr;
rcl_allocator_t allocator = rcl_get_default_allocator();
rcutils_allocator_t rcutils_allocator = rcutils_get_default_allocator();
rcutils_string_map_t substitutions_map = rcutils_get_zero_initialized_string_map();
rcutils_ret_t rcutils_ret = rcutils_string_map_init(&substitutions_map, 0, rcutils_allocator);
if (rcutils_ret != RCUTILS_RET_OK) {
if (rcutils_ret == RCUTILS_RET_BAD_ALLOC) {
throw_from_rcl_error(RCL_RET_BAD_ALLOC, "", rcutils_get_error_state(), rcutils_reset_error);
} else {
throw_from_rcl_error(RCL_RET_ERROR, "", rcutils_get_error_state(), rcutils_reset_error);
}
}
rcl_ret_t ret = rcl_get_default_topic_name_substitutions(&substitutions_map);
if (ret != RCL_RET_OK) {
rcutils_error_state_t error_state;
if (rcutils_error_state_copy(rcl_get_error_state(), &error_state) != RCUTILS_RET_OK) {
throw std::bad_alloc();
}
auto error_state_scope_exit = rclcpp::make_scope_exit([&error_state]() {
rcutils_error_state_fini(&error_state);
});
// finalize the string map before throwing
rcutils_ret = rcutils_string_map_fini(&substitutions_map);
if (rcutils_ret != RCUTILS_RET_OK) {
fprintf(stderr,
"[rclcpp|" RCUTILS_STRINGIFY(__FILE__) ":" RCUTILS_STRINGIFY(__LINE__) "]: "
"failed to fini string_map (%d) during error handling: %s\n",
rcutils_ret,
rcutils_get_error_string_safe());
rcutils_reset_error();
}
throw_from_rcl_error(ret, "", &error_state);
}
ret = rcl_expand_topic_name(
name.c_str(),
node_name.c_str(),
namespace_.c_str(),
&substitutions_map,
allocator,
&expanded_topic);
std::string result;
if (ret == RCL_RET_OK) {
result = expanded_topic;
allocator.deallocate(expanded_topic, allocator.state);
}
rcutils_ret = rcutils_string_map_fini(&substitutions_map);
if (rcutils_ret != RCUTILS_RET_OK) {
throw_from_rcl_error(RCL_RET_ERROR, "", rcutils_get_error_state(), rcutils_reset_error);
}
// expansion failed
if (ret != RCL_RET_OK) {
// if invalid topic or unknown substitution
if (ret == RCL_RET_TOPIC_NAME_INVALID || ret == RCL_RET_UNKNOWN_SUBSTITUTION) {
rcl_reset_error(); // explicitly discard error from rcl_expand_topic_name()
int validation_result;
size_t invalid_index;
rcl_ret_t ret = rcl_validate_topic_name(name.c_str(), &validation_result, &invalid_index);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret);
}
if (validation_result == RCL_TOPIC_NAME_VALID) {
throw std::runtime_error("topic name unexpectedly valid");
}
const char * validation_message = rcl_topic_name_validation_result_string(validation_result);
if (!validation_message) {
throw std::runtime_error("unable to get validation error message");
}
if (is_service) {
using rclcpp::exceptions::InvalidServiceNameError;
throw InvalidServiceNameError(name.c_str(), validation_message, invalid_index);
} else {
using rclcpp::exceptions::InvalidTopicNameError;
throw InvalidTopicNameError(name.c_str(), validation_message, invalid_index);
}
// if invalid node name
} else if (ret == RCL_RET_NODE_INVALID_NAME) {
rcl_reset_error(); // explicitly discard error from rcl_expand_topic_name()
int validation_result;
size_t invalid_index;
rmw_ret_t rmw_ret =
rmw_validate_node_name(node_name.c_str(), &validation_result, &invalid_index);
if (rmw_ret != RMW_RET_OK) {
if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
throw_from_rcl_error(
RCL_RET_INVALID_ARGUMENT, "failed to validate node name",
rmw_get_error_state(), rmw_reset_error);
}
throw_from_rcl_error(
RCL_RET_ERROR, "failed to validate node name",
rmw_get_error_state(), rmw_reset_error);
}
throw rclcpp::exceptions::InvalidNodeNameError(
node_name.c_str(),
rmw_node_name_validation_result_string(validation_result),
invalid_index);
// if invalid namespace
} else if (ret == RCL_RET_NODE_INVALID_NAMESPACE) {
rcl_reset_error(); // explicitly discard error from rcl_expand_topic_name()
int validation_result;
size_t invalid_index;
rmw_ret_t rmw_ret =
rmw_validate_namespace(namespace_.c_str(), &validation_result, &invalid_index);
if (rmw_ret != RMW_RET_OK) {
if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
throw_from_rcl_error(
RCL_RET_INVALID_ARGUMENT, "failed to validate namespace",
rmw_get_error_state(), rmw_reset_error);
}
throw_from_rcl_error(
RCL_RET_ERROR, "failed to validate namespace",
rmw_get_error_state(), rmw_reset_error);
}
throw rclcpp::exceptions::InvalidNamespaceError(
namespace_.c_str(),
rmw_namespace_validation_result_string(validation_result),
invalid_index);
// something else happened
} else {
throw_from_rcl_error(ret);
}
}
// expand succeeded, but full name validation may fail still
int validation_result;
size_t invalid_index;
rmw_ret_t rmw_ret =
rmw_validate_full_topic_name(result.c_str(), &validation_result, &invalid_index);
if (rmw_ret != RMW_RET_OK) {
if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
throw_from_rcl_error(
RCL_RET_INVALID_ARGUMENT, "failed to validate full topic name",
rmw_get_error_state(), rmw_reset_error);
}
throw_from_rcl_error(
RCL_RET_ERROR, "failed to validate full topic name",
rmw_get_error_state(), rmw_reset_error);
}
if (validation_result != RMW_TOPIC_VALID) {
if (is_service) {
throw rclcpp::exceptions::InvalidServiceNameError(
result.c_str(),
rmw_full_topic_name_validation_result_string(validation_result),
invalid_index);
} else {
throw rclcpp::exceptions::InvalidTopicNameError(
result.c_str(),
rmw_full_topic_name_validation_result_string(validation_result),
invalid_index);
}
}
return result;
}

View File

@@ -0,0 +1,356 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/graph_listener.hpp"
#include <cstdio>
#include <exception>
#include <memory>
#include <string>
#include <vector>
#include "rcl/error_handling.h"
#include "rcl/types.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/node.hpp"
#include "rmw/impl/cpp/demangle.hpp"
using rclcpp::exceptions::throw_from_rcl_error;
namespace rclcpp
{
namespace graph_listener
{
GraphListener::GraphListener()
: is_started_(false), is_shutdown_(false), shutdown_guard_condition_(nullptr)
{
rcl_ret_t ret = rcl_guard_condition_init(
&interrupt_guard_condition_,
rcl_guard_condition_get_default_options());
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
}
shutdown_guard_condition_ = rclcpp::utilities::get_sigint_guard_condition(&wait_set_);
}
GraphListener::~GraphListener()
{
this->shutdown();
}
void
GraphListener::start_if_not_started()
{
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (is_shutdown_.load()) {
throw GraphListenerShutdownError();
}
if (!is_started_) {
// Initialize the wait set before starting.
rcl_ret_t ret = rcl_wait_set_init(
&wait_set_,
0, // number_of_subscriptions
2, // number_of_guard_conditions
0, // number_of_timers
0, // number_of_clients
0, // number_of_services
rcl_get_default_allocator());
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to initialize wait set");
}
// Register an on_shutdown hook to shtudown the graph listener.
// This is important to ensure that the wait set is finalized before
// destruction of static objects occurs.
std::weak_ptr<GraphListener> weak_this = shared_from_this();
rclcpp::utilities::on_shutdown([weak_this]() {
auto shared_this = weak_this.lock();
if (shared_this) {
shared_this->shutdown();
}
});
// Start the listener thread.
listener_thread_ = std::thread(&GraphListener::run, this);
is_started_ = true;
}
}
void
GraphListener::run()
{
try {
run_loop();
} catch (const std::exception & exc) {
fprintf(stderr,
"[rclcpp] caught %s exception in GraphListener thread: %s\n",
rmw::impl::cpp::demangle(exc).c_str(),
exc.what());
std::rethrow_exception(std::current_exception());
} catch (...) {
fprintf(stderr, "[rclcpp] unknown error in GraphListener thread\n");
std::rethrow_exception(std::current_exception());
}
}
void
GraphListener::run_loop()
{
while (true) {
// If shutdown() was called, exit.
if (is_shutdown_.load()) {
return;
}
rcl_ret_t ret;
{
// This "barrier" lock ensures that other functions can acquire the
// node_graph_interfaces_mutex_ after waking up rcl_wait.
std::lock_guard<std::mutex> nodes_barrier_lock(node_graph_interfaces_barrier_mutex_);
// This is ownership is passed to nodes_lock in the next line.
node_graph_interfaces_mutex_.lock();
}
// This lock is released when the loop continues or exits.
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
// Resize the wait set if necessary.
if (wait_set_.size_of_guard_conditions < (node_graph_interfaces_.size() + 2)) {
ret = rcl_wait_set_resize_guard_conditions(&wait_set_, node_graph_interfaces_.size() + 2);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to resize wait set");
}
}
// Clear the wait set's guard conditions.
ret = rcl_wait_set_clear_guard_conditions(&wait_set_);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to clear wait set");
}
// Put the interrupt guard condition in the wait set.
ret = rcl_wait_set_add_guard_condition(&wait_set_, &interrupt_guard_condition_);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to add interrupt guard condition to wait set");
}
// Put the shutdown guard condition in the wait set.
ret = rcl_wait_set_add_guard_condition(&wait_set_, shutdown_guard_condition_);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to add shutdown guard condition to wait set");
}
// Put graph guard conditions for each node into the wait set.
for (const auto node_ptr : node_graph_interfaces_) {
// Only wait on graph changes if some user of the node is watching.
if (node_ptr->count_graph_users() == 0) {
continue;
}
// Add the graph guard condition for the node to the wait set.
auto graph_gc = node_ptr->get_graph_guard_condition();
if (!graph_gc) {
throw_from_rcl_error(RCL_RET_ERROR, "failed to get graph guard condition");
}
ret = rcl_wait_set_add_guard_condition(&wait_set_, graph_gc);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to add graph guard condition to wait set");
}
}
// Wait for: graph changes, interrupt, or shutdown/SIGINT
ret = rcl_wait(&wait_set_, -1); // block for ever until a guard condition is triggered
if (RCL_RET_TIMEOUT == ret) {
throw std::runtime_error("rcl_wait unexpectedly timed out");
}
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to wait on wait set");
}
bool shutdown_guard_condition_triggered = false;
// Check to see if the shutdown guard condition has been triggered.
for (size_t i = 0; i < wait_set_.size_of_guard_conditions; ++i) {
if (shutdown_guard_condition_ == wait_set_.guard_conditions[i]) {
shutdown_guard_condition_triggered = true;
}
}
// Notify nodes who's guard conditions are set (triggered).
for (const auto node_ptr : node_graph_interfaces_) {
auto graph_gc = node_ptr->get_graph_guard_condition();
if (!graph_gc) {
throw_from_rcl_error(RCL_RET_ERROR, "failed to get graph guard condition");
}
for (size_t i = 0; i < wait_set_.size_of_guard_conditions; ++i) {
if (graph_gc == wait_set_.guard_conditions[i]) {
node_ptr->notify_graph_change();
}
}
if (shutdown_guard_condition_triggered) {
// If shutdown, then notify the node of this as well.
node_ptr->notify_shutdown();
}
}
} // while (true)
}
static void
interrupt_(rcl_guard_condition_t * interrupt_guard_condition)
{
rcl_ret_t ret = rcl_trigger_guard_condition(interrupt_guard_condition);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to trigger the interrupt guard condition");
}
}
static void
acquire_nodes_lock_(
std::mutex * node_graph_interfaces_barrier_mutex,
std::mutex * node_graph_interfaces_mutex,
rcl_guard_condition_t * interrupt_guard_condition)
{
{
// Acquire this lock to prevent the run loop from re-locking the
// nodes_mutext_ after being woken up.
std::lock_guard<std::mutex> nodes_barrier_lock(*node_graph_interfaces_barrier_mutex);
// Trigger the interrupt guard condition to wake up rcl_wait.
interrupt_(interrupt_guard_condition);
node_graph_interfaces_mutex->lock();
}
}
static bool
has_node_(
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> * node_graph_interfaces,
rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
for (const auto node_ptr : (*node_graph_interfaces)) {
if (node_graph == node_ptr) {
return true;
}
}
return false;
}
bool
GraphListener::has_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
if (!node_graph) {
return false;
}
// Acquire the nodes mutex using the barrier to prevent the run loop from
// re-locking the nodes mutex after being interrupted.
acquire_nodes_lock_(
&node_graph_interfaces_barrier_mutex_,
&node_graph_interfaces_mutex_,
&interrupt_guard_condition_);
// Store the now acquired node_graph_interfaces_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
return has_node_(&node_graph_interfaces_, node_graph);
}
void
GraphListener::add_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
if (!node_graph) {
throw std::invalid_argument("node is nullptr");
}
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (is_shutdown_.load()) {
throw GraphListenerShutdownError();
}
// Acquire the nodes mutex using the barrier to prevent the run loop from
// re-locking the nodes mutex after being interrupted.
acquire_nodes_lock_(
&node_graph_interfaces_barrier_mutex_,
&node_graph_interfaces_mutex_,
&interrupt_guard_condition_);
// Store the now acquired node_graph_interfaces_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
if (has_node_(&node_graph_interfaces_, node_graph)) {
throw NodeAlreadyAddedError();
}
node_graph_interfaces_.push_back(node_graph);
// The run loop has already been interrupted by acquire_nodes_lock_() and
// will evaluate the new node when nodes_lock releases the node_graph_interfaces_mutex_.
}
static void
remove_node_(
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> * node_graph_interfaces,
rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
// Remove the node if it is found.
for (auto it = node_graph_interfaces->begin(); it != node_graph_interfaces->end(); ++it) {
if (node_graph == *it) {
// Found the node, remove it.
node_graph_interfaces->erase(it);
// Now trigger the interrupt guard condition to make sure
return;
}
}
// Not found in the loop.
throw NodeNotFoundError();
}
void
GraphListener::remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
if (!node_graph) {
throw std::invalid_argument("node is nullptr");
}
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (is_shutdown()) {
// If shutdown, then the run loop has been joined, so we can remove them directly.
return remove_node_(&node_graph_interfaces_, node_graph);
}
// Otherwise, first interrupt and lock against the run loop to safely remove the node.
// Acquire the nodes mutex using the barrier to prevent the run loop from
// re-locking the nodes mutex after being interrupted.
acquire_nodes_lock_(
&node_graph_interfaces_barrier_mutex_,
&node_graph_interfaces_mutex_,
&interrupt_guard_condition_);
// Store the now acquired node_graph_interfaces_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
remove_node_(&node_graph_interfaces_, node_graph);
}
void
GraphListener::shutdown()
{
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
if (!is_shutdown_.exchange(true)) {
if (is_started_) {
interrupt_(&interrupt_guard_condition_);
listener_thread_.join();
}
rcl_ret_t ret = rcl_guard_condition_fini(&interrupt_guard_condition_);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to finalize interrupt guard condition");
}
if (shutdown_guard_condition_) {
rclcpp::utilities::release_sigint_guard_condition(&wait_set_);
shutdown_guard_condition_ = nullptr;
}
if (is_started_) {
ret = rcl_wait_set_fini(&wait_set_);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to finalize wait set");
}
}
}
}
bool
GraphListener::is_shutdown()
{
return is_shutdown_.load();
}
} // namespace graph_listener
} // namespace rclcpp

View File

@@ -0,0 +1,82 @@
// 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.
#include "rclcpp/intra_process_manager.hpp"
namespace rclcpp
{
namespace intra_process_manager
{
static std::atomic<uint64_t> _next_unique_id {1};
IntraProcessManager::IntraProcessManager(
rclcpp::intra_process_manager::IntraProcessManagerImplBase::SharedPtr impl)
: impl_(impl)
{}
IntraProcessManager::~IntraProcessManager()
{}
uint64_t
IntraProcessManager::add_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription)
{
auto id = IntraProcessManager::get_next_unique_id();
impl_->add_subscription(id, subscription);
return id;
}
void
IntraProcessManager::remove_subscription(uint64_t intra_process_subscription_id)
{
impl_->remove_subscription(intra_process_subscription_id);
}
void
IntraProcessManager::remove_publisher(uint64_t intra_process_publisher_id)
{
impl_->remove_publisher(intra_process_publisher_id);
}
bool
IntraProcessManager::matches_any_publishers(const rmw_gid_t * id) const
{
return impl_->matches_any_publishers(id);
}
uint64_t
IntraProcessManager::get_next_unique_id()
{
auto next_id = _next_unique_id.fetch_add(1, std::memory_order_relaxed);
// Check for rollover (we started at 1).
if (0 == next_id) {
// This puts a technical limit on the number of times you can add a publisher or subscriber.
// But even if you could add (and remove) them at 1 kHz (very optimistic rate)
// it would still be a very long time before you could exhaust the pool of id's:
// 2^64 / 1000 times per sec / 60 sec / 60 min / 24 hours / 365 days = 584,942,417 years
// So around 585 million years. Even at 1 GHz, it would take 585 years.
// I think it's safe to avoid trying to handle overflow.
// If we roll over then it's most likely a bug.
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::overflow_error(
"exhausted the unique id's for publishers and subscribers in this process "
"(congratulations your computer is either extremely fast or extremely old)");
// *INDENT-ON*
}
return next_id;
}
} // namespace intra_process_manager
} // namespace rclcpp

View File

@@ -0,0 +1,23 @@
// 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.
#include "rclcpp/intra_process_manager_impl.hpp"
#include <memory>
rclcpp::intra_process_manager::IntraProcessManagerImplBase::SharedPtr
rclcpp::intra_process_manager::create_default_impl()
{
return std::make_shared<IntraProcessManagerImpl<>>();
}

View File

@@ -0,0 +1,27 @@
// 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.
#include "rclcpp/memory_strategies.hpp"
#include <memory>
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
using rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy;
rclcpp::memory_strategy::MemoryStrategy::SharedPtr
rclcpp::memory_strategies::create_default_strategy()
{
return std::make_shared<AllocatorMemoryStrategy<>>();
}

View File

@@ -0,0 +1,197 @@
// 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.
#include "rclcpp/memory_strategy.hpp"
using rclcpp::memory_strategy::MemoryStrategy;
rclcpp::subscription::SubscriptionBase::SharedPtr
MemoryStrategy::get_subscription_by_handle(
const rcl_subscription_t * subscriber_handle, const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & weak_subscription : group->get_subscription_ptrs()) {
auto subscription = weak_subscription.lock();
if (subscription) {
if (subscription->get_subscription_handle() == subscriber_handle) {
return subscription;
}
if (subscription->get_intra_process_subscription_handle() == subscriber_handle) {
return subscription;
}
}
}
}
}
return nullptr;
}
rclcpp::service::ServiceBase::SharedPtr
MemoryStrategy::get_service_by_handle(const rcl_service_t * service_handle,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & weak_service : group->get_service_ptrs()) {
auto service = weak_service.lock();
if (service && service->get_service_handle() == service_handle) {
return service;
}
}
}
}
return nullptr;
}
rclcpp::client::ClientBase::SharedPtr
MemoryStrategy::get_client_by_handle(const rcl_client_t * client_handle,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & weak_client : group->get_client_ptrs()) {
auto client = weak_client.lock();
if (client && client->get_client_handle() == client_handle) {
return client;
}
}
}
}
return nullptr;
}
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
MemoryStrategy::get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group,
const WeakNodeVector & weak_nodes)
{
if (!group) {
return nullptr;
}
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto callback_group = weak_group.lock();
if (callback_group == group) {
return node;
}
}
}
return nullptr;
}
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & weak_sub : group->get_subscription_ptrs()) {
auto sub = weak_sub.lock();
if (sub == subscription) {
return group;
}
}
}
}
return nullptr;
}
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_service(
rclcpp::service::ServiceBase::SharedPtr service,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & weak_serv : group->get_service_ptrs()) {
auto serv = weak_serv.lock();
if (serv && serv == service) {
return group;
}
}
}
}
return nullptr;
}
rclcpp::callback_group::CallbackGroup::SharedPtr
MemoryStrategy::get_group_by_client(
rclcpp::client::ClientBase::SharedPtr client,
const WeakNodeVector & weak_nodes)
{
for (auto & weak_node : weak_nodes) {
auto node = weak_node.lock();
if (!node) {
continue;
}
for (auto & weak_group : node->get_callback_groups()) {
auto group = weak_group.lock();
if (!group) {
continue;
}
for (auto & weak_client : group->get_client_ptrs()) {
auto cli = weak_client.lock();
if (cli && cli == client) {
return group;
}
}
}
}
return nullptr;
}

225
rclcpp/src/rclcpp/node.cpp Normal file
View File

@@ -0,0 +1,225 @@
// 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.
#include <algorithm>
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/graph_listener.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/node_interfaces/node_base.hpp"
#include "rclcpp/node_interfaces/node_graph.hpp"
#include "rclcpp/node_interfaces/node_parameters.hpp"
#include "rclcpp/node_interfaces/node_services.hpp"
#include "rclcpp/node_interfaces/node_timers.hpp"
#include "rclcpp/node_interfaces/node_topics.hpp"
using rclcpp::node::Node;
using rclcpp::exceptions::throw_from_rcl_error;
Node::Node(
const std::string & node_name,
const std::string & namespace_,
bool use_intra_process_comms)
: Node(
node_name,
namespace_,
rclcpp::contexts::default_context::get_global_default_context(),
use_intra_process_comms)
{}
Node::Node(
const std::string & node_name,
const std::string & namespace_,
rclcpp::context::Context::SharedPtr context,
bool use_intra_process_comms)
: node_base_(new rclcpp::node_interfaces::NodeBase(node_name, namespace_, context)),
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
node_timers_(new rclcpp::node_interfaces::NodeTimers(node_base_.get())),
node_topics_(new rclcpp::node_interfaces::NodeTopics(node_base_.get())),
node_services_(new rclcpp::node_interfaces::NodeServices(node_base_.get())),
node_parameters_(new rclcpp::node_interfaces::NodeParameters(
node_topics_.get(),
use_intra_process_comms
)),
use_intra_process_comms_(use_intra_process_comms)
{
}
Node::~Node()
{}
const char *
Node::get_name() const
{
return node_base_->get_name();
}
const char *
Node::get_namespace() const
{
return node_base_->get_namespace();
}
rclcpp::callback_group::CallbackGroup::SharedPtr
Node::create_callback_group(
rclcpp::callback_group::CallbackGroupType group_type)
{
return node_base_->create_callback_group(group_type);
}
bool
Node::group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
return node_base_->callback_group_in_node(group);
}
std::vector<rcl_interfaces::msg::SetParametersResult>
Node::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
return node_parameters_->set_parameters(parameters);
}
rcl_interfaces::msg::SetParametersResult
Node::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
return node_parameters_->set_parameters_atomically(parameters);
}
std::vector<rclcpp::parameter::ParameterVariant>
Node::get_parameters(
const std::vector<std::string> & names) const
{
return node_parameters_->get_parameters(names);
}
rclcpp::parameter::ParameterVariant
Node::get_parameter(const std::string & name) const
{
return node_parameters_->get_parameter(name);
}
bool Node::get_parameter(const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const
{
return node_parameters_->get_parameter(name, parameter);
}
std::vector<rcl_interfaces::msg::ParameterDescriptor>
Node::describe_parameters(
const std::vector<std::string> & names) const
{
return node_parameters_->describe_parameters(names);
}
std::vector<uint8_t>
Node::get_parameter_types(
const std::vector<std::string> & names) const
{
return node_parameters_->get_parameter_types(names);
}
rcl_interfaces::msg::ListParametersResult
Node::list_parameters(
const std::vector<std::string> & prefixes, uint64_t depth) const
{
return node_parameters_->list_parameters(prefixes, depth);
}
std::map<std::string, std::vector<std::string>>
Node::get_topic_names_and_types() const
{
return node_graph_->get_topic_names_and_types();
}
std::map<std::string, std::vector<std::string>>
Node::get_service_names_and_types() const
{
return node_graph_->get_service_names_and_types();
}
size_t
Node::count_publishers(const std::string & topic_name) const
{
return node_graph_->count_publishers(topic_name);
}
size_t
Node::count_subscribers(const std::string & topic_name) const
{
return node_graph_->count_subscribers(topic_name);
}
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
Node::get_callback_groups() const
{
return node_base_->get_callback_groups();
}
rclcpp::event::Event::SharedPtr
Node::get_graph_event()
{
return node_graph_->get_graph_event();
}
void
Node::wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout)
{
node_graph_->wait_for_graph_change(event, timeout);
}
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
Node::get_node_base_interface()
{
return node_base_;
}
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
Node::get_node_graph_interface()
{
return node_graph_;
}
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
Node::get_node_timers_interface()
{
return node_timers_;
}
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr
Node::get_node_topics_interface()
{
return node_topics_;
}
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
Node::get_node_services_interface()
{
return node_services_;
}
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
Node::get_node_parameters_interface()
{
return node_parameters_;
}

View File

@@ -0,0 +1,260 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <string>
#include <limits>
#include <memory>
#include <vector>
#include "rclcpp/node_interfaces/node_base.hpp"
#include "rclcpp/exceptions.hpp"
#include "rmw/validate_node_name.h"
#include "rmw/validate_namespace.h"
using rclcpp::exceptions::throw_from_rcl_error;
using rclcpp::node_interfaces::NodeBase;
NodeBase::NodeBase(
const std::string & node_name,
const std::string & namespace_,
rclcpp::context::Context::SharedPtr context)
: context_(context),
node_handle_(nullptr),
default_callback_group_(nullptr),
associated_with_executor_(false),
notify_guard_condition_is_valid_(false)
{
// Setup the guard condition that is notified when changes occur in the graph.
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
rcl_ret_t ret = rcl_guard_condition_init(&notify_guard_condition_, guard_condition_options);
if (ret != RCL_RET_OK) {
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
}
// Setup a safe exit lamda to clean up the guard condition in case of an error here.
auto finalize_notify_guard_condition = [this]() {
// Finalize the interrupt guard condition.
if (rcl_guard_condition_fini(&notify_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
}
};
// Determine the domain id based on the options and the ROS_DOMAIN_ID env variable.
size_t domain_id = 0;
char * ros_domain_id = nullptr;
const char * env_var = "ROS_DOMAIN_ID";
#ifndef _WIN32
ros_domain_id = getenv(env_var);
#else
size_t ros_domain_id_size;
_dupenv_s(&ros_domain_id, &ros_domain_id_size, env_var);
#endif
if (ros_domain_id) {
uint32_t number = strtoul(ros_domain_id, NULL, 0);
if (number == (std::numeric_limits<uint32_t>::max)()) {
// Finalize the interrupt guard condition.
finalize_notify_guard_condition();
#ifdef _WIN32
// free the ros_domain_id before throwing, if getenv was used on Windows
free(ros_domain_id);
#endif
throw std::runtime_error("failed to interpret ROS_DOMAIN_ID as integral number");
}
domain_id = static_cast<size_t>(number);
#ifdef _WIN32
free(ros_domain_id);
#endif
}
// Create the rcl node and store it in a shared_ptr with a custom destructor.
rcl_node_t * rcl_node = new rcl_node_t(rcl_get_zero_initialized_node());
rcl_node_options_t options = rcl_node_get_default_options();
// TODO(wjwwood): pass the Allocator to the options
options.domain_id = domain_id;
ret = rcl_node_init(rcl_node, node_name.c_str(), namespace_.c_str(), &options);
if (ret != RCL_RET_OK) {
// Finalize the interrupt guard condition.
finalize_notify_guard_condition();
if (ret == RCL_RET_NODE_INVALID_NAME) {
rcl_reset_error(); // discard rcl_node_init error
int validation_result;
size_t invalid_index;
rmw_ret_t rmw_ret =
rmw_validate_node_name(node_name.c_str(), &validation_result, &invalid_index);
if (rmw_ret != RMW_RET_OK) {
if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "failed to validate node name");
}
throw_from_rcl_error(RCL_RET_ERROR, "failed to validate node name");
}
throw rclcpp::exceptions::InvalidNodeNameError(
node_name.c_str(),
rmw_node_name_validation_result_string(validation_result),
invalid_index);
}
if (ret == RCL_RET_NODE_INVALID_NAMESPACE) {
rcl_reset_error(); // discard rcl_node_init error
int validation_result;
size_t invalid_index;
rmw_ret_t rmw_ret =
rmw_validate_namespace(namespace_.c_str(), &validation_result, &invalid_index);
if (rmw_ret != RMW_RET_OK) {
if (rmw_ret == RMW_RET_INVALID_ARGUMENT) {
throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "failed to validate namespace");
}
throw_from_rcl_error(RCL_RET_ERROR, "failed to validate namespace");
}
throw rclcpp::exceptions::InvalidNamespaceError(
namespace_.c_str(),
rmw_namespace_validation_result_string(validation_result),
invalid_index);
}
throw_from_rcl_error(ret, "failed to initialize rcl node");
}
node_handle_.reset(rcl_node, [](rcl_node_t * node) -> void {
if (rcl_node_fini(node) != RCL_RET_OK) {
fprintf(
stderr, "Error in destruction of rcl node handle: %s\n", rcl_get_error_string_safe());
}
delete node;
});
// Create the default callback group.
using rclcpp::callback_group::CallbackGroupType;
default_callback_group_ = create_callback_group(CallbackGroupType::MutuallyExclusive);
// Indicate the notify_guard_condition is now valid.
notify_guard_condition_is_valid_ = true;
}
NodeBase::~NodeBase()
{
// Finalize the interrupt guard condition after removing self from graph listener.
{
std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
notify_guard_condition_is_valid_ = false;
if (rcl_guard_condition_fini(&notify_guard_condition_) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
}
}
}
const char *
NodeBase::get_name() const
{
return rcl_node_get_name(node_handle_.get());
}
const char *
NodeBase::get_namespace() const
{
return rcl_node_get_namespace(node_handle_.get());
}
rclcpp::context::Context::SharedPtr
NodeBase::get_context()
{
return context_;
}
rcl_node_t *
NodeBase::get_rcl_node_handle()
{
return node_handle_.get();
}
const rcl_node_t *
NodeBase::get_rcl_node_handle() const
{
return node_handle_.get();
}
std::shared_ptr<rcl_node_t>
NodeBase::get_shared_rcl_node_handle()
{
return node_handle_;
}
std::shared_ptr<const rcl_node_t>
NodeBase::get_shared_rcl_node_handle() const
{
return node_handle_;
}
rclcpp::callback_group::CallbackGroup::SharedPtr
NodeBase::create_callback_group(rclcpp::callback_group::CallbackGroupType group_type)
{
using rclcpp::callback_group::CallbackGroup;
using rclcpp::callback_group::CallbackGroupType;
auto group = CallbackGroup::SharedPtr(new CallbackGroup(group_type));
callback_groups_.push_back(group);
return group;
}
rclcpp::callback_group::CallbackGroup::SharedPtr
NodeBase::get_default_callback_group()
{
return default_callback_group_;
}
bool
NodeBase::callback_group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
bool group_belongs_to_this_node = false;
for (auto & weak_group : this->callback_groups_) {
auto cur_group = weak_group.lock();
if (cur_group && (cur_group == group)) {
group_belongs_to_this_node = true;
}
}
return group_belongs_to_this_node;
}
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
NodeBase::get_callback_groups() const
{
return callback_groups_;
}
std::atomic_bool &
NodeBase::get_associated_with_executor_atomic()
{
return associated_with_executor_;
}
rcl_guard_condition_t *
NodeBase::get_notify_guard_condition()
{
std::lock_guard<std::recursive_mutex> notify_condition_lock(notify_guard_condition_mutex_);
if (!notify_guard_condition_is_valid_) {
return nullptr;
}
return &notify_guard_condition_;
}
std::unique_lock<std::recursive_mutex>
NodeBase::acquire_notify_guard_condition_lock() const
{
return std::unique_lock<std::recursive_mutex>(notify_guard_condition_mutex_);
}

View File

@@ -0,0 +1,312 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_interfaces/node_graph.hpp"
#include <map>
#include <string>
#include <vector>
#include "rcl/graph.h"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/event.hpp"
#include "rclcpp/graph_listener.hpp"
using rclcpp::node_interfaces::NodeGraph;
using rclcpp::exceptions::throw_from_rcl_error;
using rclcpp::graph_listener::GraphListener;
NodeGraph::NodeGraph(rclcpp::node_interfaces::NodeBaseInterface * node_base)
: node_base_(node_base),
graph_listener_(node_base->get_context()->get_sub_context<GraphListener>()),
should_add_to_graph_listener_(true),
graph_users_count_(0)
{}
NodeGraph::~NodeGraph()
{
// Remove self from graph listener.
// Exchange with false to prevent others from trying to add this node to the
// graph listener after checking that it was not here.
if (!should_add_to_graph_listener_.exchange(false)) {
// If it was already false, then it needs to now be removed.
graph_listener_->remove_node(this);
}
}
std::map<std::string, std::vector<std::string>>
NodeGraph::get_topic_names_and_types(bool no_demangle) const
{
rcl_names_and_types_t topic_names_and_types = rcl_get_zero_initialized_names_and_types();
rcl_allocator_t allocator = rcl_get_default_allocator();
auto ret = rcl_get_topic_names_and_types(
node_base_->get_rcl_node_handle(),
&allocator,
no_demangle,
&topic_names_and_types);
if (ret != RCL_RET_OK) {
auto error_msg = std::string("failed to get topic names and types: ") +
rcl_get_error_string_safe();
rcl_reset_error();
if (rcl_names_and_types_fini(&topic_names_and_types) != RCL_RET_OK) {
error_msg += std::string(", failed also to cleanup topic names and types, leaking memory: ") +
rcl_get_error_string_safe();
}
throw std::runtime_error(error_msg + rcl_get_error_string_safe());
}
std::map<std::string, std::vector<std::string>> topics_and_types;
for (size_t i = 0; i < topic_names_and_types.names.size; ++i) {
std::string topic_name = topic_names_and_types.names.data[i];
for (size_t j = 0; j < topic_names_and_types.types[i].size; ++j) {
topics_and_types[topic_name].emplace_back(topic_names_and_types.types[i].data[j]);
}
}
ret = rcl_names_and_types_fini(&topic_names_and_types);
if (ret != RCL_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not destroy topic names and types: ") + rcl_get_error_string_safe());
// *INDENT-ON*
}
return topics_and_types;
}
std::map<std::string, std::vector<std::string>>
NodeGraph::get_service_names_and_types() const
{
rcl_names_and_types_t service_names_and_types = rcl_get_zero_initialized_names_and_types();
rcl_allocator_t allocator = rcl_get_default_allocator();
auto ret = rcl_get_service_names_and_types(
node_base_->get_rcl_node_handle(),
&allocator,
&service_names_and_types);
if (ret != RCL_RET_OK) {
auto error_msg = std::string("failed to get service names and types: ") +
rcl_get_error_string_safe();
rcl_reset_error();
if (rcl_names_and_types_fini(&service_names_and_types) != RCL_RET_OK) {
error_msg +=
std::string(", failed also to cleanup service names and types, leaking memory: ") +
rcl_get_error_string_safe();
}
throw std::runtime_error(error_msg + rcl_get_error_string_safe());
}
std::map<std::string, std::vector<std::string>> services_and_types;
for (size_t i = 0; i < service_names_and_types.names.size; ++i) {
std::string service_name = service_names_and_types.names.data[i];
for (size_t j = 0; j < service_names_and_types.types[i].size; ++j) {
services_and_types[service_name].emplace_back(service_names_and_types.types[i].data[j]);
}
}
ret = rcl_names_and_types_fini(&service_names_and_types);
if (ret != RCL_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not destroy service names and types: ") + rcl_get_error_string_safe());
// *INDENT-ON*
}
return services_and_types;
}
std::vector<std::string>
NodeGraph::get_node_names() const
{
rcutils_string_array_t node_names_c =
rcutils_get_zero_initialized_string_array();
auto allocator = rcl_get_default_allocator();
auto ret = rcl_get_node_names(
node_base_->get_rcl_node_handle(),
allocator,
&node_names_c);
if (ret != RCL_RET_OK) {
auto error_msg = std::string("failed to get node names: ") + rcl_get_error_string_safe();
rcl_reset_error();
if (rcutils_string_array_fini(&node_names_c) != RCUTILS_RET_OK) {
error_msg += std::string(", failed also to cleanup node names, leaking memory: ") +
rcl_get_error_string_safe();
}
// TODO(karsten1987): Append rcutils_error_message once it's in master
throw std::runtime_error(error_msg);
}
std::vector<std::string> node_names(&node_names_c.data[0],
&node_names_c.data[0 + node_names_c.size]);
ret = rcutils_string_array_fini(&node_names_c);
if (ret != RCUTILS_RET_OK) {
// *INDENT-OFF*
// TODO(karsten1987): Append rcutils_error_message once it's in master
throw std::runtime_error(
std::string("could not destroy node names: "));
// *INDENT-ON*
}
return node_names;
}
size_t
NodeGraph::count_publishers(const std::string & topic_name) const
{
auto rmw_node_handle = rcl_node_get_rmw_handle(node_base_->get_rcl_node_handle());
auto fqdn = rclcpp::expand_topic_or_service_name(
topic_name,
rmw_node_handle->name,
rmw_node_handle->namespace_,
false); // false = not a service
size_t count;
// TODO(wjwwood): use the rcl equivalent methods
auto ret = rmw_count_publishers(rmw_node_handle, fqdn.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count publishers: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
return count;
}
size_t
NodeGraph::count_subscribers(const std::string & topic_name) const
{
auto rmw_node_handle = rcl_node_get_rmw_handle(node_base_->get_rcl_node_handle());
auto fqdn = rclcpp::expand_topic_or_service_name(
topic_name,
rmw_node_handle->name,
rmw_node_handle->namespace_,
false); // false = not a service
size_t count;
// TODO(wjwwood): use the rcl equivalent methods
auto ret = rmw_count_subscribers(rmw_node_handle, fqdn.c_str(), &count);
if (ret != RMW_RET_OK) {
// *INDENT-OFF*
throw std::runtime_error(
std::string("could not count subscribers: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
return count;
}
const rcl_guard_condition_t *
NodeGraph::get_graph_guard_condition() const
{
return rcl_node_get_graph_guard_condition(node_base_->get_rcl_node_handle());
}
void
NodeGraph::notify_graph_change()
{
{
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
bool bad_ptr_encountered = false;
for (auto & event_wptr : graph_events_) {
auto event_ptr = event_wptr.lock();
if (event_ptr) {
event_ptr->set();
} else {
bad_ptr_encountered = true;
}
}
if (bad_ptr_encountered) {
// remove invalid pointers with the erase-remove idiom
graph_events_.erase(
std::remove_if(
graph_events_.begin(),
graph_events_.end(),
[](const rclcpp::event::Event::WeakPtr & wptr) {
return wptr.expired();
}),
graph_events_.end());
// update graph_users_count_
graph_users_count_.store(graph_events_.size());
}
}
graph_cv_.notify_all();
{
auto notify_condition_lock = node_base_->acquire_notify_guard_condition_lock();
rcl_ret_t ret = rcl_trigger_guard_condition(node_base_->get_notify_guard_condition());
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to trigger notify guard condition");
}
}
}
void
NodeGraph::notify_shutdown()
{
// notify here anything that will not be woken up by ctrl-c or rclcpp::shutdown().
graph_cv_.notify_all();
}
rclcpp::event::Event::SharedPtr
NodeGraph::get_graph_event()
{
auto event = rclcpp::event::Event::make_shared();
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
graph_events_.push_back(event);
graph_users_count_++;
// on first call, add node to graph_listener_
if (should_add_to_graph_listener_.exchange(false)) {
graph_listener_->add_node(this);
graph_listener_->start_if_not_started();
}
return event;
}
void
NodeGraph::wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout)
{
using rclcpp::exceptions::InvalidEventError;
using rclcpp::exceptions::EventNotRegisteredError;
if (!event) {
throw InvalidEventError();
}
{
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
bool event_in_graph_events = false;
for (const auto & event_wptr : graph_events_) {
if (event == event_wptr.lock()) {
event_in_graph_events = true;
break;
}
}
if (!event_in_graph_events) {
throw EventNotRegisteredError();
}
}
auto pred = [&event]() {
return event->check() || !rclcpp::utilities::ok();
};
std::unique_lock<std::mutex> graph_lock(graph_mutex_);
if (!pred()) {
graph_cv_.wait_for(graph_lock, timeout, pred);
}
}
size_t
NodeGraph::count_graph_users()
{
return graph_users_count_.load();
}

View File

@@ -0,0 +1,241 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_interfaces/node_parameters.hpp"
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "rclcpp/create_publisher.hpp"
#include "rmw/qos_profiles.h"
using rclcpp::node_interfaces::NodeParameters;
NodeParameters::NodeParameters(
rclcpp::node_interfaces::NodeTopicsInterface * node_topics,
bool use_intra_process)
: node_topics_(node_topics)
{
using MessageT = rcl_interfaces::msg::ParameterEvent;
using PublisherT = rclcpp::publisher::Publisher<MessageT>;
using AllocatorT = std::allocator<void>;
// TODO(wjwwood): expose this allocator through the Parameter interface.
auto allocator = std::make_shared<AllocatorT>();
events_publisher_ = rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
node_topics_,
"parameter_events",
rmw_qos_profile_parameter_events,
use_intra_process,
allocator);
}
NodeParameters::~NodeParameters()
{}
std::vector<rcl_interfaces::msg::SetParametersResult>
NodeParameters::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
std::vector<rcl_interfaces::msg::SetParametersResult> results;
for (auto p : parameters) {
auto result = set_parameters_atomically({{p}});
results.push_back(result);
}
return results;
}
rcl_interfaces::msg::SetParametersResult
NodeParameters::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
std::lock_guard<std::mutex> lock(mutex_);
std::map<std::string, rclcpp::parameter::ParameterVariant> tmp_map;
auto parameter_event = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
// TODO(jacquelinekay): handle parameter constraints
rcl_interfaces::msg::SetParametersResult result;
if (parameters_callback_) {
result = parameters_callback_(parameters);
} else {
result.successful = true;
}
if (!result.successful) {
return result;
}
for (auto p : parameters) {
if (parameters_.find(p.get_name()) == parameters_.end()) {
if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
// case: parameter not set before, and input is something other than "NOT_SET"
parameter_event->new_parameters.push_back(p.to_parameter());
}
} else if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
// case: parameter was set before, and input is something other than "NOT_SET"
parameter_event->changed_parameters.push_back(p.to_parameter());
} else {
// case: parameter was set before, and input is "NOT_SET"
// therefore we will "unset" the previously set parameter
// it is not necessary to erase the parameter from parameters_
// because the new value for this key (p.get_name()) will be a
// ParameterVariant with type "NOT_SET"
parameter_event->deleted_parameters.push_back(p.to_parameter());
}
tmp_map[p.get_name()] = p;
}
// std::map::insert will not overwrite elements, so we'll keep the new
// ones and add only those that already exist in the Node's internal map
tmp_map.insert(parameters_.begin(), parameters_.end());
std::swap(tmp_map, parameters_);
events_publisher_->publish(parameter_event);
return result;
}
std::vector<rclcpp::parameter::ParameterVariant>
NodeParameters::get_parameters(const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<rclcpp::parameter::ParameterVariant> results;
for (auto & name : names) {
if (std::any_of(parameters_.cbegin(), parameters_.cend(),
[&name](const std::pair<std::string, rclcpp::parameter::ParameterVariant> & kv) {
return name == kv.first;
}))
{
results.push_back(parameters_.at(name));
}
}
return results;
}
rclcpp::parameter::ParameterVariant
NodeParameters::get_parameter(const std::string & name) const
{
rclcpp::parameter::ParameterVariant parameter;
if (get_parameter(name, parameter)) {
return parameter;
} else {
throw std::out_of_range("Parameter '" + name + "' not set");
}
}
bool
NodeParameters::get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) const
{
std::lock_guard<std::mutex> lock(mutex_);
if (parameters_.count(name)) {
parameter = parameters_.at(name);
return true;
} else {
return false;
}
}
std::vector<rcl_interfaces::msg::ParameterDescriptor>
NodeParameters::describe_parameters(const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<rcl_interfaces::msg::ParameterDescriptor> results;
for (auto & kv : parameters_) {
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
return name == kv.first;
}))
{
rcl_interfaces::msg::ParameterDescriptor parameter_descriptor;
parameter_descriptor.name = kv.first;
parameter_descriptor.type = kv.second.get_type();
results.push_back(parameter_descriptor);
}
}
return results;
}
std::vector<uint8_t>
NodeParameters::get_parameter_types(const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<uint8_t> results;
for (auto & kv : parameters_) {
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
return name == kv.first;
}))
{
results.push_back(kv.second.get_type());
} else {
results.push_back(rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET);
}
}
return results;
}
rcl_interfaces::msg::ListParametersResult
NodeParameters::list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const
{
std::lock_guard<std::mutex> lock(mutex_);
rcl_interfaces::msg::ListParametersResult result;
// TODO(esteve): define parameter separator, use "." for now
for (auto & kv : parameters_) {
if (((prefixes.size() == 0) &&
((depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
(static_cast<uint64_t>(std::count(kv.first.begin(), kv.first.end(), '.')) < depth))) ||
(std::any_of(prefixes.cbegin(), prefixes.cend(), [&kv, &depth](const std::string & prefix) {
if (kv.first == prefix) {
return true;
} else if (kv.first.find(prefix + ".") == 0) {
size_t length = prefix.length();
std::string substr = kv.first.substr(length);
// Cast as unsigned integer to avoid warning
return (depth == rcl_interfaces::srv::ListParameters::Request::DEPTH_RECURSIVE) ||
(static_cast<uint64_t>(std::count(substr.begin(), substr.end(), '.')) < depth);
}
return false;
})))
{
result.names.push_back(kv.first);
size_t last_separator = kv.first.find_last_of('.');
if (std::string::npos != last_separator) {
std::string prefix = kv.first.substr(0, last_separator);
if (std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
result.prefixes.cend())
{
result.prefixes.push_back(prefix);
}
}
}
}
return result;
}
void
NodeParameters::register_param_change_callback(ParametersCallbackFunction callback)
{
if (parameters_callback_) {
fprintf(stderr, "Warning: param_change_callback already registered, "
"overwriting previous callback\n");
}
parameters_callback_ = callback;
}

View File

@@ -0,0 +1,78 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_interfaces/node_services.hpp"
#include <string>
using rclcpp::node_interfaces::NodeServices;
NodeServices::NodeServices(rclcpp::node_interfaces::NodeBaseInterface * node_base)
: node_base_(node_base)
{}
NodeServices::~NodeServices()
{}
void
NodeServices::add_service(
rclcpp::service::ServiceBase::SharedPtr service_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
if (group) {
if (!node_base_->callback_group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create service, group not in node.");
}
group->add_service(service_base_ptr);
} else {
node_base_->get_default_callback_group()->add_service(service_base_ptr);
}
// Notify the executor that a new service was created using the parent Node.
{
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string("Failed to notify waitset on service creation: ") + rmw_get_error_string()
);
}
}
}
void
NodeServices::add_client(
rclcpp::client::ClientBase::SharedPtr client_base_ptr,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
if (group) {
if (!node_base_->callback_group_in_node(group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create client, group not in node.");
}
group->add_client(client_base_ptr);
} else {
node_base_->get_default_callback_group()->add_client(client_base_ptr);
}
// Notify the executor that a new client was created using the parent Node.
{
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string("Failed to notify waitset on client creation: ") + rmw_get_error_string()
);
}
}
}

View File

@@ -0,0 +1,47 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_interfaces/node_timers.hpp"
#include <string>
using rclcpp::node_interfaces::NodeTimers;
NodeTimers::NodeTimers(rclcpp::node_interfaces::NodeBaseInterface * node_base)
: node_base_(node_base)
{}
NodeTimers::~NodeTimers()
{}
void
NodeTimers::add_timer(
rclcpp::timer::TimerBase::SharedPtr timer,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group)
{
if (callback_group) {
if (!node_base_->callback_group_in_node(callback_group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create timer, group not in node.");
}
callback_group->add_timer(timer);
} else {
node_base_->get_default_callback_group()->add_timer(timer);
}
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on timer creation: ") + rmw_get_error_string());
}
}

View File

@@ -0,0 +1,131 @@
// Copyright 2016 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/node_interfaces/node_topics.hpp"
#include <string>
#include "rclcpp/intra_process_manager.hpp"
#include "rclcpp/exceptions.hpp"
using rclcpp::exceptions::throw_from_rcl_error;
using rclcpp::node_interfaces::NodeTopics;
NodeTopics::NodeTopics(rclcpp::node_interfaces::NodeBaseInterface * node_base)
: node_base_(node_base)
{}
NodeTopics::~NodeTopics()
{}
rclcpp::publisher::PublisherBase::SharedPtr
NodeTopics::create_publisher(
const std::string & topic_name,
const rclcpp::PublisherFactory & publisher_factory,
rcl_publisher_options_t & publisher_options,
bool use_intra_process)
{
// Create the MessageT specific Publisher using the factory, but store it as PublisherBase.
auto publisher = publisher_factory.create_typed_publisher(
node_base_, topic_name, publisher_options);
// Setup intra process publishing if requested.
if (use_intra_process) {
auto context = node_base_->get_context();
// Get the intra process manager instance for this context.
auto ipm = context->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
// Register the publisher with the intra process manager.
uint64_t intra_process_publisher_id =
publisher_factory.add_publisher_to_intra_process_manager(ipm.get(), publisher);
// Create a function to be called when publisher to do the intra process publish.
auto shared_publish_callback = publisher_factory.create_shared_publish_callback(ipm);
publisher->setup_intra_process(
intra_process_publisher_id,
shared_publish_callback,
publisher_options);
}
// Return the completed publisher.
return publisher;
}
void
NodeTopics::add_publisher(
rclcpp::publisher::PublisherBase::SharedPtr publisher)
{
// The publisher is not added to a callback group or anthing like that for now.
// It may be stored within the NodeTopics class or the NodeBase class in the future.
(void)publisher;
// Notify the executor that a new publisher was created using the parent Node.
{
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on publisher creation: ") + rmw_get_error_string());
}
}
}
rclcpp::subscription::SubscriptionBase::SharedPtr
NodeTopics::create_subscription(
const std::string & topic_name,
const rclcpp::SubscriptionFactory & subscription_factory,
rcl_subscription_options_t & subscription_options,
bool use_intra_process)
{
auto subscription = subscription_factory.create_typed_subscription(
node_base_, topic_name, subscription_options);
// Setup intra process publishing if requested.
if (use_intra_process) {
auto context = node_base_->get_context();
auto intra_process_manager =
context->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
subscription_factory.setup_intra_process(
intra_process_manager, subscription, subscription_options);
}
// Return the completed subscription.
return subscription;
}
void
NodeTopics::add_subscription(
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group)
{
// Assign to a group.
if (callback_group) {
if (!node_base_->callback_group_in_node(callback_group)) {
// TODO(jacquelinekay): use custom exception
throw std::runtime_error("Cannot create subscription, callback group not in node.");
}
callback_group->add_subscription(subscription);
} else {
node_base_->get_default_callback_group()->add_subscription(subscription);
}
// Notify the executor that a new subscription was created using the parent Node.
{
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on subscription creation: ") + rmw_get_error_string()
);
}
}
}

View File

@@ -0,0 +1,279 @@
// 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.
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
#include "rclcpp/parameter.hpp"
#include "rclcpp/utilities.hpp"
using rclcpp::parameter::ParameterType;
using rclcpp::parameter::ParameterVariant;
ParameterVariant::ParameterVariant()
: name_("")
{
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET;
}
ParameterVariant::ParameterVariant(const std::string & name, const bool bool_value)
: name_(name)
{
value_.bool_value = bool_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL;
}
ParameterVariant::ParameterVariant(const std::string & name, const int int_value)
: name_(name)
{
value_.integer_value = int_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
}
ParameterVariant::ParameterVariant(const std::string & name, const int64_t int_value)
: name_(name)
{
value_.integer_value = int_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
}
ParameterVariant::ParameterVariant(const std::string & name, const float double_value)
: name_(name)
{
value_.double_value = double_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
}
ParameterVariant::ParameterVariant(const std::string & name, const double double_value)
: name_(name)
{
value_.double_value = double_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
}
ParameterVariant::ParameterVariant(const std::string & name, const std::string & string_value)
: name_(name)
{
value_.string_value = string_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_STRING;
}
ParameterVariant::ParameterVariant(const std::string & name, const char * string_value)
: ParameterVariant(name, std::string(string_value))
{}
ParameterVariant::ParameterVariant(
const std::string & name, const std::vector<uint8_t> & bytes_value)
: name_(name)
{
value_.bytes_value = bytes_value;
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BYTES;
}
ParameterType
ParameterVariant::get_type() const
{
return static_cast<ParameterType>(value_.type);
}
std::string
ParameterVariant::get_type_name() const
{
switch (get_type()) {
case rclcpp::parameter::ParameterType::PARAMETER_BOOL:
return "bool";
case rclcpp::parameter::ParameterType::PARAMETER_INTEGER:
return "integer";
case rclcpp::parameter::ParameterType::PARAMETER_DOUBLE:
return "double";
case rclcpp::parameter::ParameterType::PARAMETER_STRING:
return "string";
case rclcpp::parameter::ParameterType::PARAMETER_BYTES:
return "bytes";
case rclcpp::parameter::ParameterType::PARAMETER_NOT_SET:
return "not set";
default:
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
"Unexpected type from ParameterVariant: " + std::to_string(get_type()));
// *INDENT-ON*
}
}
const std::string &
ParameterVariant::get_name() const
{
return name_;
}
rcl_interfaces::msg::ParameterValue
ParameterVariant::get_parameter_value() const
{
return value_;
}
int64_t
ParameterVariant::as_int() const
{
return get_value<ParameterType::PARAMETER_INTEGER>();
}
double
ParameterVariant::as_double() const
{
return get_value<ParameterType::PARAMETER_DOUBLE>();
}
const std::string &
ParameterVariant::as_string() const
{
return get_value<ParameterType::PARAMETER_STRING>();
}
bool
ParameterVariant::as_bool() const
{
return get_value<ParameterType::PARAMETER_BOOL>();
}
const std::vector<uint8_t> &
ParameterVariant::as_bytes() const
{
return get_value<ParameterType::PARAMETER_BYTES>();
}
ParameterVariant
ParameterVariant::from_parameter(const rcl_interfaces::msg::Parameter & parameter)
{
switch (parameter.value.type) {
case PARAMETER_BOOL:
return ParameterVariant(parameter.name, parameter.value.bool_value);
case PARAMETER_INTEGER:
return ParameterVariant(parameter.name, parameter.value.integer_value);
case PARAMETER_DOUBLE:
return ParameterVariant(parameter.name, parameter.value.double_value);
case PARAMETER_STRING:
return ParameterVariant(parameter.name, parameter.value.string_value);
case PARAMETER_BYTES:
return ParameterVariant(parameter.name, parameter.value.bytes_value);
case PARAMETER_NOT_SET:
throw std::runtime_error("Type from ParameterValue is not set");
default:
// TODO(wjwwood): use custom exception
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
"Unexpected type from ParameterVariant: " + std::to_string(parameter.value.type));
// *INDENT-ON*
}
}
rcl_interfaces::msg::Parameter
ParameterVariant::to_parameter()
{
rcl_interfaces::msg::Parameter parameter;
parameter.name = name_;
parameter.value = value_;
return parameter;
}
std::string
ParameterVariant::value_to_string() const
{
switch (get_type()) {
case rclcpp::parameter::ParameterType::PARAMETER_BOOL:
return as_bool() ? "true" : "false";
case rclcpp::parameter::ParameterType::PARAMETER_INTEGER:
return std::to_string(as_int());
case rclcpp::parameter::ParameterType::PARAMETER_DOUBLE:
return std::to_string(as_double());
case rclcpp::parameter::ParameterType::PARAMETER_STRING:
return as_string();
case rclcpp::parameter::ParameterType::PARAMETER_BYTES:
{
std::stringstream bytes;
bool first_byte = true;
bytes << "[" << std::hex;
for (auto & byte : as_bytes()) {
bytes << "0x" << byte;
if (!first_byte) {
bytes << ", ";
} else {
first_byte = false;
}
}
return bytes.str();
}
case rclcpp::parameter::ParameterType::PARAMETER_NOT_SET:
return "not set";
default:
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
"Unexpected type from ParameterVariant: " + std::to_string(get_type()));
// *INDENT-ON*
}
}
std::string
rclcpp::parameter::_to_json_dict_entry(const ParameterVariant & param)
{
std::stringstream ss;
ss << "\"" << param.get_name() << "\": ";
ss << "{\"type\": \"" << param.get_type_name() << "\", ";
ss << "\"value\": \"" << param.value_to_string() << "\"}";
return ss.str();
}
std::ostream &
rclcpp::parameter::operator<<(std::ostream & os, const rclcpp::parameter::ParameterVariant & pv)
{
os << std::to_string(pv);
return os;
}
std::ostream &
rclcpp::parameter::operator<<(std::ostream & os, const std::vector<ParameterVariant> & parameters)
{
os << std::to_string(parameters);
return os;
}
std::string
std::to_string(const rclcpp::parameter::ParameterVariant & param)
{
std::stringstream ss;
ss << "{\"name\": \"" << param.get_name() << "\", ";
ss << "\"type\": \"" << param.get_type_name() << "\", ";
ss << "\"value\": \"" << param.value_to_string() << "\"}";
return ss.str();
}
std::string
std::to_string(const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
std::stringstream ss;
ss << "{";
bool first = true;
for (const auto & pv : parameters) {
if (first == false) {
ss << ", ";
} else {
first = false;
}
ss << rclcpp::parameter::_to_json_dict_entry(pv);
}
ss << "}";
return ss.str();
}

View File

@@ -0,0 +1,333 @@
// 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.
#include "rclcpp/parameter_client.hpp"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
using rclcpp::parameter_client::AsyncParametersClient;
using rclcpp::parameter_client::SyncParametersClient;
AsyncParametersClient::AsyncParametersClient(
const rclcpp::node::Node::SharedPtr node,
const std::string & remote_node_name,
const rmw_qos_profile_t & qos_profile)
: node_(node)
{
if (remote_node_name != "") {
remote_node_name_ = remote_node_name;
} else {
remote_node_name_ = node_->get_name();
}
get_parameters_client_ = node_->create_client<rcl_interfaces::srv::GetParameters>(
remote_node_name_ + "__get_parameters", qos_profile);
get_parameter_types_client_ = node_->create_client<rcl_interfaces::srv::GetParameterTypes>(
remote_node_name_ + "__get_parameter_types", qos_profile);
set_parameters_client_ = node_->create_client<rcl_interfaces::srv::SetParameters>(
remote_node_name_ + "__set_parameters", qos_profile);
list_parameters_client_ = node_->create_client<rcl_interfaces::srv::ListParameters>(
remote_node_name_ + "__list_parameters", qos_profile);
describe_parameters_client_ = node_->create_client<rcl_interfaces::srv::DescribeParameters>(
remote_node_name_ + "__describe_parameters", qos_profile);
}
std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>
AsyncParametersClient::get_parameters(
const std::vector<std::string> & names,
std::function<
void(std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>)
> callback)
{
auto promise_result =
std::make_shared<std::promise<std::vector<rclcpp::parameter::ParameterVariant>>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::GetParameters::Request>();
request->names = names;
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
get_parameters_client_->async_send_request(
request,
[request, promise_result, future_result, &callback](
rclcpp::client::Client<rcl_interfaces::srv::GetParameters>::SharedFuture cb_f)
{
std::vector<rclcpp::parameter::ParameterVariant> parameter_variants;
auto & pvalues = cb_f.get()->values;
for (auto & pvalue : pvalues) {
auto i = &pvalue - &pvalues[0];
rcl_interfaces::msg::Parameter parameter;
parameter.name = request->names[i];
parameter.value = pvalue;
parameter_variants.push_back(rclcpp::parameter::ParameterVariant::from_parameter(
parameter));
}
promise_result->set_value(parameter_variants);
if (callback != nullptr) {
callback(future_result);
}
}
);
// *INDENT-ON*
return future_result;
}
std::shared_future<std::vector<rclcpp::parameter::ParameterType>>
AsyncParametersClient::get_parameter_types(
const std::vector<std::string> & names,
std::function<
void(std::shared_future<std::vector<rclcpp::parameter::ParameterType>>)
> callback)
{
auto promise_result =
std::make_shared<std::promise<std::vector<rclcpp::parameter::ParameterType>>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::GetParameterTypes::Request>();
request->names = names;
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
get_parameter_types_client_->async_send_request(
request,
[promise_result, future_result, &callback](
rclcpp::client::Client<rcl_interfaces::srv::GetParameterTypes>::SharedFuture cb_f)
{
std::vector<rclcpp::parameter::ParameterType> types;
auto & pts = cb_f.get()->types;
for (auto & pt : pts) {
pts.push_back(static_cast<rclcpp::parameter::ParameterType>(pt));
}
promise_result->set_value(types);
if (callback != nullptr) {
callback(future_result);
}
}
);
// *INDENT-ON*
return future_result;
}
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
AsyncParametersClient::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
std::function<
void(std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>)
> callback)
{
auto promise_result =
std::make_shared<std::promise<std::vector<rcl_interfaces::msg::SetParametersResult>>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::SetParameters::Request>();
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
std::transform(parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
[](rclcpp::parameter::ParameterVariant p) {
return p.to_parameter();
}
);
set_parameters_client_->async_send_request(
request,
[promise_result, future_result, &callback](
rclcpp::client::Client<rcl_interfaces::srv::SetParameters>::SharedFuture cb_f)
{
promise_result->set_value(cb_f.get()->results);
if (callback != nullptr) {
callback(future_result);
}
}
);
// *INDENT-ON*
return future_result;
}
std::shared_future<rcl_interfaces::msg::SetParametersResult>
AsyncParametersClient::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
std::function<
void(std::shared_future<rcl_interfaces::msg::SetParametersResult>)
> callback)
{
auto promise_result =
std::make_shared<std::promise<rcl_interfaces::msg::SetParametersResult>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::SetParametersAtomically::Request>();
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
std::transform(parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
[](rclcpp::parameter::ParameterVariant p) {
return p.to_parameter();
}
);
set_parameters_atomically_client_->async_send_request(
request,
[promise_result, future_result, &callback](
rclcpp::client::Client<rcl_interfaces::srv::SetParametersAtomically>::SharedFuture cb_f)
{
promise_result->set_value(cb_f.get()->result);
if (callback != nullptr) {
callback(future_result);
}
}
);
// *INDENT-ON*
return future_result;
}
std::shared_future<rcl_interfaces::msg::ListParametersResult>
AsyncParametersClient::list_parameters(
const std::vector<std::string> & prefixes,
uint64_t depth,
std::function<
void(std::shared_future<rcl_interfaces::msg::ListParametersResult>)
> callback)
{
auto promise_result =
std::make_shared<std::promise<rcl_interfaces::msg::ListParametersResult>>();
auto future_result = promise_result->get_future().share();
auto request = std::make_shared<rcl_interfaces::srv::ListParameters::Request>();
request->prefixes = prefixes;
request->depth = depth;
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
list_parameters_client_->async_send_request(
request,
[promise_result, future_result, &callback](
rclcpp::client::Client<rcl_interfaces::srv::ListParameters>::SharedFuture cb_f)
{
promise_result->set_value(cb_f.get()->result);
if (callback != nullptr) {
callback(future_result);
}
}
);
// *INDENT-ON*
return future_result;
}
SyncParametersClient::SyncParametersClient(
rclcpp::node::Node::SharedPtr node,
const rmw_qos_profile_t & qos_profile)
: node_(node)
{
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
async_parameters_client_ = std::make_shared<AsyncParametersClient>(node, "", qos_profile);
}
SyncParametersClient::SyncParametersClient(
rclcpp::executor::Executor::SharedPtr executor,
rclcpp::node::Node::SharedPtr node,
const rmw_qos_profile_t & qos_profile)
: executor_(executor), node_(node)
{
async_parameters_client_ = std::make_shared<AsyncParametersClient>(node, "", qos_profile);
}
std::vector<rclcpp::parameter::ParameterVariant>
SyncParametersClient::get_parameters(const std::vector<std::string> & parameter_names)
{
auto f = async_parameters_client_->get_parameters(parameter_names);
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
}
// Return an empty vector if unsuccessful
return std::vector<rclcpp::parameter::ParameterVariant>();
}
bool
SyncParametersClient::has_parameter(const std::string & parameter_name)
{
std::vector<std::string> names;
names.push_back(parameter_name);
auto vars = list_parameters(names, 1);
return vars.names.size() > 0;
}
std::vector<rclcpp::parameter::ParameterType>
SyncParametersClient::get_parameter_types(const std::vector<std::string> & parameter_names)
{
auto f = async_parameters_client_->get_parameter_types(parameter_names);
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
}
return std::vector<rclcpp::parameter::ParameterType>();
}
std::vector<rcl_interfaces::msg::SetParametersResult>
SyncParametersClient::set_parameters(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
auto f = async_parameters_client_->set_parameters(parameters);
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
}
return std::vector<rcl_interfaces::msg::SetParametersResult>();
}
rcl_interfaces::msg::SetParametersResult
SyncParametersClient::set_parameters_atomically(
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
{
auto f = async_parameters_client_->set_parameters_atomically(parameters);
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
}
throw std::runtime_error("Unable to get result of set parameters service call.");
}
rcl_interfaces::msg::ListParametersResult
SyncParametersClient::list_parameters(
const std::vector<std::string> & parameter_prefixes,
uint64_t depth)
{
auto f = async_parameters_client_->list_parameters(parameter_prefixes, depth);
using rclcpp::executors::spin_node_until_future_complete;
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
rclcpp::executor::FutureReturnCode::SUCCESS)
{
return f.get();
}
throw std::runtime_error("Unable to get result of list parameters service call.");
}

View File

@@ -0,0 +1,150 @@
// 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.
#include "rclcpp/parameter_service.hpp"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
using rclcpp::parameter_service::ParameterService;
ParameterService::ParameterService(
const rclcpp::node::Node::SharedPtr node,
const rmw_qos_profile_t & qos_profile)
: node_(node)
{
std::weak_ptr<rclcpp::node::Node> captured_node = node_;
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
get_parameters_service_ = node_->create_service<rcl_interfaces::srv::GetParameters>(
std::string(node_->get_name()) + "__get_parameters",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::GetParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::GetParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto values = node->get_parameters(request->names);
for (auto & pvariant : values) {
response->values.push_back(pvariant.get_parameter_value());
}
},
qos_profile
);
get_parameter_types_service_ = node_->create_service<rcl_interfaces::srv::GetParameterTypes>(
std::string(node_->get_name()) + "__get_parameter_types",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Request> request,
std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto types = node->get_parameter_types(request->names);
std::transform(types.cbegin(), types.cend(),
std::back_inserter(response->types), [](const uint8_t & type) {
return static_cast<rclcpp::parameter::ParameterType>(type);
});
},
qos_profile
);
set_parameters_service_ = node_->create_service<rcl_interfaces::srv::SetParameters>(
std::string(node_->get_name()) + "__set_parameters",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::SetParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::SetParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
std::vector<rclcpp::parameter::ParameterVariant> pvariants;
for (auto & p : request->parameters) {
pvariants.push_back(rclcpp::parameter::ParameterVariant::from_parameter(p));
}
auto results = node->set_parameters(pvariants);
response->results = results;
},
qos_profile
);
set_parameters_atomically_service_ =
node_->create_service<rcl_interfaces::srv::SetParametersAtomically>(
std::string(node_->get_name()) + "__set_parameters_atomically",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Request> request,
std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
std::vector<rclcpp::parameter::ParameterVariant> pvariants;
std::transform(request->parameters.cbegin(), request->parameters.cend(),
std::back_inserter(pvariants),
[](const rcl_interfaces::msg::Parameter & p) {
return rclcpp::parameter::ParameterVariant::
from_parameter(p);
});
auto result = node->set_parameters_atomically(pvariants);
response->result = result;
},
qos_profile
);
describe_parameters_service_ = node_->create_service<rcl_interfaces::srv::DescribeParameters>(
std::string(node_->get_name()) + "__describe_parameters",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto descriptors = node->describe_parameters(request->names);
response->descriptors = descriptors;
},
qos_profile
);
list_parameters_service_ = node_->create_service<rcl_interfaces::srv::ListParameters>(
std::string(node_->get_name()) + "__list_parameters",
[captured_node](
const std::shared_ptr<rmw_request_id_t>,
const std::shared_ptr<rcl_interfaces::srv::ListParameters::Request> request,
std::shared_ptr<rcl_interfaces::srv::ListParameters::Response> response)
{
auto node = captured_node.lock();
if (!node) {
return;
}
auto result = node->list_parameters(request->prefixes, request->depth);
response->result = result;
},
qos_profile
);
// *INDENT-ON*
}

View File

@@ -0,0 +1,200 @@
// 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.
#include "rclcpp/publisher.hpp"
#include <rmw/error_handling.h>
#include <rmw/rmw.h>
#include <iostream>
#include <memory>
#include <mutex>
#include <sstream>
#include <string>
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rmw/impl/cpp/demangle.hpp"
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/allocator/allocator_deleter.hpp"
#include "rclcpp/exceptions.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
using rclcpp::publisher::PublisherBase;
PublisherBase::PublisherBase(
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rosidl_message_type_support_t & type_support,
const rcl_publisher_options_t & publisher_options)
: rcl_node_handle_(node_base->get_shared_rcl_node_handle()),
intra_process_publisher_id_(0), store_intra_process_message_(nullptr)
{
rcl_ret_t ret = rcl_publisher_init(
&publisher_handle_,
rcl_node_handle_.get(),
&type_support,
topic.c_str(),
&publisher_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
auto rcl_node_handle = rcl_node_handle_.get();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
topic,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle));
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create publisher");
}
// Life time of this object is tied to the publisher handle.
rmw_publisher_t * publisher_rmw_handle = rcl_publisher_get_rmw_handle(&publisher_handle_);
if (!publisher_rmw_handle) {
auto msg = std::string("failed to get rmw handle: ") + rcl_get_error_string_safe();
rcl_reset_error();
throw std::runtime_error(msg);
}
if (rmw_get_gid_for_publisher(publisher_rmw_handle, &rmw_gid_) != RMW_RET_OK) {
auto msg = std::string("failed to get publisher gid: ") + rmw_get_error_string_safe();
rmw_reset_error();
throw std::runtime_error(msg);
}
}
PublisherBase::~PublisherBase()
{
if (rcl_publisher_fini(&intra_process_publisher_handle_, rcl_node_handle_.get()) != RCL_RET_OK) {
fprintf(
stderr,
"Error in destruction of intra process rcl publisher handle: %s\n",
rcl_get_error_string_safe());
rcl_reset_error();
}
if (rcl_publisher_fini(&publisher_handle_, rcl_node_handle_.get()) != RCL_RET_OK) {
fprintf(
stderr,
"Error in destruction of rcl publisher handle: %s\n",
rcl_get_error_string_safe());
rcl_reset_error();
}
}
const char *
PublisherBase::get_topic_name() const
{
return rcl_publisher_get_topic_name(&publisher_handle_);
}
size_t
PublisherBase::get_queue_size() const
{
const rcl_publisher_options_t * publisher_options = rcl_publisher_get_options(&publisher_handle_);
if (!publisher_options) {
auto msg = std::string("failed to get publisher options: ") + rcl_get_error_string_safe();
rcl_reset_error();
throw std::runtime_error(msg);
}
return publisher_options->qos.depth;
}
const rmw_gid_t &
PublisherBase::get_gid() const
{
return rmw_gid_;
}
const rmw_gid_t &
PublisherBase::get_intra_process_gid() const
{
return intra_process_rmw_gid_;
}
bool
PublisherBase::operator==(const rmw_gid_t & gid) const
{
return *this == &gid;
}
bool
PublisherBase::operator==(const rmw_gid_t * gid) const
{
bool result = false;
auto ret = rmw_compare_gids_equal(gid, &this->get_gid(), &result);
if (ret != RMW_RET_OK) {
auto msg = std::string("failed to compare gids: ") + rmw_get_error_string_safe();
rmw_reset_error();
throw std::runtime_error(msg);
}
if (!result) {
ret = rmw_compare_gids_equal(gid, &this->get_intra_process_gid(), &result);
if (ret != RMW_RET_OK) {
auto msg = std::string("failed to compare gids: ") + rmw_get_error_string_safe();
rmw_reset_error();
throw std::runtime_error(msg);
}
}
return result;
}
void
PublisherBase::setup_intra_process(
uint64_t intra_process_publisher_id,
StoreMessageCallbackT callback,
const rcl_publisher_options_t & intra_process_options)
{
auto intra_process_topic_name = std::string(this->get_topic_name()) + "/_intra";
rcl_ret_t ret = rcl_publisher_init(
&intra_process_publisher_handle_,
rcl_node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
intra_process_topic_name.c_str(),
&intra_process_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
auto rcl_node_handle = rcl_node_handle_.get();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
intra_process_topic_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle));
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create intra process publisher");
}
intra_process_publisher_id_ = intra_process_publisher_id;
store_intra_process_message_ = callback;
// Life time of this object is tied to the publisher handle.
rmw_publisher_t * publisher_rmw_handle = rcl_publisher_get_rmw_handle(
&intra_process_publisher_handle_);
if (publisher_rmw_handle == nullptr) {
auto msg = std::string("Failed to get rmw publisher handle") + rcl_get_error_string_safe();
rcl_reset_error();
throw std::runtime_error(msg);
}
auto rmw_ret = rmw_get_gid_for_publisher(
publisher_rmw_handle, &intra_process_rmw_gid_);
if (rmw_ret != RMW_RET_OK) {
auto msg =
std::string("failed to create intra process publisher gid: ") + rmw_get_error_string_safe();
rmw_reset_error();
throw std::runtime_error(msg);
}
}

View File

@@ -0,0 +1,59 @@
// 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.
#include "rclcpp/service.hpp"
#include <functional>
#include <iostream>
#include <memory>
#include <sstream>
#include <string>
#include "rclcpp/any_service_callback.hpp"
#include "rclcpp/macros.hpp"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
using rclcpp::service::ServiceBase;
ServiceBase::ServiceBase(
std::shared_ptr<rcl_node_t> node_handle,
const std::string & service_name)
: node_handle_(node_handle), service_name_(service_name)
{}
ServiceBase::ServiceBase(std::shared_ptr<rcl_node_t> node_handle)
: node_handle_(node_handle)
{}
ServiceBase::~ServiceBase()
{}
std::string
ServiceBase::get_service_name()
{
return this->service_name_;
}
const rcl_service_t *
ServiceBase::get_service_handle()
{
return service_handle_;
}
rcl_node_t *
ServiceBase::get_rcl_node_handle() const
{
return node_handle_.get();
}

View File

@@ -0,0 +1,92 @@
// 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.
#include "rclcpp/subscription.hpp"
#include <cstdio>
#include <memory>
#include <string>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
using rclcpp::subscription::SubscriptionBase;
SubscriptionBase::SubscriptionBase(
std::shared_ptr<rcl_node_t> node_handle,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
const rcl_subscription_options_t & subscription_options)
: node_handle_(node_handle)
{
rcl_ret_t ret = rcl_subscription_init(
&subscription_handle_,
node_handle_.get(),
&type_support_handle,
topic_name.c_str(),
&subscription_options);
if (ret != RCL_RET_OK) {
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
auto rcl_node_handle = node_handle_.get();
// this will throw on any validation problem
rcl_reset_error();
expand_topic_or_service_name(
topic_name,
rcl_node_get_name(rcl_node_handle),
rcl_node_get_namespace(rcl_node_handle));
}
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create subscription");
}
}
SubscriptionBase::~SubscriptionBase()
{
if (rcl_subscription_fini(&subscription_handle_, node_handle_.get()) != RCL_RET_OK) {
std::stringstream ss;
ss << "Error in destruction of rcl subscription handle: " <<
rcl_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
}
if (rcl_subscription_fini(
&intra_process_subscription_handle_, node_handle_.get()) != RCL_RET_OK)
{
std::stringstream ss;
ss << "Error in destruction of rmw intra process subscription handle: " <<
rcl_get_error_string_safe() << '\n';
(std::cerr << ss.str()).flush();
}
}
const char *
SubscriptionBase::get_topic_name() const
{
return rcl_subscription_get_topic_name(&subscription_handle_);
}
const rcl_subscription_t *
SubscriptionBase::get_subscription_handle() const
{
return &subscription_handle_;
}
const rcl_subscription_t *
SubscriptionBase::get_intra_process_subscription_handle() const
{
return &intra_process_subscription_handle_;
}

View File

@@ -0,0 +1,77 @@
// 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.
#include "rclcpp/timer.hpp"
#include <chrono>
#include <string>
using rclcpp::timer::TimerBase;
TimerBase::TimerBase(std::chrono::nanoseconds period)
{
if (rcl_timer_init(
&timer_handle_, period.count(), nullptr,
rcl_get_default_allocator()) != RCL_RET_OK)
{
fprintf(stderr, "Couldn't initialize rcl timer handle: %s\n", rcl_get_error_string_safe());
}
}
TimerBase::~TimerBase()
{}
void
TimerBase::cancel()
{
if (rcl_timer_cancel(&timer_handle_) != RCL_RET_OK) {
throw std::runtime_error(std::string("Couldn't cancel timer: ") + rcl_get_error_string_safe());
}
}
void
TimerBase::reset()
{
if (rcl_timer_reset(&timer_handle_) != RCL_RET_OK) {
throw std::runtime_error(std::string("Couldn't reset timer: ") + rcl_get_error_string_safe());
}
}
bool
TimerBase::is_ready()
{
bool ready = false;
if (rcl_timer_is_ready(&timer_handle_, &ready) != RCL_RET_OK) {
throw std::runtime_error(std::string("Failed to check timer: ") + rcl_get_error_string_safe());
}
return ready;
}
std::chrono::nanoseconds
TimerBase::time_until_trigger()
{
int64_t time_until_next_call = 0;
if (rcl_timer_get_time_until_next_call(&timer_handle_, &time_until_next_call) != RCL_RET_OK) {
throw std::runtime_error(
std::string("Timer could not get time until next call: ") +
rcl_get_error_string_safe());
}
return std::chrono::nanoseconds(time_until_next_call);
}
const rcl_timer_t *
TimerBase::get_timer_handle()
{
return &timer_handle_;
}

View File

@@ -0,0 +1,116 @@
// Copyright 2014 Open Source Robotics Foundation, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "rclcpp/type_support_decl.hpp"
#include "rclcpp/visibility_control.hpp"
#include "rcl_interfaces/msg/intra_process_message.hpp"
#include "rcl_interfaces/msg/list_parameters_result.hpp"
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
#include "rcl_interfaces/msg/parameter_event.hpp"
#include "rcl_interfaces/msg/set_parameters_result.hpp"
#include "rcl_interfaces/srv/describe_parameters.hpp"
#include "rcl_interfaces/srv/get_parameter_types.hpp"
#include "rcl_interfaces/srv/get_parameters.hpp"
#include "rcl_interfaces/srv/list_parameters.hpp"
#include "rcl_interfaces/srv/set_parameters.hpp"
#include "rcl_interfaces/srv/set_parameters_atomically.hpp"
const rosidl_message_type_support_t *
rclcpp::type_support::get_intra_process_message_msg_type_support()
{
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::IntraProcessMessage
>();
}
const rosidl_message_type_support_t *
rclcpp::type_support::get_parameter_event_msg_type_support()
{
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::ParameterEvent
>();
}
const rosidl_message_type_support_t *
rclcpp::type_support::get_set_parameters_result_msg_type_support()
{
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::SetParametersResult
>();
}
const rosidl_message_type_support_t *
rclcpp::type_support::get_parameter_descriptor_msg_type_support()
{
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::ParameterDescriptor
>();
}
const rosidl_message_type_support_t *
rclcpp::type_support::get_list_parameters_result_msg_type_support()
{
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::ListParametersResult
>();
}
const rosidl_service_type_support_t *
rclcpp::type_support::get_get_parameters_srv_type_support()
{
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::GetParameters
>();
}
const rosidl_service_type_support_t *
rclcpp::type_support::get_get_parameter_types_srv_type_support()
{
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::GetParameterTypes
>();
}
const rosidl_service_type_support_t *
rclcpp::type_support::get_set_parameters_srv_type_support()
{
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::SetParameters
>();
}
const rosidl_service_type_support_t *
rclcpp::type_support::get_list_parameters_srv_type_support()
{
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::ListParameters
>();
}
const rosidl_service_type_support_t *
rclcpp::type_support::get_describe_parameters_srv_type_support()
{
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::DescribeParameters
>();
}
const rosidl_service_type_support_t *
rclcpp::type_support::get_set_parameters_atomically_srv_type_support()
{
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::SetParametersAtomically
>();
}

View File

@@ -0,0 +1,251 @@
// 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.
#include "rclcpp/utilities.hpp"
#include <atomic>
#include <condition_variable>
#include <csignal>
#include <cstdio>
#include <cstring>
#include <map>
#include <mutex>
#include <string>
#include <vector>
#include "rcl/error_handling.h"
#include "rcl/rcl.h"
#include "rmw/error_handling.h"
#include "rmw/rmw.h"
// Determine if sigaction is available
#if __APPLE__ || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
#define HAS_SIGACTION
#endif
/// Represent the status of the global interrupt signal.
static volatile sig_atomic_t g_signal_status = 0;
/// Guard conditions for interrupting the rmw implementation when the global interrupt signal fired.
static std::map<rcl_wait_set_t *, rcl_guard_condition_t> g_sigint_guard_cond_handles;
/// Mutex to protect g_sigint_guard_cond_handles
static std::mutex g_sigint_guard_cond_handles_mutex;
/// Condition variable for timed sleep (see sleep_for).
static std::condition_variable g_interrupt_condition_variable;
static std::atomic<bool> g_is_interrupted(false);
/// Mutex for protecting the global condition variable.
static std::mutex g_interrupt_mutex;
#ifdef HAS_SIGACTION
static struct sigaction old_action;
#else
static void (* old_signal_handler)(int) = 0;
#endif
void
#ifdef HAS_SIGACTION
signal_handler(int signal_value, siginfo_t * siginfo, void * context)
#else
signal_handler(int signal_value)
#endif
{
// TODO(wjwwood): remove? move to console logging at some point?
printf("signal_handler(%d)\n", signal_value);
#ifdef HAS_SIGACTION
if (old_action.sa_flags & SA_SIGINFO) {
if (old_action.sa_sigaction != NULL) {
old_action.sa_sigaction(signal_value, siginfo, context);
}
} else {
// *INDENT-OFF*
if (
old_action.sa_handler != NULL && // Is set
old_action.sa_handler != SIG_DFL && // Is not default
old_action.sa_handler != SIG_IGN) // Is not ignored
// *INDENT-ON*
{
old_action.sa_handler(signal_value);
}
}
#else
if (old_signal_handler) {
old_signal_handler(signal_value);
}
#endif
g_signal_status = signal_value;
{
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
for (auto & kv : g_sigint_guard_cond_handles) {
rcl_ret_t status = rcl_trigger_guard_condition(&(kv.second));
if (status != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to trigger guard condition: %s\n", rcl_get_error_string_safe());
}
}
}
g_is_interrupted.store(true);
g_interrupt_condition_variable.notify_all();
}
void
rclcpp::utilities::init(int argc, char * argv[])
{
g_is_interrupted.store(false);
if (rcl_init(argc, argv, rcl_get_default_allocator()) != RCL_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to initialize rmw implementation: ") + rcl_get_error_string_safe());
// *INDENT-ON*
}
#ifdef HAS_SIGACTION
struct sigaction action;
memset(&action, 0, sizeof(action));
sigemptyset(&action.sa_mask);
action.sa_sigaction = ::signal_handler;
action.sa_flags = SA_SIGINFO;
ssize_t ret = sigaction(SIGINT, &action, &old_action);
if (ret == -1)
#else
::old_signal_handler = std::signal(SIGINT, ::signal_handler);
// NOLINTNEXTLINE(readability/braces)
if (::old_signal_handler == SIG_ERR)
#endif
{
const size_t error_length = 1024;
// NOLINTNEXTLINE(runtime/arrays)
char error_string[error_length];
#ifndef _WIN32
#if (defined(_GNU_SOURCE) && !defined(ANDROID))
char * msg = strerror_r(errno, error_string, error_length);
if (msg != error_string) {
strncpy(error_string, msg, error_length);
msg[error_length - 1] = '\0';
}
#else
int error_status = strerror_r(errno, error_string, error_length);
if (error_status != 0) {
throw std::runtime_error("Failed to get error string for errno: " + std::to_string(errno));
}
#endif
#else
strerror_s(error_string, error_length, errno);
#endif
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("Failed to set SIGINT signal handler: (" + std::to_string(errno) + ")") +
error_string);
// *INDENT-ON*
}
}
bool
rclcpp::utilities::ok()
{
return ::g_signal_status == 0;
}
static std::mutex on_shutdown_mutex_;
static std::vector<std::function<void(void)>> on_shutdown_callbacks_;
void
rclcpp::utilities::shutdown()
{
g_signal_status = SIGINT;
{
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
for (auto & kv : g_sigint_guard_cond_handles) {
if (rcl_trigger_guard_condition(&(kv.second)) != RCL_RET_OK) {
fprintf(stderr,
"[rclcpp::error] failed to trigger sigint guard condition: %s\n",
rcl_get_error_string_safe());
}
}
}
g_is_interrupted.store(true);
g_interrupt_condition_variable.notify_all();
{
std::lock_guard<std::mutex> lock(on_shutdown_mutex_);
for (auto & on_shutdown_callback : on_shutdown_callbacks_) {
on_shutdown_callback();
}
}
}
void
rclcpp::utilities::on_shutdown(std::function<void(void)> callback)
{
std::lock_guard<std::mutex> lock(on_shutdown_mutex_);
on_shutdown_callbacks_.push_back(callback);
}
rcl_guard_condition_t *
rclcpp::utilities::get_sigint_guard_condition(rcl_wait_set_t * waitset)
{
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
auto kv = g_sigint_guard_cond_handles.find(waitset);
if (kv != g_sigint_guard_cond_handles.end()) {
return &kv->second;
} else {
rcl_guard_condition_t handle =
rcl_get_zero_initialized_guard_condition();
rcl_guard_condition_options_t options = rcl_guard_condition_get_default_options();
if (rcl_guard_condition_init(&handle, options) != RCL_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(std::string(
"Couldn't initialize guard condition: ") + rcl_get_error_string_safe());
// *INDENT-ON*
}
g_sigint_guard_cond_handles[waitset] = handle;
return &g_sigint_guard_cond_handles[waitset];
}
}
void
rclcpp::utilities::release_sigint_guard_condition(rcl_wait_set_t * waitset)
{
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
auto kv = g_sigint_guard_cond_handles.find(waitset);
if (kv != g_sigint_guard_cond_handles.end()) {
if (rcl_guard_condition_fini(&kv->second) != RCL_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(std::string(
"Failed to destroy sigint guard condition: ") +
rcl_get_error_string_safe());
// *INDENT-ON*
}
g_sigint_guard_cond_handles.erase(kv);
} else {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(std::string(
"Tried to release sigint guard condition for nonexistent waitset"));
// *INDENT-ON*
}
}
bool
rclcpp::utilities::sleep_for(const std::chrono::nanoseconds & nanoseconds)
{
std::chrono::nanoseconds time_left = nanoseconds;
{
std::unique_lock<std::mutex> lock(::g_interrupt_mutex);
auto start = std::chrono::steady_clock::now();
::g_interrupt_condition_variable.wait_for(lock, nanoseconds);
time_left -= std::chrono::steady_clock::now() - start;
}
if (time_left > std::chrono::nanoseconds::zero() && !g_is_interrupted) {
return sleep_for(time_left);
}
// Return true if the timeout elapsed successfully, otherwise false.
return !g_is_interrupted;
}

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