Compare commits

...

72 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
121 changed files with 8292 additions and 1377 deletions

1
rclcpp/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
doc_output

View File

@@ -3,15 +3,26 @@ 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(rmw_implementation_cmake REQUIRED)
find_package(rosidl_generator_cpp REQUIRED)
find_package(rosidl_typesupport_c REQUIRED)
find_package(rosidl_typesupport_cpp REQUIRED)
if(NOT WIN32)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra")
# 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)
@@ -26,6 +37,7 @@ set(${PROJECT_NAME}_SRCS
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
@@ -33,11 +45,17 @@ set(${PROJECT_NAME}_SRCS
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/node.cpp
src/rclcpp/service.cpp
src/rclcpp/subscription.cpp
src/rclcpp/timer.cpp
@@ -45,34 +63,32 @@ set(${PROJECT_NAME}_SRCS
src/rclcpp/utilities.cpp
)
macro(target)
if(NOT target_suffix STREQUAL "")
get_rcl_information("${rmw_implementation}" "rcl${target_suffix}")
endif()
add_library(${PROJECT_NAME}${target_suffix} SHARED
${${PROJECT_NAME}_SRCS})
ament_target_dependencies(${PROJECT_NAME}${target_suffix}
"rcl${target_suffix}"
"rosidl_generator_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}${target_suffix}
PRIVATE "RCLCPP_BUILDING_LIBRARY")
# 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}${target_suffix}
ARCHIVE DESTINATION lib
LIBRARY DESTINATION lib
RUNTIME DESTINATION bin
)
endmacro()
call_for_each_rmw_implementation(target GENERATE_DEFAULT)
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)
@@ -82,12 +98,35 @@ if(BUILD_TESTING)
find_package(ament_lint_auto REQUIRED)
ament_lint_auto_find_test_dependencies()
find_package(rmw_implementation_cmake REQUIRED)
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()
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)
@@ -97,6 +136,7 @@ if(BUILD_TESTING)
${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)
@@ -106,8 +146,29 @@ if(BUILD_TESTING)
${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)
@@ -115,11 +176,80 @@ if(BUILD_TESTING)
${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()
ament_package(

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

@@ -1,92 +0,0 @@
# Copyright 2015 Open Source Robotics Foundation, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
# Get all information about rclcpp for a specific RMW implementation.
#
# It sets the common variables _DEFINITIONS, _INCLUDE_DIRS and _LIBRARIES
# with the given prefix.
#
# :param rmw_implementation: the RMW implementation name
# :type target: string
# :param var_prefix: the prefix of all output variable names
# :type var_prefix: string
#
macro(get_rclcpp_information rmw_implementation var_prefix)
# pretend to be a "package"
# so that the variables can be used by various functions / macros
set(${var_prefix}_FOUND TRUE)
# Get rcl using the existing macro
if(NOT target_suffix STREQUAL "")
get_rcl_information("${rmw_implementation}" "rcl${target_suffix}")
endif()
# include directories
normalize_path(${var_prefix}_INCLUDE_DIRS
"${rclcpp_DIR}/../../../include")
# libraries
set(_libs)
# search for library relative to this CMake file
set(_library_target "rclcpp")
get_available_rmw_implementations(_rmw_impls)
list(LENGTH _rmw_impls _rmw_impls_length)
if(_rmw_impls_length GREATER 1)
set(_library_target "${_library_target}__${rmw_implementation}")
endif()
set(_lib "NOTFOUND")
find_library(
_lib NAMES "${_library_target}"
PATHS "${rclcpp_DIR}/../../../lib"
NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH
)
if(NOT _lib)
# warn about not existing library and ignore it
message(WARNING "Package 'rclcpp' doesn't contain the library '${_library_target}'")
elseif(NOT IS_ABSOLUTE "${_lib}")
# the found library must be an absolute path
message(FATAL_ERROR "Package 'rclcpp' found the library '${_library_target}' at '${_lib}' which is not an absolute path")
elseif(NOT EXISTS "${_lib}")
# the found library must exist
message(FATAL_ERROR "Package 'rclcpp' found the library '${_lib}' which doesn't exist")
else()
list(APPEND _libs "${_lib}")
endif()
# dependencies
set(_exported_dependencies
"rcl_interfaces"
"rcl${target_suffix}"
"rosidl_generator_cpp")
set(${var_prefix}_DEFINITIONS)
foreach(_dep ${_exported_dependencies})
if(NOT ${_dep}_FOUND)
find_package("${_dep}" QUIET REQUIRED)
endif()
if(${_dep}_DEFINITIONS)
list_append_unique(${var_prefix}_DEFINITIONS "${${_dep}_DEFINITIONS}")
endif()
if(${_dep}_INCLUDE_DIRS)
list_append_unique(${var_prefix}_INCLUDE_DIRS "${${_dep}_INCLUDE_DIRS}")
endif()
if(${_dep}_LIBRARIES)
list(APPEND _libs "${${_dep}_LIBRARIES}")
endif()
endforeach()
if(_libs)
ament_libraries_deduplicate(_libs "${_libs}")
endif()
set(${var_prefix}_LIBRARIES "${_libs}")
endmacro()

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

@@ -74,6 +74,8 @@ rcl_allocator_t get_rcl_allocator(Alloc & allocator)
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;
}

View File

@@ -17,8 +17,13 @@
#include <memory>
#include "rclcpp/callback_group.hpp"
#include "rclcpp/client.hpp"
#include "rclcpp/macros.hpp"
#include "rclcpp/node.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
@@ -28,7 +33,7 @@ namespace executor
struct AnyExecutable
{
RCLCPP_SMART_PTR_DEFINITIONS(AnyExecutable);
RCLCPP_SMART_PTR_DEFINITIONS(AnyExecutable)
RCLCPP_PUBLIC
AnyExecutable();
@@ -44,7 +49,7 @@ 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;
};
} // namespace executor

View File

@@ -21,6 +21,7 @@
#include <memory>
#include <stdexcept>
#include <type_traits>
#include <utility>
#include "rclcpp/allocator/allocator_common.hpp"
#include "rclcpp/function_traits.hpp"

View File

@@ -30,10 +30,12 @@ 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
{
@@ -46,10 +48,12 @@ 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)
RCLCPP_PUBLIC
explicit CallbackGroup(CallbackGroupType group_type);
@@ -63,7 +67,7 @@ public:
get_timer_ptrs() const;
RCLCPP_PUBLIC
const std::vector<rclcpp::service::ServiceBase::SharedPtr> &
const std::vector<rclcpp::service::ServiceBase::WeakPtr> &
get_service_ptrs() const;
RCLCPP_PUBLIC
@@ -78,8 +82,8 @@ public:
const CallbackGroupType &
type() const;
private:
RCLCPP_DISABLE_COPY(CallbackGroup);
protected:
RCLCPP_DISABLE_COPY(CallbackGroup)
RCLCPP_PUBLIC
void
@@ -102,7 +106,7 @@ private:
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::SharedPtr> service_ptrs_;
std::vector<rclcpp::service::ServiceBase::WeakPtr> service_ptrs_;
std::vector<rclcpp::client::ClientBase::WeakPtr> client_ptrs_;
std::atomic_bool can_be_taken_from_;
};

View File

@@ -27,10 +27,13 @@
#include "rcl/error_handling.h"
#include "rcl/wait.h"
#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"
@@ -39,10 +42,10 @@
namespace rclcpp
{
namespace node
namespace node_interfaces
{
class Node;
} // namespace node
class NodeBaseInterface;
} // namespace node_interfaces
namespace client
{
@@ -50,11 +53,12 @@ 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<rclcpp::node::Node> parent_node,
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name);
RCLCPP_PUBLIC
@@ -88,7 +92,7 @@ public:
std::shared_ptr<rmw_request_id_t> request_header, std::shared_ptr<void> response) = 0;
protected:
RCLCPP_DISABLE_COPY(ClientBase);
RCLCPP_DISABLE_COPY(ClientBase)
RCLCPP_PUBLIC
bool
@@ -98,7 +102,7 @@ protected:
rcl_node_t *
get_rcl_node_handle() const;
std::weak_ptr<rclcpp::node::Node> node_;
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();
@@ -124,25 +128,36 @@ public:
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<rclcpp::node::Node> parent_node,
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(parent_node, service_name)
: ClientBase(node_base, node_graph, service_name)
{
using rosidl_generator_cpp::get_service_type_support_handle;
using rosidl_typesupport_cpp::get_service_type_support_handle;
auto service_type_support_handle =
get_service_type_support_handle<ServiceT>();
if (rcl_client_init(&client_handle_, this->get_rcl_node_handle(),
service_type_support_handle, service_name.c_str(), &client_options) != RCL_RET_OK)
{
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("could not create client: ") +
rcl_get_error_string_safe());
// *INDENT-ON*
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");
}
}
@@ -150,7 +165,8 @@ public:
{
if (rcl_client_fini(&client_handle_, this->get_rcl_node_handle()) != RCL_RET_OK) {
fprintf(stderr,
"Error in destruction of rmw client handle: %s\n", rmw_get_error_string_safe());
"Error in destruction of rcl client handle: %s\n", rcl_get_error_string_safe());
rcl_reset_error();
}
}
@@ -209,11 +225,9 @@ public:
{
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
int64_t sequence_number;
if (RCL_RET_OK != rcl_send_request(get_client_handle(), request.get(), &sequence_number)) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to send request: ") + rcl_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>();
@@ -250,7 +264,7 @@ public:
}
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_;

View File

@@ -21,6 +21,7 @@
#include <typeindex>
#include <typeinfo>
#include <unordered_map>
#include <utility>
#include "rclcpp/macros.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -35,7 +36,7 @@ namespace context
class Context
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(Context);
RCLCPP_SMART_PTR_DEFINITIONS(Context)
RCLCPP_PUBLIC
Context();
@@ -67,7 +68,7 @@ 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_;

View File

@@ -28,7 +28,7 @@ namespace default_context
class DefaultContext : public rclcpp::context::Context
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(DefaultContext);
RCLCPP_SMART_PTR_DEFINITIONS(DefaultContext)
RCLCPP_PUBLIC
DefaultContext();

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

@@ -29,7 +29,7 @@ namespace event
class Event
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Event);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Event)
RCLCPP_PUBLIC
Event();
@@ -47,7 +47,7 @@ public:
check_and_clear();
private:
RCLCPP_DISABLE_COPY(Event);
RCLCPP_DISABLE_COPY(Event)
std::atomic_bool state_;
};

View File

@@ -35,18 +35,88 @@ public:
: 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 reset_error if true rcl_reset_error() is called before returning
* \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 = "", bool reset_error = true);
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
{
@@ -95,6 +165,22 @@ 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

View File

@@ -28,16 +28,21 @@
#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
{
@@ -88,7 +93,7 @@ static inline ExecutorArgs create_default_executor_arguments()
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.
@@ -114,7 +119,12 @@ public:
*/
RCLCPP_PUBLIC
virtual void
add_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify = true);
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.
/**
@@ -125,7 +135,12 @@ public:
*/
RCLCPP_PUBLIC
virtual void
remove_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify = true);
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.
/**
@@ -136,7 +151,7 @@ public:
*/
template<typename T = std::milli>
void
spin_node_once(rclcpp::node::Node::SharedPtr node,
spin_node_once(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t, T>(-1))
{
return spin_node_once_nanoseconds(
@@ -145,13 +160,30 @@ public:
);
}
/// 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.
*/
RCLCPP_PUBLIC
void
spin_node_some(rclcpp::node::Node::SharedPtr node);
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.
/**
@@ -170,14 +202,13 @@ public:
/// 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.
* 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.
* `-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
@@ -247,7 +278,9 @@ public:
protected:
RCLCPP_PUBLIC
void
spin_node_once_nanoseconds(rclcpp::node::Node::SharedPtr node, std::chrono::nanoseconds timeout);
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,
@@ -284,7 +317,7 @@ protected:
wait_for_work(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
RCLCPP_PUBLIC
rclcpp::node::Node::SharedPtr
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group);
RCLCPP_PUBLIC
@@ -316,9 +349,9 @@ protected:
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::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
};
} // namespace executor

View File

@@ -16,6 +16,7 @@
#define RCLCPP__EXECUTORS_HPP_
#include <future>
#include <memory>
#include "rclcpp/executors/multi_threaded_executor.hpp"
#include "rclcpp/executors/single_threaded_executor.hpp"
@@ -27,16 +28,24 @@ 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.
/** \param[in] node_ptr Shared pointer to the node to spin. */
RCLCPP_PUBLIC
void
spin_some(node::Node::SharedPtr node_ptr);
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.
/** \param[in] node_ptr Shared pointer to the node to spin. */
RCLCPP_PUBLIC
void
spin(node::Node::SharedPtr node_ptr);
spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
RCLCPP_PUBLIC
void
spin(rclcpp::node::Node::SharedPtr node_ptr);
namespace executors
{
@@ -48,16 +57,19 @@ using rclcpp::executors::single_threaded_executor::SingleThreadedExecutor;
/**
* \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>
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))
{
@@ -69,18 +81,44 @@ spin_node_until_future_complete(
return retcode;
}
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);
}
} // namespace executors
template<typename FutureT, typename TimeT = std::milli>
rclcpp::executor::FutureReturnCode
spin_until_future_complete(
node::Node::SharedPtr node_ptr, std::shared_future<FutureT> & future,
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

@@ -34,7 +34,7 @@ namespace multi_threaded_executor
class MultiThreadedExecutor : public executor::Executor
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MultiThreadedExecutor);
RCLCPP_SMART_PTR_DEFINITIONS(MultiThreadedExecutor)
RCLCPP_PUBLIC
MultiThreadedExecutor(
@@ -57,7 +57,7 @@ protected:
run(size_t this_thread_number);
private:
RCLCPP_DISABLE_COPY(MultiThreadedExecutor);
RCLCPP_DISABLE_COPY(MultiThreadedExecutor)
std::mutex wait_mutex_;
size_t number_of_threads_;

View File

@@ -42,7 +42,7 @@ 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.
RCLCPP_PUBLIC
@@ -61,7 +61,7 @@ public:
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

@@ -24,16 +24,12 @@
#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 node
{
class Node;
} // namespace node
namespace graph_listener
{
@@ -72,7 +68,8 @@ public:
virtual ~GraphListener();
/// Start the graph listener's listen thread if it hasn't been started.
/* This function is thread-safe.
/**
* This function is thread-safe.
*
* \throws GraphListenerShutdownError if the GraphListener is shutdown
*/
@@ -82,7 +79,7 @@ public:
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
@@ -91,20 +88,22 @@ public:
RCLCPP_PUBLIC
virtual
void
add_node(rclcpp::node::Node * node);
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.
/**
* Also return false if given nullptr.
*
* \throws std::system_error anything std::mutex::lock() throws
*/
RCLCPP_PUBLIC
virtual
bool
has_node(rclcpp::node::Node * node);
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
@@ -112,10 +111,11 @@ public:
RCLCPP_PUBLIC
virtual
void
remove_node(rclcpp::node::Node * node);
remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
/// Stop the listening thread.
/* The thread cannot be restarted, and the class is defunct after calling.
/**
* 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
@@ -152,16 +152,16 @@ protected:
run_loop();
private:
RCLCPP_DISABLE_COPY(GraphListener);
RCLCPP_DISABLE_COPY(GraphListener)
std::thread listener_thread_;
bool is_started_;
std::atomic_bool is_shutdown_;
mutable std::mutex shutdown_mutex_;
mutable std::mutex nodes_barrier_mutex_;
mutable std::mutex nodes_mutex_;
std::vector<rclcpp::node::Node *> nodes_;
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_;

View File

@@ -40,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
@@ -54,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
@@ -120,10 +121,10 @@ 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(
@@ -133,7 +134,8 @@ 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,
@@ -149,7 +151,8 @@ public:
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.
*/
@@ -158,7 +161,8 @@ public:
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,7 +198,8 @@ public:
}
/// 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.
*/
@@ -203,7 +208,8 @@ public:
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
@@ -261,7 +267,8 @@ public:
}
/// 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

View File

@@ -17,6 +17,7 @@
#include <algorithm>
#include <atomic>
#include <cstring>
#include <functional>
#include <limits>
#include <map>
@@ -41,7 +42,7 @@ namespace intra_process_manager
class IntraProcessManagerImplBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerImplBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerImplBase)
IntraProcessManagerImplBase() = default;
~IntraProcessManagerImplBase() = default;
@@ -78,7 +79,7 @@ public:
matches_any_publishers(const rmw_gid_t * id) const = 0;
private:
RCLCPP_DISABLE_COPY(IntraProcessManagerImplBase);
RCLCPP_DISABLE_COPY(IntraProcessManagerImplBase)
};
template<typename Allocator = std::allocator<void>>
@@ -92,6 +93,8 @@ public:
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);
}
@@ -242,7 +245,7 @@ public:
}
private:
RCLCPP_DISABLE_COPY(IntraProcessManagerImpl);
RCLCPP_DISABLE_COPY(IntraProcessManagerImpl)
template<typename T>
using RebindAlloc = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
@@ -253,8 +256,20 @@ private:
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_;
@@ -262,7 +277,7 @@ private:
struct PublisherInfo
{
RCLCPP_DISABLE_COPY(PublisherInfo);
RCLCPP_DISABLE_COPY(PublisherInfo)
PublisherInfo() = default;

View File

@@ -12,10 +12,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <memory>
#include <utility>
#ifndef RCLCPP__MACROS_HPP_
#define RCLCPP__MACROS_HPP_
/* Disables the copy constructor and operator= for the given class.
/**
* 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__)

View File

@@ -20,6 +20,7 @@
#include <cstdint>
#include <memory>
#include <mutex>
#include <utility>
#include <vector>
#include "rclcpp/allocator/allocator_common.hpp"
@@ -34,11 +35,12 @@ namespace mapped_ring_buffer
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.
@@ -57,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>;
@@ -65,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)
@@ -85,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.
@@ -109,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.
@@ -146,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.
*
@@ -168,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.
*
@@ -206,7 +214,7 @@ public:
}
private:
RCLCPP_DISABLE_COPY(MappedRingBuffer<T, Alloc>);
RCLCPP_DISABLE_COPY(MappedRingBuffer<T, Alloc>)
struct element
{

View File

@@ -23,7 +23,7 @@
#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
@@ -40,8 +40,8 @@ namespace memory_strategy
class RCLCPP_PUBLIC MemoryStrategy
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy);
using WeakNodeVector = std::vector<std::weak_ptr<rclcpp::node::Node>>;
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy)
using WeakNodeVector = std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>;
virtual bool collect_entities(const WeakNodeVector & weak_nodes) = 0;
@@ -88,7 +88,7 @@ public:
static rclcpp::client::ClientBase::SharedPtr
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);

View File

@@ -28,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;
@@ -56,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

@@ -39,6 +39,12 @@
#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"
@@ -46,85 +52,82 @@
#include "rclcpp/timer.hpp"
#include "rclcpp/visibility_control.hpp"
namespace rcl
{
struct rcl_node_t;
} // namespace rcl
namespace rclcpp
{
namespace graph_listener
{
class GraphListener;
} // namespace graph_listener
namespace node
{
class InvalidEventError : public std::runtime_error
{
public:
InvalidEventError()
: std::runtime_error("event is invalid") {}
};
class EventNotRegisteredError : public std::runtime_error
{
public:
EventNotRegisteredError()
: std::runtime_error("event already registered") {}
};
/// Node is the single point of entry for creating publishers and subscribers.
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.
*/
RCLCPP_PUBLIC
explicit Node(const std::string & node_name, bool use_intra_process_comms = false);
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.
/** \return The name of the node. */
RCLCPP_PUBLIC
const std::string &
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);
@@ -133,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,
@@ -150,14 +156,19 @@ 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,
@@ -176,14 +187,19 @@ 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,
@@ -200,30 +216,13 @@ public:
* \param[in] callback User-defined callback function.
* \param[in] group Callback group to execute this timer's callback in.
*/
template<typename CallbackType>
typename rclcpp::timer::WallTimer<CallbackType>::SharedPtr
template<typename DurationT = std::milli, typename CallbackT>
typename rclcpp::timer::WallTimer<CallbackT>::SharedPtr
create_wall_timer(
std::chrono::nanoseconds period,
CallbackType callback,
std::chrono::duration<int64_t, DurationT> period,
CallbackT callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
/// Create a timer.
/**
* \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
@@ -249,10 +248,56 @@ public:
rcl_interfaces::msg::SetParametersResult
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
template<typename ParameterT>
void
set_parameter_if_not_set(
const std::string & name,
const ParameterT & value);
RCLCPP_PUBLIC
std::vector<rclcpp::parameter::ParameterVariant>
get_parameters(const std::vector<std::string> & names) const;
RCLCPP_PUBLIC
rclcpp::parameter::ParameterVariant
get_parameter(const std::string & name) const;
RCLCPP_PUBLIC
bool
get_parameter(
const std::string & name,
rclcpp::parameter::ParameterVariant & parameter) 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;
/// 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;
RCLCPP_PUBLIC
std::vector<rcl_interfaces::msg::ParameterDescriptor>
describe_parameters(const std::vector<std::string> & names) const;
@@ -265,10 +310,24 @@ 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::string>
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;
@@ -277,53 +336,6 @@ public:
size_t
count_subscribers(const std::string & topic_name) const;
RCLCPP_PUBLIC
const CallbackGroupWeakPtrList &
get_callback_groups() const;
RCLCPP_PUBLIC
const rcl_guard_condition_t *
get_notify_guard_condition() const;
RCLCPP_PUBLIC
const rcl_guard_condition_t *
get_graph_guard_condition() const;
RCLCPP_PUBLIC
const rcl_node_t *
get_rcl_node_handle() const;
/// Return the rcl_node_t node handle (non-const version).
RCLCPP_PUBLIC
rcl_node_t *
get_rcl_node_handle();
/// 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
std::shared_ptr<rcl_node_t>
get_shared_node_handle();
/// 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
void
notify_graph_change();
/// Notify any and all blocking node actions that shutdown has occurred.
RCLCPP_PUBLIC
void
notify_shutdown();
/// 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
@@ -334,7 +346,8 @@ public:
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.
/**
* 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
@@ -346,63 +359,51 @@ public:
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout);
/// Return the number of on loan graph events, see get_graph_event().
/* This is typically only used by the rclcpp::graph_listener::GraphListener.
*/
/// Return the Node's internal NodeBaseInterface implementation.
RCLCPP_PUBLIC
size_t
count_graph_users();
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
get_node_base_interface();
std::atomic_bool has_executor;
/// 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);
RCLCPP_DISABLE_COPY(Node)
RCLCPP_PUBLIC
bool
group_in_node(callback_group::CallbackGroup::SharedPtr group);
std::string name_;
std::shared_ptr<rcl_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_;
/// Guard condition for notifying the Executor of changes to this node.
mutable std::mutex notify_guard_condition_mutex_;
rcl_guard_condition_t notify_guard_condition_ = rcl_get_zero_initialized_guard_condition();
bool notify_guard_condition_is_valid_;
/// 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.
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_;
std::map<std::string, rclcpp::parameter::ParameterVariant> parameters_;
publisher::Publisher<rcl_interfaces::msg::ParameterEvent>::SharedPtr events_publisher_;
};
} // namespace node

View File

@@ -38,6 +38,8 @@
#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"
@@ -50,8 +52,8 @@ namespace rclcpp
namespace node
{
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)
@@ -61,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)
@@ -73,67 +75,16 @@ Node::create_publisher(
if (!allocator) {
allocator = std::make_shared<Alloc>();
}
auto publisher_options = rcl_publisher_get_default_options();
publisher_options.qos = qos_profile;
auto message_alloc =
std::make_shared<typename publisher::Publisher<MessageT, Alloc>::MessageAlloc>(
*allocator.get());
publisher_options.allocator = allocator::get_rcl_allocator<MessageT>(
*message_alloc.get());
auto publisher = publisher::Publisher<MessageT, Alloc>::make_shared(
node_handle_, topic_name, publisher_options, message_alloc);
if (use_intra_process_comms_) {
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,
publisher_options);
}
if (rcl_trigger_guard_condition(&notify_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on publisher creation: ") + rmw_get_error_string());
}
return publisher;
return rclcpp::create_publisher<MessageT, Alloc, PublisherT>(
this->node_topics_.get(),
topic_name,
qos_profile,
use_intra_process_comms_,
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,
CallbackT && callback,
@@ -142,103 +93,31 @@ Node::create_subscription(
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(std::forward<CallbackT>(callback));
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 message_alloc =
std::make_shared<typename subscription::Subscription<MessageT, Alloc>::MessageAlloc>();
auto subscription_options = rcl_subscription_get_default_options();
subscription_options.qos = qos_profile;
subscription_options.allocator = rclcpp::allocator::get_rcl_allocator<MessageT>(
*message_alloc.get());
subscription_options.ignore_local_publications = ignore_local_publications;
using rclcpp::subscription::Subscription;
using rclcpp::subscription::SubscriptionBase;
auto sub = Subscription<MessageT, Alloc>::make_shared(
node_handle_,
return rclcpp::create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
this->node_topics_.get(),
topic_name,
subscription_options,
any_subscription_callback,
msg_mem_strat);
auto sub_base_ptr = std::dynamic_pointer_cast<SubscriptionBase>(sub);
// Setup intra process.
if (use_intra_process_comms_) {
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 = qos_profile;
intra_process_options.ignore_local_publications = false;
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,
[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);
},
intra_process_options
);
// *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_++;
if (rcl_trigger_guard_condition(&notify_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on subscription creation: ") + rmw_get_error_string());
}
return sub;
std::forward<CallbackT>(callback),
qos_profile,
group,
ignore_local_publications,
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,
@@ -251,7 +130,7 @@ 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,
std::forward<CallbackT>(callback),
qos,
@@ -261,30 +140,17 @@ Node::create_subscription(
allocator);
}
template<typename CallbackType>
typename rclcpp::timer::WallTimer<CallbackType>::SharedPtr
template<typename DurationT, typename CallbackT>
typename rclcpp::timer::WallTimer<CallbackT>::SharedPtr
Node::create_wall_timer(
std::chrono::nanoseconds period,
CallbackType callback,
std::chrono::duration<int64_t, DurationT> period,
CallbackT callback,
rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
auto timer = rclcpp::timer::WallTimer<CallbackType>::make_shared(
period, std::move(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_++;
if (rcl_trigger_guard_condition(&notify_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on timer creation: ") + rmw_get_error_string());
}
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;
}
@@ -302,27 +168,13 @@ Node::create_client(
using rclcpp::client::ClientBase;
auto cli = Client<ServiceT>::make_shared(
shared_from_this(),
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_++;
if (rcl_trigger_guard_condition(&notify_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on client creation: ") + rmw_get_error_string());
}
node_services_->add_client(cli_base_ptr, group);
return cli;
}
@@ -341,27 +193,61 @@ Node::create_service(
service_options.qos = qos_profile;
auto serv = service::Service<ServiceT>::make_shared(
node_handle_,
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_++;
if (rcl_trigger_guard_condition(&notify_guard_condition_) != RCL_RET_OK) {
throw std::runtime_error(
std::string(
"Failed to notify waitset on service creation: ") + rmw_get_error_string());
}
node_services_->add_service(serv_base_ptr, group);
return serv;
}
template<typename CallbackT>
void
Node::register_param_change_callback(CallbackT && callback)
{
this->node_parameters_->register_param_change_callback(std::forward<CallbackT>(callback));
}
template<typename ParameterT>
void
Node::set_parameter_if_not_set(
const std::string & name,
const ParameterT & value)
{
rclcpp::parameter::ParameterVariant parameter_variant;
if (!this->get_parameter(name, parameter_variant)) {
this->set_parameters({
rclcpp::parameter::ParameterVariant(name, value),
});
}
}
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;
}
template<typename ParameterT>
bool
Node::get_parameter_or(
const std::string & name,
ParameterT & value,
const ParameterT & alternative_value) const
{
bool got_parameter = get_parameter(name, value);
if (!got_parameter) {
value = alternative_value;
}
return got_parameter;
}
} // namespace node
} // namespace rclcpp

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

@@ -216,7 +216,7 @@ private:
};
/* Return a json encoded version of the parameter intended for a dict. */
/// Return a json encoded version of the parameter intended for a dict.
RCLCPP_PUBLIC
std::string
_to_json_dict_entry(const ParameterVariant & param);
@@ -235,12 +235,12 @@ operator<<(std::ostream & os, const std::vector<ParameterVariant> & parameters);
namespace std
{
/* Return a json encoded version of the parameter intended for a list. */
/// Return a json encoded version of the parameter intended for a list.
RCLCPP_PUBLIC
std::string
to_string(const rclcpp::parameter::ParameterVariant & param);
/* Return a json encoded version of a vector of parameters, as a string*/
/// 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);

View File

@@ -16,6 +16,7 @@
#define RCLCPP__PARAMETER_CLIENT_HPP_
#include <string>
#include <utility>
#include <vector>
#include "rcl_interfaces/msg/parameter.hpp"
@@ -43,7 +44,7 @@ namespace parameter_client
class AsyncParametersClient
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(AsyncParametersClient);
RCLCPP_SMART_PTR_DEFINITIONS(AsyncParametersClient)
RCLCPP_PUBLIC
AsyncParametersClient(
@@ -117,7 +118,7 @@ private:
class SyncParametersClient
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient);
RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient)
RCLCPP_PUBLIC
explicit SyncParametersClient(

View File

@@ -38,7 +38,7 @@ namespace parameter_service
class ParameterService
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(ParameterService);
RCLCPP_SMART_PTR_DEFINITIONS(ParameterService)
RCLCPP_PUBLIC
explicit ParameterService(

View File

@@ -28,67 +28,72 @@
#include "rcl/publisher.h"
#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/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 rcl representation of the owner node.
* \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<rcl_node_t> node_handle,
std::string topic,
size_t queue_size);
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rosidl_message_type_support_t & type_support,
const rcl_publisher_options_t & publisher_options);
RCLCPP_PUBLIC
virtual ~PublisherBase();
/// Get the topic that this publisher publishes on.
// \return The topic name.
/** \return The topic name. */
RCLCPP_PUBLIC
const std::string &
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;
/// 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;
/// 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;
@@ -113,9 +118,9 @@ public:
bool
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(
@@ -123,14 +128,11 @@ protected:
StoreMessageCallbackT callback,
const rcl_publisher_options_t & intra_process_options);
std::shared_ptr<rcl_node_t> node_handle_;
protected:
std::shared_ptr<rcl_node_t> rcl_node_handle_;
rcl_publisher_t publisher_handle_ = rcl_get_zero_initialized_publisher();
rcl_publisher_t intra_process_publisher_handle_ = rcl_get_zero_initialized_publisher();
rcl_allocator_t rcl_allocator_ = rcl_get_default_allocator();
std::string topic_;
size_t queue_size_;
uint64_t intra_process_publisher_id_;
StoreMessageCallbackT store_intra_process_message_;
@@ -143,74 +145,38 @@ protected:
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<rcl_node_t> node_handle,
std::string topic,
rclcpp::node_interfaces::NodeBaseInterface * node_base,
const std::string & topic,
const rcl_publisher_options_t & publisher_options,
std::shared_ptr<MessageAlloc> allocator)
: PublisherBase(node_handle, topic, publisher_options.qos.depth), message_allocator_(allocator)
const std::shared_ptr<MessageAlloc> & allocator)
: PublisherBase(
node_base,
topic,
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
publisher_options),
message_allocator_(allocator)
{
using rosidl_generator_cpp::get_message_type_support_handle;
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
rcl_allocator_ = publisher_options.allocator;
auto type_support_handle = get_message_type_support_handle<MessageT>();
if (rcl_publisher_init(
&publisher_handle_, node_handle_.get(), type_support_handle,
topic.c_str(), &publisher_options) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("could not create publisher: ") +
rcl_get_error_string_safe());
}
// 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) {
throw std::runtime_error(
std::string("failed to get rmw handle: ") + rcl_get_error_string_safe());
}
if (rmw_get_gid_for_publisher(publisher_rmw_handle, &rmw_gid_) != RMW_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to get publisher gid: ") + rmw_get_error_string_safe());
// *INDENT-ON*
}
}
virtual ~Publisher()
{
if (rcl_publisher_fini(&intra_process_publisher_handle_, node_handle_.get()) != RCL_RET_OK) {
fprintf(
stderr,
"Error in destruction of intra process rcl publisher handle: %s\n",
rcl_get_error_string_safe());
}
if (rcl_publisher_fini(&publisher_handle_, node_handle_.get()) != RCL_RET_OK) {
fprintf(
stderr,
"Error in destruction of rcl publisher handle: %s\n",
rcl_get_error_string_safe());
}
}
{}
/// 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());
@@ -242,7 +208,7 @@ public:
}
}
void
virtual void
publish(const std::shared_ptr<MessageT> & msg)
{
// Avoid allocating when not using intra process.
@@ -261,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.
@@ -280,7 +246,7 @@ public:
return this->publish(unique_msg);
}
void
virtual void
publish(const MessageT & msg)
{
// Avoid allocating when not using intra process.
@@ -295,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_;
@@ -319,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

@@ -31,7 +31,7 @@ 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() const = 0;
@@ -46,7 +46,7 @@ template<class Clock = std::chrono::high_resolution_clock>
class GenericRate : public RateBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate);
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate)
explicit GenericRate(double rate)
: GenericRate<Clock>(
@@ -106,7 +106,7 @@ public:
}
private:
RCLCPP_DISABLE_COPY(GenericRate);
RCLCPP_DISABLE_COPY(GenericRate)
std::chrono::nanoseconds period_;
using ClockDurationNano = std::chrono::duration<typename Clock::rep, std::nano>;

View File

@@ -12,6 +12,113 @@
// See the License for the specific language governing permissions and
// limitations under the License.
/** \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_
@@ -24,42 +131,10 @@
#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"
// NOLINTNEXTLINE(runtime/int)
inline const std::chrono::seconds operator"" _s(unsigned long long s)
{
return std::chrono::seconds(s);
}
inline const std::chrono::nanoseconds operator"" _s(long double s)
{
return std::chrono::duration_cast<std::chrono::nanoseconds>(
std::chrono::duration<long double>(s));
}
// NOLINTNEXTLINE(runtime/int)
inline const std::chrono::nanoseconds operator"" _ms(unsigned long long ms)
{
return std::chrono::milliseconds(ms);
}
inline 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));
}
// NOLINTNEXTLINE(runtime/int)
inline const std::chrono::nanoseconds operator"" _ns(unsigned long long ns)
{
return std::chrono::nanoseconds(ns);
}
inline 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 rclcpp
{

View File

@@ -25,8 +25,10 @@
#include "rcl/service.h"
#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"
@@ -39,12 +41,16 @@ 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<rcl_node_t> node_handle,
const std::string service_name);
const std::string & service_name);
RCLCPP_PUBLIC
explicit ServiceBase(
std::shared_ptr<rcl_node_t> node_handle);
RCLCPP_PUBLIC
virtual ~ServiceBase();
@@ -64,12 +70,17 @@ public:
std::shared_ptr<void> request) = 0;
protected:
RCLCPP_DISABLE_COPY(ServiceBase);
RCLCPP_DISABLE_COPY(ServiceBase)
RCLCPP_PUBLIC
rcl_node_t *
get_rcl_node_handle() const;
std::shared_ptr<rcl_node_t> node_handle_;
rcl_service_t service_handle_ = rcl_get_zero_initialized_service();
rcl_service_t * service_handle_ = nullptr;
std::string service_name_;
bool owns_rcl_handle_ = true;
};
using any_service_callback::AnyServiceCallback;
@@ -88,7 +99,7 @@ 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<rcl_node_t> node_handle,
@@ -97,28 +108,70 @@ public:
rcl_service_options_t & service_options)
: ServiceBase(node_handle, service_name), any_callback_(any_callback)
{
using rosidl_generator_cpp::get_service_type_support_handle;
using rosidl_typesupport_cpp::get_service_type_support_handle;
auto service_type_support_handle = get_service_type_support_handle<ServiceT>();
if (rcl_service_init(
&service_handle_, node_handle.get(), service_type_support_handle, service_name.c_str(),
&service_options) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("could not create service: ") +
rcl_get_error_string_safe());
// 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),
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()
{
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();
// 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();
}
}
}
@@ -150,15 +203,12 @@ public:
rcl_ret_t status = rcl_send_response(get_service_handle(), req_id.get(), response.get());
if (status != RCL_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to send response: ") + rcl_get_error_string_safe());
// *INDENT-ON*
rclcpp::exceptions::throw_from_rcl_error(status, "failed to send response");
}
}
private:
RCLCPP_DISABLE_COPY(Service);
RCLCPP_DISABLE_COPY(Service)
AnyServiceCallback<ServiceT> any_callback_;
};

View File

@@ -44,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;
@@ -52,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());
@@ -144,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()) {
@@ -162,7 +160,8 @@ public:
}
}
}
for (auto & service : group->get_service_ptrs()) {
for (auto & weak_service : group->get_service_ptrs()) {
auto service = weak_service.lock();
if (service) {
service_handles_.push_back(service->get_service_handle());
}
@@ -265,7 +264,7 @@ public:
any_exec->subscription = subscription;
}
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);
subscription_handles_.erase(it);
return;
}
@@ -299,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;
}
@@ -332,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;
}

View File

@@ -15,6 +15,8 @@
#ifndef RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_
#define RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_
#include <memory>
#include "rclcpp/macros.hpp"
#include "rclcpp/message_memory_strategy.hpp"
#include "rclcpp/visibility_control.hpp"
@@ -44,7 +46,7 @@ class MessagePoolMemoryStrategy
: public message_memory_strategy::MessageMemoryStrategy<MessageT>
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(MessagePoolMemoryStrategy);
RCLCPP_SMART_PTR_DEFINITIONS(MessagePoolMemoryStrategy)
/// Default constructor
MessagePoolMemoryStrategy()

View File

@@ -29,19 +29,21 @@
#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
{
@@ -51,19 +53,21 @@ 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 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<rcl_node_t> node_handle,
const rosidl_message_type_support_t & type_support_handle,
const std::string & topic_name,
bool ignore_local_publications);
const rcl_subscription_options_t & subscription_options);
/// Default destructor.
RCLCPP_PUBLIC
@@ -71,7 +75,7 @@ public:
/// Get the topic that this subscription is subscribed on.
RCLCPP_PUBLIC
const std::string &
const char *
get_topic_name() const;
RCLCPP_PUBLIC
@@ -83,7 +87,7 @@ public:
get_intra_process_subscription_handle() const;
/// Borrow a new message.
// \return Shared pointer to the fresh message.
/** \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.
@@ -95,9 +99,10 @@ public:
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.
/** \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,
@@ -109,9 +114,7 @@ protected:
std::shared_ptr<rcl_node_t> node_handle_;
private:
RCLCPP_DISABLE_COPY(SubscriptionBase);
std::string topic_name_;
bool ignore_local_publications_;
RCLCPP_DISABLE_COPY(SubscriptionBase)
};
using any_subscription_callback::AnySubscriptionCallback;
@@ -120,7 +123,7 @@ using any_subscription_callback::AnySubscriptionCallback;
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>;
@@ -128,7 +131,7 @@ 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.
/**
@@ -136,8 +139,8 @@ public:
* should be instantiated through Node::create_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(
@@ -149,23 +152,15 @@ public:
memory_strategy = message_memory_strategy::MessageMemoryStrategy<MessageT,
Alloc>::create_default())
: SubscriptionBase(
node_handle, topic_name, subscription_options.ignore_local_publications),
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)
{
using rosidl_generator_cpp::get_message_type_support_handle;
auto type_support_handle = get_message_type_support_handle<MessageT>();
if (rcl_subscription_init(
&subscription_handle_, node_handle_.get(), type_support_handle, topic_name.c_str(),
&subscription_options) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("could not create subscription: ") + rcl_get_error_string_safe());
}
}
{}
/// Support dynamically setting the message memory strategy.
/**
@@ -200,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);
@@ -232,29 +229,36 @@ 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,
GetMessageCallbackType get_message_callback,
MatchesAnyPublishersCallbackType matches_any_publisher_callback,
const rcl_subscription_options_t & intra_process_options)
{
if (rcl_subscription_init(
&intra_process_subscription_handle_, node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
(get_topic_name() + "__intra").c_str(),
&intra_process_options) != RCL_RET_OK)
{
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("could not create intra process subscription: ") + rcl_get_error_string_safe());
// *INDENT-ON*
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;
@@ -262,6 +266,7 @@ private:
matches_any_intra_process_publishers_ = matches_any_publisher_callback;
}
/// Implemenation detail.
const rcl_subscription_t *
get_intra_process_subscription_handle() const
{
@@ -271,7 +276,8 @@ private:
return &intra_process_subscription_handle_;
}
RCLCPP_DISABLE_COPY(Subscription);
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

@@ -21,6 +21,7 @@
#include <sstream>
#include <thread>
#include <type_traits>
#include <utility>
#include "rclcpp/function_traits.hpp"
#include "rclcpp/macros.hpp"
@@ -42,7 +43,7 @@ namespace timer
class TimerBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimerBase);
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimerBase)
RCLCPP_PUBLIC
explicit TimerBase(std::chrono::nanoseconds period);
@@ -54,6 +55,10 @@ public:
void
cancel();
RCLCPP_PUBLIC
void
reset();
RCLCPP_PUBLIC
virtual void
execute_callback() = 0;
@@ -63,13 +68,13 @@ public:
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.
/** \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 is ready to trigger the callback.
@@ -102,7 +107,7 @@ template<
class GenericTimer : public TimerBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(GenericTimer);
RCLCPP_SMART_PTR_DEFINITIONS(GenericTimer)
/// Default constructor.
/**
@@ -169,7 +174,7 @@ public:
}
protected:
RCLCPP_DISABLE_COPY(GenericTimer);
RCLCPP_DISABLE_COPY(GenericTimer)
FunctorT callback_;
};

View File

@@ -18,8 +18,8 @@
#include "rosidl_generator_cpp/message_type_support_decl.hpp"
#include "rosidl_generator_cpp/service_type_support_decl.hpp"
#include "rosidl_generator_cpp/message_type_support.hpp"
#include "rosidl_generator_cpp/service_type_support.hpp"
#include "rosidl_typesupport_cpp/message_type_support.hpp"
#include "rosidl_typesupport_cpp/service_type_support.hpp"
#include "rclcpp/visibility_control.hpp"

View File

@@ -57,7 +57,7 @@ void
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();
@@ -68,7 +68,7 @@ void
shutdown();
/// Register a function to be called when shutdown is called.
/* Calling the callbacks is the last thing shutdown() does. */
/** Calling the callbacks is the last thing shutdown() does. */
RCLCPP_PUBLIC
void
on_shutdown(std::function<void(void)> callback);
@@ -81,7 +81,7 @@ on_shutdown(std::function<void(void)> callback);
* 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] waitset Pointer to the rcl_wait_set_t that will be using the
* \param waitset Pointer to the rcl_wait_set_t that will be using the
* resulting guard condition.
* \return Pointer to the guard condition.
*/
@@ -95,7 +95,7 @@ get_sigint_guard_condition(rcl_wait_set_t * 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] waitset Pointer to the rcl_wait_set_t that was using the
* \param waitset Pointer to the rcl_wait_set_t that was using the
* resulting guard condition.
*/
RCLCPP_PUBLIC

View File

@@ -22,8 +22,6 @@
#ifndef RCLCPP__VISIBILITY_CONTROL_HPP_
#define RCLCPP__VISIBILITY_CONTROL_HPP_
#include "rmw/rmw.h"
// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
// https://gcc.gnu.org/wiki/Visibility

View File

@@ -2,7 +2,7 @@
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
<package format="2">
<name>rclcpp</name>
<version>0.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>
@@ -11,11 +11,16 @@
<build_export_depend>rmw</build_export_depend>
<build_depend>builtin_interfaces</build_depend>
<build_depend>rcl_interfaces</build_depend>
<build_depend>rmw_implementation_cmake</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>
@@ -26,6 +31,7 @@
<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,16 +14,16 @@
# copied from rclcpp/rclcpp-extras.cmake
include("${rclcpp_DIR}/get_rclcpp_information.cmake")
# 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)
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")
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

@@ -23,7 +23,7 @@ AnyExecutable::AnyExecutable()
service(nullptr),
client(nullptr),
callback_group(nullptr),
node(nullptr)
node_base(nullptr)
{}
AnyExecutable::~AnyExecutable()

View File

@@ -37,7 +37,7 @@ CallbackGroup::get_timer_ptrs() const
return timer_ptrs_;
}
const std::vector<rclcpp::service::ServiceBase::SharedPtr> &
const std::vector<rclcpp::service::ServiceBase::WeakPtr> &
CallbackGroup::get_service_ptrs() const
{
std::lock_guard<std::mutex> lock(mutex_);

View File

@@ -16,13 +16,15 @@
#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.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;
@@ -30,9 +32,11 @@ using rclcpp::exceptions::InvalidNodeError;
using rclcpp::exceptions::throw_from_rcl_error;
ClientBase::ClientBase(
std::shared_ptr<rclcpp::node::Node> parent_node,
rclcpp::node_interfaces::NodeBaseInterface * node_base,
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
const std::string & service_name)
: node_(parent_node), node_handle_(parent_node->get_shared_node_handle()),
: node_graph_(node_graph),
node_handle_(node_base->get_shared_rcl_node_handle()),
service_name_(service_name)
{}
@@ -66,6 +70,12 @@ 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;
@@ -74,13 +84,8 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
// check was non-blocking, return immediately
return false;
}
// make an event to reuse, rather than create a new one each time
auto node_ptr = node_.lock();
if (!node_ptr) {
throw InvalidNodeError();
}
auto event = node_ptr->get_graph_event();
// update the time even on the first loop to account for time in first server_is_read()
// 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.
@@ -94,10 +99,13 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
return false;
}
node_ptr->wait_for_graph_change(event, time_to_wait);
if (event->check_and_clear()) {
if (this->service_is_ready()) {
return true;
}
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);

View File

@@ -18,18 +18,40 @@
#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, bool reset_error)
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");
}
const rcl_error_state_t * error_state = rcl_get_error_state();
if (!error_state) {
error_state = rcl_get_error_state();
}
if (!error_state) {
throw std::runtime_error("rcl error state is not set");
}
@@ -39,7 +61,7 @@ throw_from_rcl_error(rcl_ret_t ret, const std::string & prefix, bool reset_error
}
RCLErrorBase base_exc(ret, error_state);
if (reset_error) {
rcl_reset_error();
reset_error();
}
switch (ret) {
case RCL_RET_BAD_ALLOC:

View File

@@ -13,6 +13,7 @@
// limitations under the License.
#include <algorithm>
#include <memory>
#include <string>
#include <type_traits>
@@ -20,6 +21,7 @@
#include "rcl/error_handling.h"
#include "rclcpp/executor.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/scope_exit.hpp"
#include "rclcpp/utilities.hpp"
@@ -85,10 +87,11 @@ Executor::~Executor()
}
void
Executor::add_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify)
Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
{
// If the node already has an executor
if (node_ptr->has_executor.exchange(true)) {
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
@@ -111,14 +114,20 @@ Executor::add_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify)
}
void
Executor::remove_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify)
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)
[&](std::weak_ptr<rclcpp::node::Node> & i)
[&](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr & i)
{
bool matched = (i.lock() == node_ptr);
node_removed |= matched;
@@ -127,7 +136,8 @@ Executor::remove_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify)
// *INDENT-ON*
)
);
node_ptr->has_executor.store(false);
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) {
@@ -139,9 +149,15 @@ Executor::remove_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify)
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::Node::SharedPtr node,
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
std::chrono::nanoseconds timeout)
{
this->add_node(node, false);
@@ -151,13 +167,19 @@ Executor::spin_node_once_nanoseconds(
}
void
Executor::spin_node_some(rclcpp::node::Node::SharedPtr node)
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()
{
@@ -247,7 +269,7 @@ Executor::execute_subscription(
} 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().c_str(), rcl_get_error_string_safe());
subscription->get_topic_name(), rcl_get_error_string_safe());
}
subscription->return_message(message);
}
@@ -269,7 +291,7 @@ Executor::execute_intra_process_subscription(
} 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().c_str(), rcl_get_error_string_safe());
subscription->get_topic_name(), rcl_get_error_string_safe());
}
}
@@ -290,11 +312,9 @@ Executor::execute_service(
service->get_service_handle(),
request_header.get(),
request.get());
if (status != RCL_RET_SERVICE_TAKE_FAILED) {
if (status == RCL_RET_OK) {
service->handle_request(request_header, request);
}
} else {
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());
@@ -311,11 +331,9 @@ Executor::execute_client(
client->get_client_handle(),
request_header.get(),
response.get());
if (status != RCL_RET_SERVICE_TAKE_FAILED) {
if (status == RCL_RET_OK) {
client->handle_response(request_header, response);
}
} else {
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());
@@ -335,7 +353,7 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
remove_if(
weak_nodes_.begin(), weak_nodes_.end(),
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
[](std::weak_ptr<rclcpp::node::Node> i)
[](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr i)
{
return i.expired();
}
@@ -415,11 +433,11 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
}
}
rclcpp::node::Node::SharedPtr
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
Executor::get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group)
{
if (!group) {
return rclcpp::node::Node::SharedPtr();
return nullptr;
}
for (auto & weak_node : weak_nodes_) {
auto node = weak_node.lock();
@@ -433,7 +451,7 @@ Executor::get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr gro
}
}
}
return rclcpp::node::Node::SharedPtr();
return nullptr;
}
rclcpp::callback_group::CallbackGroup::SharedPtr

View File

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

@@ -26,7 +26,7 @@ void
SingleThreadedExecutor::spin()
{
if (spinning.exchange(true)) {
throw std::runtime_error("spin_some() called while already spinning");
throw std::runtime_error("spin() called while already spinning");
}
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
while (rclcpp::utilities::ok() && spinning.load()) {

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

@@ -16,6 +16,7 @@
#include <cstdio>
#include <exception>
#include <memory>
#include <string>
#include <vector>
@@ -114,17 +115,17 @@ GraphListener::run_loop()
rcl_ret_t ret;
{
// This "barrier" lock ensures that other functions can acquire the
// nodes_mutex_ after waking up rcl_wait.
std::lock_guard<std::mutex> nodes_barrier_lock(nodes_barrier_mutex_);
// 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.
nodes_mutex_.lock();
node_graph_interfaces_mutex_.lock();
}
// This lock is released when the loop continues or exits.
std::lock_guard<std::mutex> nodes_lock(nodes_mutex_, std::adopt_lock);
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 < (nodes_.size() + 2)) {
ret = rcl_wait_set_resize_guard_conditions(&wait_set_, nodes_.size() + 2);
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");
}
@@ -145,13 +146,13 @@ GraphListener::run_loop()
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 : nodes_) {
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 = rcl_node_get_graph_guard_condition(node_ptr->get_rcl_node_handle());
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");
}
@@ -178,8 +179,8 @@ GraphListener::run_loop()
}
}
// Notify nodes who's guard conditions are set (triggered).
for (const auto node_ptr : nodes_) {
auto graph_gc = rcl_node_get_graph_guard_condition(node_ptr->get_rcl_node_handle());
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");
}
@@ -207,25 +208,27 @@ interrupt_(rcl_guard_condition_t * interrupt_guard_condition)
static void
acquire_nodes_lock_(
std::mutex * nodes_barrier_mutex,
std::mutex * nodes_mutex,
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(*nodes_barrier_mutex);
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);
nodes_mutex->lock();
node_graph_interfaces_mutex->lock();
}
}
static bool
has_node_(std::vector<rclcpp::node::Node *> * nodes, rclcpp::node::Node * node)
has_node_(
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> * node_graph_interfaces,
rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
for (const auto node_ptr : (*nodes)) {
if (node == node_ptr) {
for (const auto node_ptr : (*node_graph_interfaces)) {
if (node_graph == node_ptr) {
return true;
}
}
@@ -233,23 +236,26 @@ has_node_(std::vector<rclcpp::node::Node *> * nodes, rclcpp::node::Node * node)
}
bool
GraphListener::has_node(rclcpp::node::Node * node)
GraphListener::has_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
if (!node) {
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_(&nodes_barrier_mutex_, &nodes_mutex_, &interrupt_guard_condition_);
// Store the now acquired nodes_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(nodes_mutex_, std::adopt_lock);
return has_node_(&nodes_, node);
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::Node * node)
GraphListener::add_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
if (!node) {
if (!node_graph) {
throw std::invalid_argument("node is nullptr");
}
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
@@ -259,25 +265,30 @@ GraphListener::add_node(rclcpp::node::Node * 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_(&nodes_barrier_mutex_, &nodes_mutex_, &interrupt_guard_condition_);
// Store the now acquired nodes_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(nodes_mutex_, std::adopt_lock);
if (has_node_(&nodes_, node)) {
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();
}
nodes_.push_back(node);
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 nodes_mutex_.
// will evaluate the new node when nodes_lock releases the node_graph_interfaces_mutex_.
}
static void
remove_node_(std::vector<rclcpp::node::Node *> * nodes, rclcpp::node::Node * node)
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 = nodes->begin(); it != nodes->end(); ++it) {
if (node == *it) {
for (auto it = node_graph_interfaces->begin(); it != node_graph_interfaces->end(); ++it) {
if (node_graph == *it) {
// Found the node, remove it.
nodes->erase(it);
node_graph_interfaces->erase(it);
// Now trigger the interrupt guard condition to make sure
return;
}
@@ -287,23 +298,26 @@ remove_node_(std::vector<rclcpp::node::Node *> * nodes, rclcpp::node::Node * nod
}
void
GraphListener::remove_node(rclcpp::node::Node * node)
GraphListener::remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
{
if (!node) {
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_(&nodes_, node);
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_(&nodes_barrier_mutex_, &nodes_mutex_, &interrupt_guard_condition_);
// Store the now acquired nodes_mutex_ in the scoped lock using adopt_lock.
std::lock_guard<std::mutex> nodes_lock(nodes_mutex_, std::adopt_lock);
remove_node_(&nodes_, node);
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

View File

@@ -14,6 +14,8 @@
#include "rclcpp/memory_strategies.hpp"
#include <memory>
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
using rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy;

View File

@@ -60,8 +60,9 @@ MemoryStrategy::get_service_by_handle(const rcl_service_t * service_handle,
if (!group) {
continue;
}
for (auto & service : group->get_service_ptrs()) {
if (service->get_service_handle() == service_handle) {
for (auto & weak_service : group->get_service_ptrs()) {
auto service = weak_service.lock();
if (service && service->get_service_handle() == service_handle) {
return service;
}
}
@@ -95,7 +96,7 @@ MemoryStrategy::get_client_by_handle(const rcl_client_t * client_handle,
return nullptr;
}
rclcpp::node::Node::SharedPtr
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
MemoryStrategy::get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group,
const WeakNodeVector & weak_nodes)
{
@@ -158,8 +159,9 @@ MemoryStrategy::get_group_by_service(
if (!group) {
continue;
}
for (auto & serv : group->get_service_ptrs()) {
if (serv == service) {
for (auto & weak_serv : group->get_service_ptrs()) {
auto serv = weak_serv.lock();
if (serv && serv == service) {
return group;
}
}

View File

@@ -15,466 +15,169 @@
#include <algorithm>
#include <limits>
#include <map>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "rcl_interfaces/srv/list_parameters.hpp"
#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, bool use_intra_process_comms)
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)
: 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), notify_guard_condition_is_valid_(false),
graph_listener_(context->get_sub_context<rclcpp::graph_listener::GraphListener>()),
should_add_to_graph_listener_(true), graph_users_count_(0)
: 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)
{
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");
}
has_executor.store(false);
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.
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());
}
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
}
rcl_node_t * rcl_node = new rcl_node_t(rcl_get_zero_initialized_node());
node_handle_.reset(rcl_node, [](rcl_node_t * node) {
if (rcl_node_fini(node) != RCL_RET_OK) {
fprintf(
stderr, "Error in destruction of rmw node handle: %s\n", rcl_get_error_string_safe());
}
delete node;
});
rcl_node_options_t options = rcl_node_get_default_options();
// TODO(jacquelinekay): Allocator options
options.domain_id = domain_id;
ret = rcl_node_init(node_handle_.get(), name_.c_str(), &options);
if (ret != RCL_RET_OK) {
// 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());
}
throw_from_rcl_error(ret, "failed to initialize rcl node");
}
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);
notify_guard_condition_is_valid_ = true;
}
Node::~Node()
{
// 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);
}
// Finalize the interrupt guard condition after removing self from graph listener.
{
std::lock_guard<std::mutex> notify_guard_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 std::string &
const char *
Node::get_name() const
{
return name_;
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)
{
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;
return node_base_->create_callback_group(group_type);
}
bool
Node::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;
return node_base_->callback_group_in_node(group);
}
// 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);
// }
std::vector<rcl_interfaces::msg::SetParametersResult>
Node::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;
return node_parameters_->set_parameters(parameters);
}
rcl_interfaces::msg::SetParametersResult
Node::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>();
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;
}
// 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);
return result;
return node_parameters_->set_parameters_atomically(parameters);
}
std::vector<rclcpp::parameter::ParameterVariant>
Node::get_parameters(
const std::vector<std::string> & names) const
{
std::lock_guard<std::mutex> lock(mutex_);
std::vector<rclcpp::parameter::ParameterVariant> results;
return node_parameters_->get_parameters(names);
}
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
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
{
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;
return node_parameters_->describe_parameters(names);
}
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;
return node_parameters_->get_parameter_types(names);
}
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 (((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;
return node_parameters_->list_parameters(prefixes, depth);
}
std::map<std::string, std::string>
std::map<std::string, std::vector<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;
return node_graph_->get_topic_names_and_types();
}
auto ret = rmw_get_topic_names_and_types(rcl_node_get_rmw_handle(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;
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
{
size_t count;
auto ret = rmw_count_publishers(rcl_node_get_rmw_handle(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;
return node_graph_->count_publishers(topic_name);
}
size_t
Node::count_subscribers(const std::string & topic_name) const
{
size_t count;
auto ret = rmw_count_subscribers(rcl_node_get_rmw_handle(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;
return node_graph_->count_subscribers(topic_name);
}
const Node::CallbackGroupWeakPtrList &
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
Node::get_callback_groups() const
{
return callback_groups_;
}
const rcl_guard_condition_t *
Node::get_notify_guard_condition() const
{
std::lock_guard<std::mutex> notify_guard_condition_lock(notify_guard_condition_mutex_);
if (!notify_guard_condition_is_valid_) {
return nullptr;
}
return &notify_guard_condition_;
}
const rcl_guard_condition_t *
Node::get_graph_guard_condition() const
{
return rcl_node_get_graph_guard_condition(node_handle_.get());
}
const rcl_node_t *
Node::get_rcl_node_handle() const
{
return node_handle_.get();
}
rcl_node_t *
Node::get_rcl_node_handle()
{
return node_handle_.get();
}
std::shared_ptr<rcl_node_t>
Node::get_shared_node_handle()
{
return node_handle_;
}
void
Node::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();
{
std::lock_guard<std::mutex> notify_guard_condition_lock(notify_guard_condition_mutex_);
rcl_ret_t ret = rcl_trigger_guard_condition(&notify_guard_condition_);
if (RCL_RET_OK != ret) {
throw_from_rcl_error(ret, "failed to trigger notify guard condition");
}
}
}
void
Node::notify_shutdown()
{
// notify here anything that will not be woken up by ctrl-c or rclcpp::shutdown().
graph_cv_.notify_all();
return node_base_->get_callback_groups();
}
rclcpp::event::Event::SharedPtr
Node::get_graph_event()
{
auto event = rclcpp::event::Event::make_shared();
std::lock_guard<std::mutex> graph_changed_lock(graph_mutex_);
// 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();
}
graph_events_.push_back(event);
graph_users_count_++;
return event;
return node_graph_->get_graph_event();
}
void
@@ -482,30 +185,41 @@ Node::wait_for_graph_change(
rclcpp::event::Event::SharedPtr event,
std::chrono::nanoseconds timeout)
{
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();
}
}
std::unique_lock<std::mutex> graph_lock(graph_mutex_);
graph_cv_.wait_for(graph_lock, timeout, [&event]() {
return event->check() || !rclcpp::utilities::ok();
});
node_graph_->wait_for_graph_change(event, timeout);
}
size_t
Node::count_graph_users()
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
Node::get_node_base_interface()
{
return graph_users_count_.load();
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

@@ -15,6 +15,7 @@
#include "rclcpp/parameter_client.hpp"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
@@ -250,7 +251,8 @@ std::vector<rclcpp::parameter::ParameterVariant>
SyncParametersClient::get_parameters(const std::vector<std::string> & parameter_names)
{
auto f = async_parameters_client_->get_parameters(parameter_names);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
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();
@@ -273,7 +275,8 @@ SyncParametersClient::get_parameter_types(const std::vector<std::string> & param
{
auto f = async_parameters_client_->get_parameter_types(parameter_names);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
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();
@@ -287,7 +290,8 @@ SyncParametersClient::set_parameters(
{
auto f = async_parameters_client_->set_parameters(parameters);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
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();
@@ -301,7 +305,8 @@ SyncParametersClient::set_parameters_atomically(
{
auto f = async_parameters_client_->set_parameters_atomically(parameters);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
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();
@@ -317,7 +322,8 @@ SyncParametersClient::list_parameters(
{
auto f = async_parameters_client_->list_parameters(parameter_prefixes, depth);
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
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();

View File

@@ -15,6 +15,8 @@
#include "rclcpp/parameter_service.hpp"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
using rclcpp::parameter_service::ParameterService;
@@ -27,7 +29,7 @@ ParameterService::ParameterService(
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>(
node_->get_name() + "__get_parameters",
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,
@@ -46,7 +48,7 @@ ParameterService::ParameterService(
);
get_parameter_types_service_ = node_->create_service<rcl_interfaces::srv::GetParameterTypes>(
node_->get_name() + "__get_parameter_types",
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,
@@ -66,7 +68,7 @@ ParameterService::ParameterService(
);
set_parameters_service_ = node_->create_service<rcl_interfaces::srv::SetParameters>(
node_->get_name() + "__set_parameters",
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,
@@ -88,7 +90,7 @@ ParameterService::ParameterService(
set_parameters_atomically_service_ =
node_->create_service<rcl_interfaces::srv::SetParametersAtomically>(
node_->get_name() + "__set_parameters_atomically",
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,
@@ -112,7 +114,7 @@ ParameterService::ParameterService(
);
describe_parameters_service_ = node_->create_service<rcl_interfaces::srv::DescribeParameters>(
node_->get_name() + "__describe_parameters",
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,
@@ -129,7 +131,7 @@ ParameterService::ParameterService(
);
list_parameters_service_ = node_->create_service<rcl_interfaces::srv::ListParameters>(
node_->get_name() + "__list_parameters",
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,

View File

@@ -28,34 +28,89 @@
#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(
std::shared_ptr<rcl_node_t> node_handle,
std::string topic,
size_t queue_size)
: node_handle_(node_handle),
topic_(topic), queue_size_(queue_size),
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 std::string &
const char *
PublisherBase::get_topic_name() const
{
return topic_;
return rcl_publisher_get_topic_name(&publisher_handle_);
}
size_t
PublisherBase::get_queue_size() const
{
return queue_size_;
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 &
@@ -82,14 +137,16 @@ 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) {
throw std::runtime_error(
std::string("failed to compare gids: ") + rmw_get_error_string_safe());
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) {
throw std::runtime_error(
std::string("failed to compare gids: ") + rmw_get_error_string_safe());
auto msg = std::string("failed to compare gids: ") + rmw_get_error_string_safe();
rmw_reset_error();
throw std::runtime_error(msg);
}
}
return result;
@@ -101,14 +158,25 @@ PublisherBase::setup_intra_process(
StoreMessageCallbackT callback,
const rcl_publisher_options_t & intra_process_options)
{
if (rcl_publisher_init(
&intra_process_publisher_handle_, node_handle_.get(),
rclcpp::type_support::get_intra_process_message_msg_type_support(),
(topic_ + "__intra").c_str(), &intra_process_options) != RCL_RET_OK)
{
throw std::runtime_error(
std::string("could not create intra process publisher: ") +
rcl_get_error_string_safe());
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;
@@ -117,16 +185,16 @@ PublisherBase::setup_intra_process(
rmw_publisher_t * publisher_rmw_handle = rcl_publisher_get_rmw_handle(
&intra_process_publisher_handle_);
if (publisher_rmw_handle == nullptr) {
throw std::runtime_error(std::string(
"Failed to get rmw publisher handle") + rcl_get_error_string_safe());
auto msg = std::string("Failed to get rmw publisher handle") + rcl_get_error_string_safe();
rcl_reset_error();
throw std::runtime_error(msg);
}
auto ret = rmw_get_gid_for_publisher(
auto rmw_ret = rmw_get_gid_for_publisher(
publisher_rmw_handle, &intra_process_rmw_gid_);
if (ret != RMW_RET_OK) {
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
throw std::runtime_error(
std::string("failed to create intra process publisher gid: ") +
rmw_get_error_string_safe());
// *INDENT-ON*
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

@@ -29,10 +29,14 @@ using rclcpp::service::ServiceBase;
ServiceBase::ServiceBase(
std::shared_ptr<rcl_node_t> node_handle,
const std::string service_name)
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()
{}
@@ -45,5 +49,11 @@ ServiceBase::get_service_name()
const rcl_service_t *
ServiceBase::get_service_handle()
{
return &service_handle_;
return service_handle_;
}
rcl_node_t *
ServiceBase::get_rcl_node_handle() const
{
return node_handle_.get();
}

View File

@@ -15,8 +15,12 @@
#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"
@@ -25,14 +29,30 @@ 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,
bool ignore_local_publications)
: node_handle_(node_handle),
topic_name_(topic_name),
ignore_local_publications_(ignore_local_publications)
const rcl_subscription_options_t & subscription_options)
: node_handle_(node_handle)
{
// To avoid unused private member warnings.
(void)ignore_local_publications_;
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()
@@ -53,10 +73,10 @@ SubscriptionBase::~SubscriptionBase()
}
}
const std::string &
const char *
SubscriptionBase::get_topic_name() const
{
return this->topic_name_;
return rcl_subscription_get_topic_name(&subscription_handle_);
}
const rcl_subscription_t *

View File

@@ -40,6 +40,14 @@ TimerBase::cancel()
}
}
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()
{

View File

@@ -30,7 +30,7 @@
const rosidl_message_type_support_t *
rclcpp::type_support::get_intra_process_message_msg_type_support()
{
return rosidl_generator_cpp::get_message_type_support_handle<
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::IntraProcessMessage
>();
}
@@ -38,7 +38,7 @@ rclcpp::type_support::get_intra_process_message_msg_type_support()
const rosidl_message_type_support_t *
rclcpp::type_support::get_parameter_event_msg_type_support()
{
return rosidl_generator_cpp::get_message_type_support_handle<
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::ParameterEvent
>();
}
@@ -46,7 +46,7 @@ rclcpp::type_support::get_parameter_event_msg_type_support()
const rosidl_message_type_support_t *
rclcpp::type_support::get_set_parameters_result_msg_type_support()
{
return rosidl_generator_cpp::get_message_type_support_handle<
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::SetParametersResult
>();
}
@@ -54,7 +54,7 @@ rclcpp::type_support::get_set_parameters_result_msg_type_support()
const rosidl_message_type_support_t *
rclcpp::type_support::get_parameter_descriptor_msg_type_support()
{
return rosidl_generator_cpp::get_message_type_support_handle<
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::ParameterDescriptor
>();
}
@@ -62,7 +62,7 @@ rclcpp::type_support::get_parameter_descriptor_msg_type_support()
const rosidl_message_type_support_t *
rclcpp::type_support::get_list_parameters_result_msg_type_support()
{
return rosidl_generator_cpp::get_message_type_support_handle<
return rosidl_typesupport_cpp::get_message_type_support_handle<
rcl_interfaces::msg::ListParametersResult
>();
}
@@ -70,7 +70,7 @@ rclcpp::type_support::get_list_parameters_result_msg_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_get_parameters_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::GetParameters
>();
}
@@ -78,7 +78,7 @@ rclcpp::type_support::get_get_parameters_srv_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_get_parameter_types_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::GetParameterTypes
>();
}
@@ -86,7 +86,7 @@ rclcpp::type_support::get_get_parameter_types_srv_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_set_parameters_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::SetParameters
>();
}
@@ -94,7 +94,7 @@ rclcpp::type_support::get_set_parameters_srv_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_list_parameters_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::ListParameters
>();
}
@@ -102,7 +102,7 @@ rclcpp::type_support::get_list_parameters_srv_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_describe_parameters_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::DescribeParameters
>();
}
@@ -110,7 +110,7 @@ rclcpp::type_support::get_describe_parameters_srv_type_support()
const rosidl_service_type_support_t *
rclcpp::type_support::get_set_parameters_atomically_srv_type_support()
{
return rosidl_generator_cpp::get_service_type_support_handle<
return rosidl_typesupport_cpp::get_service_type_support_handle<
rcl_interfaces::srv::SetParametersAtomically
>();
}

View File

@@ -86,7 +86,7 @@ signal_handler(int signal_value)
g_signal_status = signal_value;
{
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
for (auto const & kv : g_sigint_guard_cond_handles) {
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,
@@ -164,7 +164,7 @@ rclcpp::utilities::shutdown()
g_signal_status = SIGINT;
{
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
for (auto const & kv : g_sigint_guard_cond_handles) {
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",

View File

@@ -0,0 +1,3 @@
bool request
---
bool response

View File

@@ -0,0 +1,60 @@
// 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 <gtest/gtest.h>
#include <string>
#include <memory>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcl_interfaces/srv/list_parameters.hpp"
class TestClient : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::node::Node::SharedPtr node;
};
/*
Testing client construction and destruction.
*/
TEST_F(TestClient, construction_and_destruction) {
using rcl_interfaces::srv::ListParameters;
{
auto client = node->create_client<ListParameters>("service");
}
{
ASSERT_THROW({
auto client = node->create_client<ListParameters>("invalid_service?");
}, rclcpp::exceptions::InvalidServiceNameError);
}
}

View File

@@ -0,0 +1,74 @@
// 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 <gtest/gtest.h>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/expand_topic_or_service_name.hpp"
/*
Testing expand_topic_or_service_name.
*/
TEST(TestExpandTopicOrServiceName, normal) {
using rclcpp::expand_topic_or_service_name;
{
ASSERT_EQ("/ns/chatter", expand_topic_or_service_name("chatter", "node", "/ns"));
}
}
/*
Testing exceptions of expand_topic_or_service_name.
*/
TEST(TestExpandTopicOrServiceName, exceptions) {
using rclcpp::expand_topic_or_service_name;
{
ASSERT_THROW({
expand_topic_or_service_name("chatter", "invalid_node?", "/ns");
}, rclcpp::exceptions::InvalidNodeNameError);
}
{
ASSERT_THROW({
expand_topic_or_service_name("chatter", "node", "/invalid_ns?");
}, rclcpp::exceptions::InvalidNamespaceError);
}
{
ASSERT_THROW({
expand_topic_or_service_name("chatter/42invalid", "node", "/ns");
}, rclcpp::exceptions::InvalidTopicNameError);
}
{
ASSERT_THROW({
// this one will only fail on the "full" topic name validation check
expand_topic_or_service_name("chatter/{ns}/invalid", "node", "/ns");
}, rclcpp::exceptions::InvalidTopicNameError);
}
{
ASSERT_THROW({
// is_service = true
expand_topic_or_service_name("chatter/42invalid", "node", "/ns", true);
}, rclcpp::exceptions::InvalidServiceNameError);
}
{
ASSERT_THROW({
// is_service = true
// this one will only fail on the "full" topic name validation check
expand_topic_or_service_name("chatter/{ns}/invalid", "node", "/ns", true);
}, rclcpp::exceptions::InvalidServiceNameError);
}
}

View File

@@ -0,0 +1,142 @@
// 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 <gtest/gtest.h>
#include <string>
#include <memory>
#include "rclcpp/node.hpp"
#include "rclcpp/any_service_callback.hpp"
#include "rclcpp/service.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcl/service.h"
#include "rclcpp/srv/mock.hpp"
#include "rclcpp/srv/mock.h"
class TestExternallyDefinedServices : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
};
void
callback(const std::shared_ptr<rclcpp::srv::Mock::Request>/*req*/,
std::shared_ptr<rclcpp::srv::Mock::Response>/*resp*/)
{}
TEST_F(TestExternallyDefinedServices, default_behavior) {
auto node_handle = rclcpp::node::Node::make_shared("base_node");
try {
auto srv = node_handle->create_service<rclcpp::srv::Mock>("test",
callback);
} catch (const std::exception &) {
FAIL();
return;
}
SUCCEED();
}
TEST_F(TestExternallyDefinedServices, extern_defined_uninitialized) {
auto node_handle = rclcpp::node::Node::make_shared("base_node");
// mock for externally defined service
rcl_service_t service_handle = rcl_get_zero_initialized_service();
rclcpp::any_service_callback::AnyServiceCallback<rclcpp::srv::Mock> cb;
// don't initialize the service
// expect fail
try {
rclcpp::service::Service<rclcpp::srv::Mock>(
node_handle->get_node_base_interface()->get_shared_rcl_node_handle(),
&service_handle, cb);
} catch (const std::runtime_error &) {
SUCCEED();
return;
}
FAIL();
}
TEST_F(TestExternallyDefinedServices, extern_defined_initialized) {
auto node_handle = rclcpp::node::Node::make_shared("base_node");
// mock for externally defined service
rcl_service_t service_handle = rcl_get_zero_initialized_service();
rcl_service_options_t service_options = rcl_service_get_default_options();
const rosidl_service_type_support_t * ts =
rosidl_typesupport_cpp::get_service_type_support_handle<rclcpp::srv::Mock>();
rcl_ret_t ret = rcl_service_init(
&service_handle,
node_handle->get_node_base_interface()->get_rcl_node_handle(),
ts, "base_node_service", &service_options);
if (ret != RCL_RET_OK) {
FAIL();
return;
}
rclcpp::any_service_callback::AnyServiceCallback<rclcpp::srv::Mock> cb;
try {
rclcpp::service::Service<rclcpp::srv::Mock>(
node_handle->get_node_base_interface()->get_shared_rcl_node_handle(),
&service_handle, cb);
} catch (const std::runtime_error &) {
FAIL();
return;
}
SUCCEED();
}
TEST_F(TestExternallyDefinedServices, extern_defined_destructor) {
auto node_handle = rclcpp::node::Node::make_shared("base_node");
// mock for externally defined service
rcl_service_t service_handle = rcl_get_zero_initialized_service();
rcl_service_options_t service_options = rcl_service_get_default_options();
const rosidl_service_type_support_t * ts =
rosidl_typesupport_cpp::get_service_type_support_handle<rclcpp::srv::Mock>();
rcl_ret_t ret = rcl_service_init(
&service_handle,
node_handle->get_node_base_interface()->get_rcl_node_handle(),
ts, "base_node_service", &service_options);
if (ret != RCL_RET_OK) {
FAIL();
return;
}
rclcpp::any_service_callback::AnyServiceCallback<rclcpp::srv::Mock> cb;
{
// Call constructor
rclcpp::service::Service<rclcpp::srv::Mock> srv_cpp(
node_handle->get_node_base_interface()->get_shared_rcl_node_handle(),
&service_handle, cb);
// Call destructor
}
if (!service_handle.impl) {
FAIL();
return;
}
SUCCEED();
}

View File

@@ -0,0 +1,76 @@
// 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 <gtest/gtest.h>
#include <memory>
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
#include "rclcpp/memory_strategy.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
class TestFindWeakNodes : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
};
TEST_F(TestFindWeakNodes, allocator_strategy_with_weak_nodes) {
// GIVEN
// A vector of weak pointers to nodes
auto memory_strategy = std::make_shared<
rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy<>>();
auto existing_node = rclcpp::node::Node::make_shared("existing_node");
auto dead_node = rclcpp::node::Node::make_shared("dead_node");
rclcpp::memory_strategy::MemoryStrategy::WeakNodeVector weak_nodes;
weak_nodes.push_back(existing_node->get_node_base_interface());
weak_nodes.push_back(dead_node->get_node_base_interface());
// AND
// Delete dead_node, creating a dangling pointer in weak_nodes
dead_node.reset();
ASSERT_FALSE(weak_nodes[0].expired());
ASSERT_TRUE(weak_nodes[1].expired());
// WHEN
bool has_invalid_weak_nodes = memory_strategy->collect_entities(weak_nodes);
// THEN
// The result of finding dangling node pointers should be true
ASSERT_TRUE(has_invalid_weak_nodes);
}
TEST_F(TestFindWeakNodes, allocator_strategy_no_weak_nodes) {
// GIVEN
// A vector of weak pointers to nodes, all valid
auto memory_strategy = std::make_shared<
rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy<>>();
auto existing_node1 = rclcpp::node::Node::make_shared("existing_node1");
auto existing_node2 = rclcpp::node::Node::make_shared("existing_node2");
rclcpp::memory_strategy::MemoryStrategy::WeakNodeVector weak_nodes;
weak_nodes.push_back(existing_node1->get_node_base_interface());
weak_nodes.push_back(existing_node2->get_node_base_interface());
ASSERT_FALSE(weak_nodes[0].expired());
ASSERT_FALSE(weak_nodes[1].expired());
// WHEN
bool has_invalid_weak_nodes = memory_strategy->collect_entities(weak_nodes);
// THEN
// The result of finding dangling node pointers should be false
ASSERT_FALSE(has_invalid_weak_nodes);
}

View File

@@ -31,14 +31,14 @@ namespace mock
class PublisherBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase);
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
PublisherBase()
: mock_topic_name(""), mock_queue_size(0) {}
const std::string & get_topic_name() const
const char * get_topic_name() const
{
return mock_topic_name;
return mock_topic_name.c_str();
}
size_t get_queue_size() const
{
@@ -66,7 +66,7 @@ public:
using MessageUniquePtr = std::unique_ptr<T, MessageDeleter>;
std::shared_ptr<MessageAlloc> allocator_;
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>);
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<T, Alloc>)
Publisher()
{
@@ -93,14 +93,14 @@ namespace mock
class SubscriptionBase
{
public:
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionBase);
RCLCPP_SMART_PTR_DEFINITIONS(SubscriptionBase)
SubscriptionBase()
: mock_topic_name(""), mock_queue_size(0) {}
const std::string & get_topic_name() const
const char * get_topic_name() const
{
return mock_topic_name;
return mock_topic_name.c_str();
}
size_t get_queue_size() const
{

View File

@@ -17,6 +17,8 @@
#define RCLCPP_BUILDING_LIBRARY 1 // Prevent including unavailable symbols
#include <rclcpp/mapped_ring_buffer.hpp>
#include <memory>
/*
Tests get_copy and pop on an empty mrb.
*/

75
rclcpp/test/test_node.cpp Normal file
View File

@@ -0,0 +1,75 @@
// 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 <gtest/gtest.h>
#include <string>
#include <memory>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/node.hpp"
#include "rclcpp/rclcpp.hpp"
class TestNode : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
};
/*
Testing node construction and destruction.
*/
TEST_F(TestNode, construction_and_destruction) {
{
auto node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
}
{
ASSERT_THROW({
auto node = std::make_shared<rclcpp::node::Node>("invalid_node?", "/ns");
}, rclcpp::exceptions::InvalidNodeNameError);
}
{
ASSERT_THROW({
auto node = std::make_shared<rclcpp::node::Node>("my_node", "/invalid_ns?");
}, rclcpp::exceptions::InvalidNamespaceError);
}
}
TEST_F(TestNode, get_name_and_namespace) {
{
auto node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/ns", node->get_namespace());
}
{
auto node = std::make_shared<rclcpp::node::Node>("my_node", "ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/ns", node->get_namespace());
}
{
auto node = std::make_shared<rclcpp::node::Node>("my_node", "/my/ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/my/ns", node->get_namespace());
}
{
auto node = std::make_shared<rclcpp::node::Node>("my_node", "my/ns");
EXPECT_STREQ("my_node", node->get_name());
EXPECT_STREQ("/my/ns", node->get_namespace());
}
}

View File

@@ -0,0 +1,60 @@
// 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 <gtest/gtest.h>
#include <string>
#include <memory>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcl_interfaces/msg/intra_process_message.hpp"
class TestPublisher : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::node::Node::SharedPtr node;
};
/*
Testing publisher construction and destruction.
*/
TEST_F(TestPublisher, construction_and_destruction) {
using rcl_interfaces::msg::IntraProcessMessage;
{
auto publisher = node->create_publisher<IntraProcessMessage>("topic");
}
{
ASSERT_THROW({
auto publisher = node->create_publisher<IntraProcessMessage>("invalid_topic?");
}, rclcpp::exceptions::InvalidTopicNameError);
}
}

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.
#include <gtest/gtest.h>
#include <string>
#include <memory>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcl_interfaces/srv/list_parameters.hpp"
class TestService : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::node::Node::SharedPtr node;
};
/*
Testing service construction and destruction.
*/
TEST_F(TestService, construction_and_destruction) {
using rcl_interfaces::srv::ListParameters;
auto callback =
[](const ListParameters::Request::SharedPtr, ListParameters::Response::SharedPtr) {
};
{
auto service = node->create_service<ListParameters>("service", callback);
}
{
ASSERT_THROW({
auto service = node->create_service<ListParameters>("invalid_service?", callback);
}, rclcpp::exceptions::InvalidServiceNameError);
}
}

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.
#include <gtest/gtest.h>
#include <string>
#include <memory>
#include "rclcpp/exceptions.hpp"
#include "rclcpp/rclcpp.hpp"
#include "rcl_interfaces/msg/intra_process_message.hpp"
class TestSubscription : public ::testing::Test
{
protected:
static void SetUpTestCase()
{
rclcpp::init(0, nullptr);
}
void SetUp()
{
node = std::make_shared<rclcpp::node::Node>("my_node", "/ns");
}
void TearDown()
{
node.reset();
}
rclcpp::node::Node::SharedPtr node;
};
/*
Testing subscription construction and destruction.
*/
TEST_F(TestSubscription, construction_and_destruction) {
using rcl_interfaces::msg::IntraProcessMessage;
auto callback = [](const IntraProcessMessage::SharedPtr msg) {
(void)msg;
};
{
auto sub = node->create_subscription<IntraProcessMessage>("topic", callback);
}
{
ASSERT_THROW({
auto sub = node->create_subscription<IntraProcessMessage>("invalid_topic?", callback);
}, rclcpp::exceptions::InvalidTopicNameError);
}
}

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