Compare commits
194 Commits
release-al
...
release-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9dd3d4c3c5 | ||
|
|
9c008267ef | ||
|
|
b8d72d682a | ||
|
|
5a99bbdc6e | ||
|
|
99441d8494 | ||
|
|
756ef6886d | ||
|
|
f396ff2bac | ||
|
|
2847e4aefd | ||
|
|
a4f00dc574 | ||
|
|
c0a78bac37 | ||
|
|
bfa09fb78c | ||
|
|
454d38776c | ||
|
|
b90676871d | ||
|
|
708903e5df | ||
|
|
5777bbee79 | ||
|
|
100417a98d | ||
|
|
675ad04c76 | ||
|
|
3e6a6d2781 | ||
|
|
d2112b294b | ||
|
|
81b8255e61 | ||
|
|
e6e1848b97 | ||
|
|
dbe674deb7 | ||
|
|
c07aee5cf0 | ||
|
|
2009ca676b | ||
|
|
71f5b7fe5b | ||
|
|
ce146cfdba | ||
|
|
dc6b15983a | ||
|
|
4d8b60feb5 | ||
|
|
78dfef75b4 | ||
|
|
8ca02c9d37 | ||
|
|
fee3118bdd | ||
|
|
9603db7b7d | ||
|
|
e3d4995383 | ||
|
|
197c17ae99 | ||
|
|
d9673a556d | ||
|
|
c0af872c18 | ||
|
|
9e309db793 | ||
|
|
321e0b61b0 | ||
|
|
0515e7aaf2 | ||
|
|
d00a441195 | ||
|
|
2c6d95946e | ||
|
|
d241a730fe | ||
|
|
cc98d00add | ||
|
|
e2f53b09b4 | ||
|
|
734ac278db | ||
|
|
2309e5e250 | ||
|
|
3fe1924c63 | ||
|
|
3405f489d3 | ||
|
|
1b5168195b | ||
|
|
a987f8d015 | ||
|
|
aa2d0a3954 | ||
|
|
5894a9cd4e | ||
|
|
80fc2a59cd | ||
|
|
d12154b1f9 | ||
|
|
a06a397cc6 | ||
|
|
4c876d5966 | ||
|
|
da14d88cd6 | ||
|
|
01317def07 | ||
|
|
74505f25fa | ||
|
|
ec71e6562e | ||
|
|
7f714a8601 | ||
|
|
7494350ad2 | ||
|
|
d158dd46db | ||
|
|
a71ce25a5a | ||
|
|
f28bb11078 | ||
|
|
29a1bd44dc | ||
|
|
4e74edf8d4 | ||
|
|
6ea435f743 | ||
|
|
902d558e64 | ||
|
|
1402715d76 | ||
|
|
8c5f6e4e06 | ||
|
|
2401c0f197 | ||
|
|
fc0d539837 | ||
|
|
ea76716982 | ||
|
|
8251b84f68 | ||
|
|
058de29628 | ||
|
|
e8600d1b80 | ||
|
|
5e2a76cc20 | ||
|
|
3553107823 | ||
|
|
759b063db5 | ||
|
|
aeb3c55894 | ||
|
|
bf6394004c | ||
|
|
39f0a1b93f | ||
|
|
7a5285a3d0 | ||
|
|
3c45a571e7 | ||
|
|
af0b1e6b07 | ||
|
|
0f58c5305c | ||
|
|
a5fa8277f3 | ||
|
|
458019bdff | ||
|
|
0a9f2e26a2 | ||
|
|
45f43ef523 | ||
|
|
c99b9b9734 | ||
|
|
b34a5f5504 | ||
|
|
6adfb917a9 | ||
|
|
e961189be8 | ||
|
|
6bcd9db4d6 | ||
|
|
2be9568498 | ||
|
|
e7833fa709 | ||
|
|
02311dee4c | ||
|
|
82139f1a12 | ||
|
|
e8f9344015 | ||
|
|
0c826497f1 | ||
|
|
249b7d80d8 | ||
|
|
f6ce2d8dc5 | ||
|
|
69f7bca85d | ||
|
|
4a04fe8b4a | ||
|
|
6fbf3f8c5f | ||
|
|
9f84273467 | ||
|
|
9d754a70a2 | ||
|
|
4b0ad21b3d | ||
|
|
6ec5e8e974 | ||
|
|
be0be759ec | ||
|
|
4d47ef30ae | ||
|
|
496c3e1798 | ||
|
|
8af64b95e5 | ||
|
|
fac550cc82 | ||
|
|
ebdf394dfa | ||
|
|
aa818076f8 | ||
|
|
6d79b5e0e3 | ||
|
|
300c7ca137 | ||
|
|
a7a61119fd | ||
|
|
a8d9d894c3 | ||
|
|
1fe47b717f | ||
|
|
c878e966e3 | ||
|
|
38e0911b8e | ||
|
|
16da43e1f0 | ||
|
|
7422cda6c6 | ||
|
|
bcbdd00212 | ||
|
|
dd1f38b4c8 | ||
|
|
8abdf6f670 | ||
|
|
1577ab2992 | ||
|
|
534ae69ed5 | ||
|
|
4f9f152ce8 | ||
|
|
b22fca5780 | ||
|
|
51a14baae2 | ||
|
|
6fd373d274 | ||
|
|
46d18d5627 | ||
|
|
9cd129c70f | ||
|
|
e38ac73c69 | ||
|
|
3688bb3215 | ||
|
|
4f929bd257 | ||
|
|
c2554d61bc | ||
|
|
6d2f8ae634 | ||
|
|
67dcd9fe49 | ||
|
|
f0fd292ccb | ||
|
|
13d36a1bde | ||
|
|
74002333e6 | ||
|
|
f73ebcb8d7 | ||
|
|
67151def41 | ||
|
|
7fdbc4a89a | ||
|
|
f5e5acd06e | ||
|
|
741d701350 | ||
|
|
bfa5f2cdcb | ||
|
|
7f35c72064 | ||
|
|
dbd2f6ac6a | ||
|
|
12859aca2c | ||
|
|
481d58bb6e | ||
|
|
2f6fc44d43 | ||
|
|
82d895c749 | ||
|
|
3a5bbffc65 | ||
|
|
eff6500aca | ||
|
|
03697d149a | ||
|
|
6e596336df | ||
|
|
ff9b492041 | ||
|
|
10ce7a3bd6 | ||
|
|
26d52d949e | ||
|
|
e95d3f249d | ||
|
|
39c663ea64 | ||
|
|
1c9eb0b367 | ||
|
|
f1e7ea5ca0 | ||
|
|
2b342357d9 | ||
|
|
9dce2808ea | ||
|
|
c469a8a2e9 | ||
|
|
eb3a793eb7 | ||
|
|
5bd71c1f80 | ||
|
|
0a478e5233 | ||
|
|
fabea6b4b6 | ||
|
|
2b8d38d012 | ||
|
|
24f9df6102 | ||
|
|
660d762072 | ||
|
|
f4a094afc8 | ||
|
|
e42d7d7044 | ||
|
|
39b66fa0b5 | ||
|
|
8c47f6151b | ||
|
|
361faf650d | ||
|
|
dcd5117f89 | ||
|
|
ca64af3145 | ||
|
|
36cd6e3cdd | ||
|
|
3dd193efbb | ||
|
|
ef89f5a5ab | ||
|
|
51695d1b24 | ||
|
|
0bbe9b2099 | ||
|
|
f531b02928 | ||
|
|
f0faa1fefe |
1
rclcpp/.gitignore
vendored
Normal file
1
rclcpp/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
doc_output
|
||||
@@ -1,34 +1,254 @@
|
||||
cmake_minimum_required(VERSION 2.8.3)
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(rclcpp)
|
||||
|
||||
find_package(ament_cmake REQUIRED)
|
||||
find_package(builtin_interfaces REQUIRED)
|
||||
find_package(rcl REQUIRED)
|
||||
find_package(rcl_interfaces REQUIRED)
|
||||
find_package(rmw REQUIRED)
|
||||
find_package(rmw_implementation REQUIRED)
|
||||
find_package(rosidl_generator_cpp REQUIRED)
|
||||
find_package(rosidl_typesupport_c REQUIRED)
|
||||
find_package(rosidl_typesupport_cpp REQUIRED)
|
||||
|
||||
ament_export_dependencies(rmw)
|
||||
ament_export_dependencies(rcl_interfaces)
|
||||
# Default to C++14
|
||||
if(NOT CMAKE_CXX_STANDARD)
|
||||
set(CMAKE_CXX_STANDARD 14)
|
||||
endif()
|
||||
if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang")
|
||||
# we dont use add_compile_options with pedantic in message packages
|
||||
# because the Python C extensions dont comply with it
|
||||
# TODO(mikaelarguedas) change to add_compile_options
|
||||
# once this is not a message package anymore
|
||||
# https://github.com/ros2/system_tests/issues/191
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wpedantic")
|
||||
endif()
|
||||
|
||||
include_directories(include)
|
||||
|
||||
set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/any_executable.cpp
|
||||
src/rclcpp/callback_group.cpp
|
||||
src/rclcpp/client.cpp
|
||||
src/rclcpp/context.cpp
|
||||
src/rclcpp/contexts/default_context.cpp
|
||||
src/rclcpp/event.cpp
|
||||
src/rclcpp/exceptions.cpp
|
||||
src/rclcpp/executor.cpp
|
||||
src/rclcpp/executors.cpp
|
||||
src/rclcpp/expand_topic_or_service_name.cpp
|
||||
src/rclcpp/executors/multi_threaded_executor.cpp
|
||||
src/rclcpp/executors/single_threaded_executor.cpp
|
||||
src/rclcpp/graph_listener.cpp
|
||||
src/rclcpp/intra_process_manager.cpp
|
||||
src/rclcpp/intra_process_manager_impl.cpp
|
||||
src/rclcpp/memory_strategies.cpp
|
||||
src/rclcpp/memory_strategy.cpp
|
||||
src/rclcpp/node.cpp
|
||||
src/rclcpp/node_interfaces/node_base.cpp
|
||||
src/rclcpp/node_interfaces/node_graph.cpp
|
||||
src/rclcpp/node_interfaces/node_parameters.cpp
|
||||
src/rclcpp/node_interfaces/node_services.cpp
|
||||
src/rclcpp/node_interfaces/node_timers.cpp
|
||||
src/rclcpp/node_interfaces/node_topics.cpp
|
||||
src/rclcpp/parameter.cpp
|
||||
src/rclcpp/parameter_client.cpp
|
||||
src/rclcpp/parameter_service.cpp
|
||||
src/rclcpp/publisher.cpp
|
||||
src/rclcpp/service.cpp
|
||||
src/rclcpp/subscription.cpp
|
||||
src/rclcpp/timer.cpp
|
||||
src/rclcpp/type_support.cpp
|
||||
src/rclcpp/utilities.cpp
|
||||
)
|
||||
|
||||
add_library(${PROJECT_NAME} SHARED
|
||||
${${PROJECT_NAME}_SRCS})
|
||||
ament_target_dependencies(${PROJECT_NAME}
|
||||
"builtin_interfaces"
|
||||
"rcl"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp")
|
||||
|
||||
# Causes the visibility macros to use dllexport rather than dllimport,
|
||||
# which is appropriate when building the dll but not consuming it.
|
||||
target_compile_definitions(${PROJECT_NAME}
|
||||
PRIVATE "RCLCPP_BUILDING_LIBRARY")
|
||||
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME}
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
|
||||
ament_export_dependencies(ament_cmake)
|
||||
ament_export_dependencies(builtin_interfaces)
|
||||
ament_export_dependencies(rcl)
|
||||
ament_export_dependencies(rosidl_generator_cpp)
|
||||
ament_export_dependencies(rosidl_typesupport_c)
|
||||
ament_export_dependencies(rosidl_typesupport_cpp)
|
||||
|
||||
ament_export_include_directories(include)
|
||||
|
||||
if(AMENT_ENABLE_TESTING)
|
||||
ament_export_libraries(${PROJECT_NAME})
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
|
||||
find_package(rmw REQUIRED)
|
||||
find_package(rmw_implementation_cmake REQUIRED)
|
||||
|
||||
if(NOT WIN32)
|
||||
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall -Wextra")
|
||||
ament_add_gtest(test_client test/test_client.cpp)
|
||||
if(TARGET test_client)
|
||||
target_include_directories(test_client PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries(test_client ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_expand_topic_or_service_name test/test_expand_topic_or_service_name.cpp)
|
||||
if(TARGET test_expand_topic_or_service_name)
|
||||
target_include_directories(test_expand_topic_or_service_name PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries(test_expand_topic_or_service_name ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
include_directories(include)
|
||||
|
||||
ament_add_gtest(test_function_traits test/test_function_traits.cpp)
|
||||
if(TARGET test_function_traits)
|
||||
target_include_directories(test_function_traits PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_mapped_ring_buffer test/test_mapped_ring_buffer.cpp)
|
||||
if(TARGET test_mapped_ring_buffer)
|
||||
target_include_directories(test_mapped_ring_buffer PUBLIC
|
||||
${rcl_INCLUDE_DIRS}
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_intra_process_manager test/test_intra_process_manager.cpp)
|
||||
if(TARGET test_intra_process_manager)
|
||||
target_include_directories(test_intra_process_manager PUBLIC
|
||||
"${rcl_interfaces_INCLUDE_DIRS}"
|
||||
"${rmw_INCLUDE_DIRS}")
|
||||
${rcl_INCLUDE_DIRS}
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_node test/test_node.cpp)
|
||||
if(TARGET test_node)
|
||||
target_include_directories(test_node PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries(test_node ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_publisher test/test_publisher.cpp)
|
||||
if(TARGET test_publisher)
|
||||
target_include_directories(test_publisher PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries(test_publisher ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_rate test/test_rate.cpp
|
||||
ENV RCL_ASSERT_RMW_ID_MATCHES=${rmw_implementation})
|
||||
if(TARGET test_rate)
|
||||
target_include_directories(test_rate PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries(test_rate
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_service test/test_service.cpp)
|
||||
if(TARGET test_service)
|
||||
target_include_directories(test_service PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries(test_service ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_subscription test/test_subscription.cpp)
|
||||
if(TARGET test_subscription)
|
||||
target_include_directories(test_subscription PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries(test_subscription ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_find_weak_nodes test/test_find_weak_nodes.cpp)
|
||||
if(TARGET test_find_weak_nodes)
|
||||
target_include_directories(test_find_weak_nodes PUBLIC
|
||||
${rcl_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
get_default_rmw_implementation(default_rmw)
|
||||
find_package(${default_rmw} REQUIRED)
|
||||
get_rmw_typesupport(typesupport_impls_cpp "${default_rmw}" LANGUAGE "cpp")
|
||||
get_rmw_typesupport(typesupport_impls_c "${default_rmw}" LANGUAGE "c")
|
||||
set(mock_msg_files
|
||||
"test/mock_msgs/srv/Mock.srv")
|
||||
rosidl_generate_interfaces(mock_msgs
|
||||
${mock_msg_files}
|
||||
LIBRARY_NAME "rclcpp"
|
||||
SKIP_INSTALL)
|
||||
|
||||
set(append_library_dirs "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
if(WIN32)
|
||||
set(append_library_dirs "${append_library_dirs}/$<CONFIG>")
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_externally_defined_services test/test_externally_defined_services.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_externally_defined_services)
|
||||
target_include_directories(test_externally_defined_services PUBLIC
|
||||
${rcl_INCLUDE_DIRS}
|
||||
)
|
||||
target_link_libraries(test_externally_defined_services ${PROJECT_NAME})
|
||||
foreach(typesupport_impl_cpp ${typesupport_impls_cpp})
|
||||
rosidl_target_interfaces(test_externally_defined_services
|
||||
mock_msgs ${typesupport_impl_cpp})
|
||||
endforeach()
|
||||
foreach(typesupport_impl_c ${typesupport_impls_c})
|
||||
rosidl_target_interfaces(test_externally_defined_services
|
||||
mock_msgs ${typesupport_impl_c})
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_time test/test_time.cpp
|
||||
APPEND_LIBRARY_DIRS "${append_library_dirs}")
|
||||
if(TARGET test_time)
|
||||
ament_target_dependencies(test_time
|
||||
"rcl")
|
||||
target_link_libraries(test_time ${PROJECT_NAME})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
@@ -37,11 +257,11 @@ ament_package(
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY include/
|
||||
DESTINATION include
|
||||
DIRECTORY cmake
|
||||
DESTINATION share/${PROJECT_NAME}
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY src/
|
||||
DESTINATION src/rclcpp
|
||||
DIRECTORY include/
|
||||
DESTINATION include
|
||||
)
|
||||
|
||||
28
rclcpp/Doxyfile
Normal file
28
rclcpp/Doxyfile
Normal 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"
|
||||
26
rclcpp/cmake/rclcpp_create_node_main.cmake
Normal file
26
rclcpp/cmake/rclcpp_create_node_main.cmake
Normal 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()
|
||||
17
rclcpp/cmake/rclcpp_package_hook.cmake
Normal file
17
rclcpp/cmake/rclcpp_package_hook.cmake
Normal 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}")
|
||||
61
rclcpp/cmake/rclcpp_register_node_plugins.cmake
Normal file
61
rclcpp/cmake/rclcpp_register_node_plugins.cmake
Normal 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()
|
||||
@@ -17,6 +17,8 @@
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rcl/allocator.h"
|
||||
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
@@ -27,6 +29,66 @@ namespace allocator
|
||||
template<typename T, typename Alloc>
|
||||
using AllocRebind = typename std::allocator_traits<Alloc>::template rebind_traits<T>;
|
||||
|
||||
template<typename Alloc>
|
||||
void * retyped_allocate(size_t size, void * untyped_allocator)
|
||||
{
|
||||
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
|
||||
if (!typed_allocator) {
|
||||
throw std::runtime_error("Received incorrect allocator type");
|
||||
}
|
||||
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
void retyped_deallocate(void * untyped_pointer, void * untyped_allocator)
|
||||
{
|
||||
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
|
||||
if (!typed_allocator) {
|
||||
throw std::runtime_error("Received incorrect allocator type");
|
||||
}
|
||||
auto typed_ptr = static_cast<T *>(untyped_pointer);
|
||||
std::allocator_traits<Alloc>::deallocate(*typed_allocator, typed_ptr, 1);
|
||||
}
|
||||
|
||||
template<typename T, typename Alloc>
|
||||
void * retyped_reallocate(void * untyped_pointer, size_t size, void * untyped_allocator)
|
||||
{
|
||||
auto typed_allocator = static_cast<Alloc *>(untyped_allocator);
|
||||
if (!typed_allocator) {
|
||||
throw std::runtime_error("Received incorrect allocator type");
|
||||
}
|
||||
auto typed_ptr = static_cast<T *>(untyped_pointer);
|
||||
std::allocator_traits<Alloc>::deallocate(*typed_allocator, typed_ptr, 1);
|
||||
return std::allocator_traits<Alloc>::allocate(*typed_allocator, size);
|
||||
}
|
||||
|
||||
|
||||
// Convert a std::allocator_traits-formatted Allocator into an rcl allocator
|
||||
template<typename T, typename Alloc,
|
||||
typename std::enable_if<!std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
|
||||
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
|
||||
{
|
||||
rcl_allocator_t rcl_allocator = rcl_get_default_allocator();
|
||||
#ifndef _WIN32
|
||||
rcl_allocator.allocate = &retyped_allocate<Alloc>;
|
||||
rcl_allocator.deallocate = &retyped_deallocate<T, Alloc>;
|
||||
rcl_allocator.reallocate = &retyped_reallocate<T, Alloc>;
|
||||
rcl_allocator.state = &allocator;
|
||||
#else
|
||||
(void)allocator; // Remove warning
|
||||
#endif
|
||||
return rcl_allocator;
|
||||
}
|
||||
|
||||
// TODO(jacquelinekay) Workaround for an incomplete implementation of std::allocator<void>
|
||||
template<typename T, typename Alloc,
|
||||
typename std::enable_if<std::is_same<Alloc, std::allocator<void>>::value>::type * = nullptr>
|
||||
rcl_allocator_t get_rcl_allocator(Alloc & allocator)
|
||||
{
|
||||
(void)allocator;
|
||||
return rcl_get_default_allocator();
|
||||
}
|
||||
|
||||
} // namespace allocator
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -16,10 +16,10 @@
|
||||
#define RCLCPP__ALLOCATOR__ALLOCATOR_DELETER_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace allocator
|
||||
{
|
||||
|
||||
|
||||
@@ -12,13 +12,19 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_ANY_EXECUTABLE_HPP_
|
||||
#define RCLCPP_RCLCPP_ANY_EXECUTABLE_HPP_
|
||||
#ifndef RCLCPP__ANY_EXECUTABLE_HPP_
|
||||
#define RCLCPP__ANY_EXECUTABLE_HPP_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <rclcpp/macros.hpp>
|
||||
#include <rclcpp/node.hpp>
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/timer.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -27,10 +33,14 @@ namespace executor
|
||||
|
||||
struct AnyExecutable
|
||||
{
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(AnyExecutable);
|
||||
AnyExecutable()
|
||||
: subscription(0), timer(0), callback_group(0), node(0)
|
||||
{}
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(AnyExecutable)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
AnyExecutable();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~AnyExecutable();
|
||||
|
||||
// Only one of the following pointers will be set.
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr subscription;
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr subscription_intra_process;
|
||||
@@ -39,10 +49,10 @@ struct AnyExecutable
|
||||
rclcpp::client::ClientBase::SharedPtr client;
|
||||
// These are used to keep the scope on the containing items
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group;
|
||||
rclcpp::node::Node::SharedPtr node;
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base;
|
||||
};
|
||||
|
||||
} /* executor */
|
||||
} /* rclcpp */
|
||||
} // namespace executor
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif
|
||||
#endif // RCLCPP__ANY_EXECUTABLE_HPP_
|
||||
|
||||
@@ -12,16 +12,17 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_ANY_SERVICE_CALLBACK_HPP_
|
||||
#define RCLCPP_RCLCPP_ANY_SERVICE_CALLBACK_HPP_
|
||||
|
||||
#include <rclcpp/function_traits.hpp>
|
||||
#ifndef RCLCPP__ANY_SERVICE_CALLBACK_HPP_
|
||||
#define RCLCPP__ANY_SERVICE_CALLBACK_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
|
||||
#include <rmw/types.h>
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/types.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -56,7 +57,7 @@ public:
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::same_arguments<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrCallback
|
||||
>::value
|
||||
@@ -70,7 +71,7 @@ public:
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::same_arguments<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrWithRequestHeaderCallback
|
||||
>::value
|
||||
@@ -97,7 +98,7 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
} /* namespace any_service_callback */
|
||||
} /* namespace rclcpp */
|
||||
} // namespace any_service_callback
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_ANY_SERVICE_CALLBACK_HPP_ */
|
||||
#endif // RCLCPP__ANY_SERVICE_CALLBACK_HPP_
|
||||
|
||||
@@ -19,10 +19,13 @@
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -70,7 +73,7 @@ public:
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::same_arguments<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrCallback
|
||||
>::value
|
||||
@@ -84,7 +87,7 @@ public:
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::same_arguments<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
SharedPtrWithInfoCallback
|
||||
>::value
|
||||
@@ -98,7 +101,7 @@ public:
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::same_arguments<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
ConstSharedPtrCallback
|
||||
>::value
|
||||
@@ -112,7 +115,7 @@ public:
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::same_arguments<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
ConstSharedPtrWithInfoCallback
|
||||
>::value
|
||||
@@ -126,7 +129,7 @@ public:
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::same_arguments<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
UniquePtrCallback
|
||||
>::value
|
||||
@@ -140,7 +143,7 @@ public:
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::same_arguments<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
UniquePtrWithInfoCallback
|
||||
>::value
|
||||
@@ -181,16 +184,16 @@ public:
|
||||
{
|
||||
(void)message_info;
|
||||
if (shared_ptr_callback_) {
|
||||
typename MessageT::SharedPtr shared_message = std::move(message);
|
||||
typename std::shared_ptr<MessageT> shared_message = std::move(message);
|
||||
shared_ptr_callback_(shared_message);
|
||||
} else if (shared_ptr_with_info_callback_) {
|
||||
typename MessageT::SharedPtr shared_message = std::move(message);
|
||||
typename std::shared_ptr<MessageT> shared_message = std::move(message);
|
||||
shared_ptr_with_info_callback_(shared_message, message_info);
|
||||
} else if (const_shared_ptr_callback_) {
|
||||
typename MessageT::ConstSharedPtr const_shared_message = std::move(message);
|
||||
typename std::shared_ptr<MessageT const> const_shared_message = std::move(message);
|
||||
const_shared_ptr_callback_(const_shared_message);
|
||||
} else if (const_shared_ptr_with_info_callback_) {
|
||||
typename MessageT::ConstSharedPtr const_shared_message = std::move(message);
|
||||
typename std::shared_ptr<MessageT const> const_shared_message = std::move(message);
|
||||
const_shared_ptr_with_info_callback_(const_shared_message, message_info);
|
||||
} else if (unique_ptr_callback_) {
|
||||
unique_ptr_callback_(std::move(message));
|
||||
|
||||
@@ -12,26 +12,30 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_CALLBACK_GROUP_HPP_
|
||||
#define RCLCPP_RCLCPP_CALLBACK_GROUP_HPP_
|
||||
#ifndef RCLCPP__CALLBACK_GROUP_HPP_
|
||||
#define RCLCPP__CALLBACK_GROUP_HPP_
|
||||
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <rclcpp/subscription.hpp>
|
||||
#include <rclcpp/timer.hpp>
|
||||
#include <rclcpp/service.hpp>
|
||||
#include <rclcpp/client.hpp>
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/timer.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
// Forward declarations for friend statement in class CallbackGroup
|
||||
namespace node
|
||||
namespace node_interfaces
|
||||
{
|
||||
class Node;
|
||||
} // namespace node
|
||||
class NodeServices;
|
||||
class NodeTimers;
|
||||
class NodeTopics;
|
||||
} // namespace node_interfaces
|
||||
|
||||
namespace callback_group
|
||||
{
|
||||
@@ -44,87 +48,70 @@ enum class CallbackGroupType
|
||||
|
||||
class CallbackGroup
|
||||
{
|
||||
friend class rclcpp::node::Node;
|
||||
friend class rclcpp::node_interfaces::NodeServices;
|
||||
friend class rclcpp::node_interfaces::NodeTimers;
|
||||
friend class rclcpp::node_interfaces::NodeTopics;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(CallbackGroup);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(CallbackGroup)
|
||||
|
||||
CallbackGroup(CallbackGroupType group_type)
|
||||
: type_(group_type), can_be_taken_from_(true)
|
||||
{}
|
||||
RCLCPP_PUBLIC
|
||||
explicit CallbackGroup(CallbackGroupType group_type);
|
||||
|
||||
const std::vector<subscription::SubscriptionBase::WeakPtr> &
|
||||
get_subscription_ptrs() const
|
||||
{
|
||||
return subscription_ptrs_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<rclcpp::subscription::SubscriptionBase::WeakPtr> &
|
||||
get_subscription_ptrs() const;
|
||||
|
||||
const std::vector<timer::TimerBase::WeakPtr> &
|
||||
get_timer_ptrs() const
|
||||
{
|
||||
return timer_ptrs_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<rclcpp::timer::TimerBase::WeakPtr> &
|
||||
get_timer_ptrs() const;
|
||||
|
||||
const std::vector<service::ServiceBase::SharedPtr> &
|
||||
get_service_ptrs() const
|
||||
{
|
||||
return service_ptrs_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<rclcpp::service::ServiceBase::WeakPtr> &
|
||||
get_service_ptrs() const;
|
||||
|
||||
const std::vector<client::ClientBase::SharedPtr> &
|
||||
get_client_ptrs() const
|
||||
{
|
||||
return client_ptrs_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<rclcpp::client::ClientBase::WeakPtr> &
|
||||
get_client_ptrs() const;
|
||||
|
||||
std::atomic_bool & can_be_taken_from()
|
||||
{
|
||||
return can_be_taken_from_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
std::atomic_bool &
|
||||
can_be_taken_from();
|
||||
|
||||
const CallbackGroupType & type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
const CallbackGroupType &
|
||||
type() const;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(CallbackGroup);
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(CallbackGroup)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_subscription(
|
||||
const subscription::SubscriptionBase::SharedPtr subscription_ptr)
|
||||
{
|
||||
subscription_ptrs_.push_back(subscription_ptr);
|
||||
}
|
||||
add_subscription(const rclcpp::subscription::SubscriptionBase::SharedPtr subscription_ptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_timer(const timer::TimerBase::SharedPtr timer_ptr)
|
||||
{
|
||||
timer_ptrs_.push_back(timer_ptr);
|
||||
}
|
||||
add_timer(const rclcpp::timer::TimerBase::SharedPtr timer_ptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_service(const service::ServiceBase::SharedPtr service_ptr)
|
||||
{
|
||||
service_ptrs_.push_back(service_ptr);
|
||||
}
|
||||
add_service(const rclcpp::service::ServiceBase::SharedPtr service_ptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_client(const client::ClientBase::SharedPtr client_ptr)
|
||||
{
|
||||
client_ptrs_.push_back(client_ptr);
|
||||
}
|
||||
add_client(const rclcpp::client::ClientBase::SharedPtr client_ptr);
|
||||
|
||||
CallbackGroupType type_;
|
||||
std::vector<subscription::SubscriptionBase::WeakPtr> subscription_ptrs_;
|
||||
std::vector<timer::TimerBase::WeakPtr> timer_ptrs_;
|
||||
std::vector<service::ServiceBase::SharedPtr> service_ptrs_;
|
||||
std::vector<client::ClientBase::SharedPtr> client_ptrs_;
|
||||
// Mutex to protect the subsequent vectors of pointers.
|
||||
mutable std::mutex mutex_;
|
||||
std::vector<rclcpp::subscription::SubscriptionBase::WeakPtr> subscription_ptrs_;
|
||||
std::vector<rclcpp::timer::TimerBase::WeakPtr> timer_ptrs_;
|
||||
std::vector<rclcpp::service::ServiceBase::WeakPtr> service_ptrs_;
|
||||
std::vector<rclcpp::client::ClientBase::WeakPtr> client_ptrs_;
|
||||
std::atomic_bool can_be_taken_from_;
|
||||
|
||||
};
|
||||
|
||||
} /* namespace callback_group */
|
||||
} /* namespace rclcpp */
|
||||
} // namespace callback_group
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_CALLBACK_GROUP_HPP_ */
|
||||
#endif // RCLCPP__CALLBACK_GROUP_HPP_
|
||||
|
||||
@@ -12,113 +12,190 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_CLIENT_HPP_
|
||||
#define RCLCPP_RCLCPP_CLIENT_HPP_
|
||||
#ifndef RCLCPP__CLIENT_HPP_
|
||||
#define RCLCPP__CLIENT_HPP_
|
||||
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <utility>
|
||||
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
#include "rcl/client.h"
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/wait.h"
|
||||
|
||||
#include <rclcpp/macros.hpp>
|
||||
#include <rclcpp/utilities.hpp>
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace node_interfaces
|
||||
{
|
||||
class NodeBaseInterface;
|
||||
} // namespace node_interfaces
|
||||
|
||||
namespace client
|
||||
{
|
||||
|
||||
class ClientBase
|
||||
{
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientBase);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ClientBase)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
ClientBase(
|
||||
std::shared_ptr<rmw_node_t> node_handle,
|
||||
rmw_client_t * client_handle,
|
||||
const std::string & service_name)
|
||||
: node_handle_(node_handle), client_handle_(client_handle), service_name_(service_name)
|
||||
{}
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
|
||||
const std::string & service_name);
|
||||
|
||||
virtual ~ClientBase()
|
||||
{
|
||||
if (client_handle_) {
|
||||
if (rmw_destroy_client(client_handle_) != RMW_RET_OK) {
|
||||
fprintf(stderr,
|
||||
"Error in destruction of rmw client handle: %s\n", rmw_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~ClientBase();
|
||||
|
||||
const std::string & get_service_name() const
|
||||
{
|
||||
return this->service_name_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
const std::string &
|
||||
get_service_name() const;
|
||||
|
||||
const rmw_client_t * get_client_handle() const
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_client_t *
|
||||
get_client_handle() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
service_is_ready() const;
|
||||
|
||||
template<typename RatioT = std::milli>
|
||||
bool
|
||||
wait_for_service(
|
||||
std::chrono::duration<int64_t, RatioT> timeout = std::chrono::duration<int64_t, RatioT>(-1))
|
||||
{
|
||||
return this->client_handle_;
|
||||
return wait_for_service_nanoseconds(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
virtual std::shared_ptr<void> create_response() = 0;
|
||||
virtual std::shared_ptr<void> create_request_header() = 0;
|
||||
virtual std::shared_ptr<rmw_request_id_t> create_request_header() = 0;
|
||||
virtual void handle_response(
|
||||
std::shared_ptr<void> & request_header, std::shared_ptr<void> & response) = 0;
|
||||
std::shared_ptr<rmw_request_id_t> request_header, std::shared_ptr<void> response) = 0;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(ClientBase);
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(ClientBase)
|
||||
|
||||
std::shared_ptr<rmw_node_t> node_handle_;
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
wait_for_service_nanoseconds(std::chrono::nanoseconds timeout);
|
||||
|
||||
rmw_client_t * client_handle_;
|
||||
RCLCPP_PUBLIC
|
||||
rcl_node_t *
|
||||
get_rcl_node_handle() const;
|
||||
|
||||
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
|
||||
rcl_client_t client_handle_ = rcl_get_zero_initialized_client();
|
||||
std::string service_name_;
|
||||
|
||||
};
|
||||
|
||||
template<typename ServiceT>
|
||||
class Client : public ClientBase
|
||||
{
|
||||
public:
|
||||
using Promise = std::promise<typename ServiceT::Response::SharedPtr>;
|
||||
using SharedRequest = typename ServiceT::Request::SharedPtr;
|
||||
using SharedResponse = typename ServiceT::Response::SharedPtr;
|
||||
|
||||
using Promise = std::promise<SharedResponse>;
|
||||
using PromiseWithRequest = std::promise<std::pair<SharedRequest, SharedResponse>>;
|
||||
|
||||
using SharedPromise = std::shared_ptr<Promise>;
|
||||
using SharedFuture = std::shared_future<typename ServiceT::Response::SharedPtr>;
|
||||
using SharedPromiseWithRequest = std::shared_ptr<PromiseWithRequest>;
|
||||
|
||||
using SharedFuture = std::shared_future<SharedResponse>;
|
||||
using SharedFutureWithRequest = std::shared_future<std::pair<SharedRequest, SharedResponse>>;
|
||||
|
||||
using CallbackType = std::function<void(SharedFuture)>;
|
||||
using CallbackWithRequestType = std::function<void(SharedFutureWithRequest)>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Client);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Client)
|
||||
|
||||
Client(
|
||||
std::shared_ptr<rmw_node_t> node_handle,
|
||||
rmw_client_t * client_handle,
|
||||
const std::string & service_name)
|
||||
: ClientBase(node_handle, client_handle, service_name)
|
||||
{}
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
|
||||
const std::string & service_name,
|
||||
rcl_client_options_t & client_options)
|
||||
: ClientBase(node_base, node_graph, service_name)
|
||||
{
|
||||
using rosidl_typesupport_cpp::get_service_type_support_handle;
|
||||
auto service_type_support_handle =
|
||||
get_service_type_support_handle<ServiceT>();
|
||||
rcl_ret_t ret = rcl_client_init(
|
||||
&client_handle_,
|
||||
this->get_rcl_node_handle(),
|
||||
service_type_support_handle,
|
||||
service_name.c_str(),
|
||||
&client_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
if (ret == RCL_RET_SERVICE_NAME_INVALID) {
|
||||
auto rcl_node_handle = this->get_rcl_node_handle();
|
||||
// this will throw on any validation problem
|
||||
rcl_reset_error();
|
||||
expand_topic_or_service_name(
|
||||
service_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle),
|
||||
true);
|
||||
}
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create client");
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<void> create_response()
|
||||
virtual ~Client()
|
||||
{
|
||||
if (rcl_client_fini(&client_handle_, this->get_rcl_node_handle()) != RCL_RET_OK) {
|
||||
fprintf(stderr,
|
||||
"Error in destruction of rcl client handle: %s\n", rcl_get_error_string_safe());
|
||||
rcl_reset_error();
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
create_response()
|
||||
{
|
||||
return std::shared_ptr<void>(new typename ServiceT::Response());
|
||||
}
|
||||
|
||||
std::shared_ptr<void> create_request_header()
|
||||
std::shared_ptr<rmw_request_id_t>
|
||||
create_request_header()
|
||||
{
|
||||
// TODO(wjwwood): This should probably use rmw_request_id's allocator.
|
||||
// (since it is a C type)
|
||||
return std::shared_ptr<void>(new rmw_request_id_t);
|
||||
return std::shared_ptr<rmw_request_id_t>(new rmw_request_id_t);
|
||||
}
|
||||
|
||||
void handle_response(std::shared_ptr<void> & request_header, std::shared_ptr<void> & response)
|
||||
void
|
||||
handle_response(std::shared_ptr<rmw_request_id_t> request_header,
|
||||
std::shared_ptr<void> response)
|
||||
{
|
||||
auto typed_request_header = std::static_pointer_cast<rmw_request_id_t>(request_header);
|
||||
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
|
||||
auto typed_response = std::static_pointer_cast<typename ServiceT::Response>(response);
|
||||
int64_t sequence_number = typed_request_header->sequence_number;
|
||||
// TODO this must check if the sequence_number is valid otherwise the call_promise will be null
|
||||
int64_t sequence_number = request_header->sequence_number;
|
||||
// TODO(esteve) this should throw instead since it is not expected to happen in the first place
|
||||
if (this->pending_requests_.count(sequence_number) == 0) {
|
||||
fprintf(stderr, "Received invalid sequence number. Ignoring...\n");
|
||||
return;
|
||||
}
|
||||
auto tuple = this->pending_requests_[sequence_number];
|
||||
auto call_promise = std::get<0>(tuple);
|
||||
auto callback = std::get<1>(tuple);
|
||||
@@ -128,37 +205,72 @@ public:
|
||||
callback(future);
|
||||
}
|
||||
|
||||
SharedFuture async_send_request(
|
||||
typename ServiceT::Request::SharedPtr request)
|
||||
SharedFuture
|
||||
async_send_request(SharedRequest request)
|
||||
{
|
||||
return async_send_request(request, [](SharedFuture) {});
|
||||
}
|
||||
|
||||
SharedFuture async_send_request(
|
||||
typename ServiceT::Request::SharedPtr request,
|
||||
CallbackType cb)
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
CallbackType
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
SharedFuture
|
||||
async_send_request(SharedRequest request, CallbackT && cb)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(pending_requests_mutex_);
|
||||
int64_t sequence_number;
|
||||
if (RMW_RET_OK != rmw_send_request(get_client_handle(), request.get(), &sequence_number)) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("failed to send request: ") + rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
rcl_ret_t ret = rcl_send_request(get_client_handle(), request.get(), &sequence_number);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to send request");
|
||||
}
|
||||
|
||||
SharedPromise call_promise = std::make_shared<Promise>();
|
||||
SharedFuture f(call_promise->get_future());
|
||||
pending_requests_[sequence_number] = std::make_tuple(call_promise, cb, f);
|
||||
pending_requests_[sequence_number] =
|
||||
std::make_tuple(call_promise, std::forward<CallbackType>(cb), f);
|
||||
return f;
|
||||
}
|
||||
|
||||
template<
|
||||
typename CallbackT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<
|
||||
CallbackT,
|
||||
CallbackWithRequestType
|
||||
>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
SharedFutureWithRequest
|
||||
async_send_request(SharedRequest request, CallbackT && cb)
|
||||
{
|
||||
SharedPromiseWithRequest promise = std::make_shared<PromiseWithRequest>();
|
||||
SharedFutureWithRequest future_with_request(promise->get_future());
|
||||
|
||||
auto wrapping_cb = [future_with_request, promise, request, &cb](SharedFuture future) {
|
||||
auto response = future.get();
|
||||
promise->set_value(std::make_pair(request, response));
|
||||
cb(future_with_request);
|
||||
};
|
||||
|
||||
async_send_request(request, wrapping_cb);
|
||||
|
||||
return future_with_request;
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Client);
|
||||
RCLCPP_DISABLE_COPY(Client)
|
||||
|
||||
std::map<int64_t, std::tuple<SharedPromise, CallbackType, SharedFuture>> pending_requests_;
|
||||
std::mutex pending_requests_mutex_;
|
||||
};
|
||||
|
||||
} /* namespace client */
|
||||
} /* namespace rclcpp */
|
||||
} // namespace client
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_CLIENT_HPP_ */
|
||||
#endif // RCLCPP__CLIENT_HPP_
|
||||
|
||||
@@ -12,20 +12,20 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_CONTEXT_HPP_
|
||||
#define RCLCPP_RCLCPP_CONTEXT_HPP_
|
||||
|
||||
#include <rclcpp/macros.hpp>
|
||||
#ifndef RCLCPP__CONTEXT_HPP_
|
||||
#define RCLCPP__CONTEXT_HPP_
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <typeinfo>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include <rmw/rmw.h>
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -36,9 +36,10 @@ namespace context
|
||||
class Context
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Context);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Context)
|
||||
|
||||
Context() {}
|
||||
RCLCPP_PUBLIC
|
||||
Context();
|
||||
|
||||
template<typename SubContext, typename ... Args>
|
||||
std::shared_ptr<SubContext>
|
||||
@@ -51,7 +52,7 @@ public:
|
||||
auto it = sub_contexts_.find(type_i);
|
||||
if (it == sub_contexts_.end()) {
|
||||
// It doesn't exist yet, make it
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
sub_context = std::shared_ptr<SubContext>(
|
||||
new SubContext(std::forward<Args>(args) ...),
|
||||
[] (SubContext * sub_context_ptr) {
|
||||
@@ -67,14 +68,13 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Context);
|
||||
RCLCPP_DISABLE_COPY(Context)
|
||||
|
||||
std::unordered_map<std::type_index, std::shared_ptr<void>> sub_contexts_;
|
||||
std::mutex mutex_;
|
||||
|
||||
};
|
||||
|
||||
} /* namespace context */
|
||||
} /* namespace rclcpp */
|
||||
} // namespace context
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_CONTEXT_HPP_ */
|
||||
#endif // RCLCPP__CONTEXT_HPP_
|
||||
|
||||
@@ -12,10 +12,11 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_CONTEXTS_DEFAULT_CONTEXT_HPP_
|
||||
#define RCLCPP_RCLCPP_CONTEXTS_DEFAULT_CONTEXT_HPP_
|
||||
#ifndef RCLCPP__CONTEXTS__DEFAULT_CONTEXT_HPP_
|
||||
#define RCLCPP__CONTEXTS__DEFAULT_CONTEXT_HPP_
|
||||
|
||||
#include <rclcpp/context.hpp>
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -27,21 +28,18 @@ namespace default_context
|
||||
class DefaultContext : public rclcpp::context::Context
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(DefaultContext);
|
||||
|
||||
DefaultContext() {}
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(DefaultContext)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DefaultContext();
|
||||
};
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
DefaultContext::SharedPtr
|
||||
get_global_default_context()
|
||||
{
|
||||
static DefaultContext::SharedPtr default_context = DefaultContext::make_shared();
|
||||
return default_context;
|
||||
}
|
||||
get_global_default_context();
|
||||
|
||||
} // namespace default_context
|
||||
} // namespace contexts
|
||||
} // namespace rclcpp
|
||||
} // namespace default_context
|
||||
} // namespace contexts
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_CONTEXTS_DEFAULT_CONTEXT_HPP_ */
|
||||
#endif // RCLCPP__CONTEXTS__DEFAULT_CONTEXT_HPP_
|
||||
|
||||
51
rclcpp/include/rclcpp/create_publisher.hpp
Normal file
51
rclcpp/include/rclcpp/create_publisher.hpp
Normal 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_
|
||||
62
rclcpp/include/rclcpp/create_subscription.hpp
Normal file
62
rclcpp/include/rclcpp/create_subscription.hpp
Normal 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_
|
||||
58
rclcpp/include/rclcpp/event.hpp
Normal file
58
rclcpp/include/rclcpp/event.hpp
Normal file
@@ -0,0 +1,58 @@
|
||||
// Copyright 2016 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__EVENT_HPP_
|
||||
#define RCLCPP__EVENT_HPP_
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace event
|
||||
{
|
||||
|
||||
class Event
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Event)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
Event();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
set();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
check();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
check_and_clear();
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Event)
|
||||
|
||||
std::atomic_bool state_;
|
||||
};
|
||||
|
||||
} // namespace event
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EVENT_HPP_
|
||||
187
rclcpp/include/rclcpp/exceptions.hpp
Normal file
187
rclcpp/include/rclcpp/exceptions.hpp
Normal file
@@ -0,0 +1,187 @@
|
||||
// Copyright 2016 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__EXCEPTIONS_HPP_
|
||||
#define RCLCPP__EXCEPTIONS_HPP_
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/types.h"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace exceptions
|
||||
{
|
||||
|
||||
/// Thrown when a method is trying to use a node, but it is invalid.
|
||||
class InvalidNodeError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
InvalidNodeError()
|
||||
: std::runtime_error("node is invalid") {}
|
||||
};
|
||||
|
||||
/// Thrown when a any kind of name (node, namespace, topic, etc.) is invalid.
|
||||
class NameValidationError : public std::invalid_argument
|
||||
{
|
||||
public:
|
||||
NameValidationError(
|
||||
const char * name_type_,
|
||||
const char * name_,
|
||||
const char * error_msg_,
|
||||
size_t invalid_index_)
|
||||
: std::invalid_argument(format_error(name_type_, name_, error_msg_, invalid_index_)),
|
||||
name_type(name_type_), name(name_), error_msg(error_msg_), invalid_index(invalid_index_)
|
||||
{}
|
||||
|
||||
static std::string
|
||||
format_error(
|
||||
const char * name_type,
|
||||
const char * name,
|
||||
const char * error_msg,
|
||||
size_t invalid_index);
|
||||
|
||||
const std::string name_type;
|
||||
const std::string name;
|
||||
const std::string error_msg;
|
||||
const size_t invalid_index;
|
||||
};
|
||||
|
||||
/// Thrown when a node name is invalid.
|
||||
class InvalidNodeNameError : public NameValidationError
|
||||
{
|
||||
public:
|
||||
InvalidNodeNameError(const char * node_name, const char * error_msg, size_t invalid_index)
|
||||
: NameValidationError("node name", node_name, error_msg, invalid_index)
|
||||
{}
|
||||
};
|
||||
|
||||
/// Thrown when a node namespace is invalid.
|
||||
class InvalidNamespaceError : public NameValidationError
|
||||
{
|
||||
public:
|
||||
InvalidNamespaceError(const char * namespace_, const char * error_msg, size_t invalid_index)
|
||||
: NameValidationError("namespace", namespace_, error_msg, invalid_index)
|
||||
{}
|
||||
};
|
||||
|
||||
/// Thrown when a topic name is invalid.
|
||||
class InvalidTopicNameError : public NameValidationError
|
||||
{
|
||||
public:
|
||||
InvalidTopicNameError(const char * namespace_, const char * error_msg, size_t invalid_index)
|
||||
: NameValidationError("topic name", namespace_, error_msg, invalid_index)
|
||||
{}
|
||||
};
|
||||
|
||||
/// Thrown when a service name is invalid.
|
||||
class InvalidServiceNameError : public NameValidationError
|
||||
{
|
||||
public:
|
||||
InvalidServiceNameError(const char * namespace_, const char * error_msg, size_t invalid_index)
|
||||
: NameValidationError("service name", namespace_, error_msg, invalid_index)
|
||||
{}
|
||||
};
|
||||
|
||||
/// Throw a C++ std::exception which was created based on an rcl error.
|
||||
/**
|
||||
* Passing nullptr for reset_error is safe and will avoid calling any function
|
||||
* to reset the error.
|
||||
*
|
||||
* \param ret the return code for the current error state
|
||||
* \param prefix string to prefix to the error if applicable (not all errors have custom messages)
|
||||
* \param error_state error state to create exception from, if nullptr rcl_get_error_state is used
|
||||
* \param reset_error function to be called before throwing which whill clear the error state
|
||||
* \throws std::invalid_argument if ret is RCL_RET_OK
|
||||
* \throws std::runtime_error if the rcl_get_error_state returns 0
|
||||
* \throws RCLErrorBase some child class exception based on ret
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
throw_from_rcl_error(
|
||||
rcl_ret_t ret,
|
||||
const std::string & prefix = "",
|
||||
const rcl_error_state_t * error_state = nullptr,
|
||||
void (* reset_error)() = rcl_reset_error);
|
||||
|
||||
class RCLErrorBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
RCLErrorBase(rcl_ret_t ret, const rcl_error_state_t * error_state);
|
||||
virtual ~RCLErrorBase() {}
|
||||
|
||||
rcl_ret_t ret;
|
||||
std::string message;
|
||||
std::string file;
|
||||
size_t line;
|
||||
std::string formatted_message;
|
||||
};
|
||||
|
||||
/// Created when the return code does not match one of the other specialized exceptions.
|
||||
class RCLError : public RCLErrorBase, public std::runtime_error
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
RCLError(rcl_ret_t ret, const rcl_error_state_t * error_state, const std::string & prefix);
|
||||
RCLCPP_PUBLIC
|
||||
RCLError(const RCLErrorBase & base_exc, const std::string & prefix);
|
||||
};
|
||||
|
||||
/// Created when the ret is RCL_RET_BAD_ALLOC.
|
||||
class RCLBadAlloc : public RCLErrorBase, public std::bad_alloc
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
RCLBadAlloc(rcl_ret_t ret, const rcl_error_state_t * error_state);
|
||||
RCLCPP_PUBLIC
|
||||
explicit RCLBadAlloc(const RCLErrorBase & base_exc);
|
||||
};
|
||||
|
||||
/// Created when the ret is RCL_RET_INVALID_ARGUMENT.
|
||||
class RCLInvalidArgument : public RCLErrorBase, public std::invalid_argument
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
RCLInvalidArgument(
|
||||
rcl_ret_t ret,
|
||||
const rcl_error_state_t * error_state,
|
||||
const std::string & prefix);
|
||||
RCLCPP_PUBLIC
|
||||
RCLInvalidArgument(const RCLErrorBase & base_exc, const std::string & prefix);
|
||||
};
|
||||
|
||||
/// Thrown when an invalid rclcpp::Event object or SharedPtr is encountered.
|
||||
class InvalidEventError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
InvalidEventError()
|
||||
: std::runtime_error("event is invalid") {}
|
||||
};
|
||||
|
||||
/// Thrown when an unregistered rclcpp::Event is encountered where a registered one was expected.
|
||||
class EventNotRegisteredError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
EventNotRegisteredError()
|
||||
: std::runtime_error("event already registered") {}
|
||||
};
|
||||
|
||||
} // namespace exceptions
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXCEPTIONS_HPP_
|
||||
@@ -17,26 +17,69 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <list>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
|
||||
#include "rclcpp/any_executable.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/memory_strategies.hpp"
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
// Forward declaration is used in convenience method signature.
|
||||
namespace node
|
||||
{
|
||||
class Node;
|
||||
} // namespace node
|
||||
|
||||
namespace executor
|
||||
{
|
||||
|
||||
/// Return codes to be used with spin_until_future_complete.
|
||||
/**
|
||||
* SUCCESS: The future is complete and can be accessed with "get" without blocking.
|
||||
* INTERRUPTED: The future is not complete, spinning was interrupted by Ctrl-C or another error.
|
||||
* TIMEOUT: Spinning timed out.
|
||||
*/
|
||||
enum class FutureReturnCode {SUCCESS, INTERRUPTED, TIMEOUT};
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::ostream &
|
||||
operator<<(std::ostream & os, const FutureReturnCode & future_return_code);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
to_string(const FutureReturnCode & future_return_code);
|
||||
|
||||
///
|
||||
/**
|
||||
* Options to be passed to the executor constructor.
|
||||
*/
|
||||
struct ExecutorArgs
|
||||
{
|
||||
memory_strategy::MemoryStrategy::SharedPtr memory_strategy;
|
||||
size_t max_conditions = 0;
|
||||
};
|
||||
|
||||
static inline ExecutorArgs create_default_executor_arguments()
|
||||
{
|
||||
ExecutorArgs args;
|
||||
args.memory_strategy = memory_strategies::create_default_strategy();
|
||||
args.max_conditions = 0;
|
||||
return args;
|
||||
}
|
||||
|
||||
/// Coordinate the order and timing of available communication tasks.
|
||||
/**
|
||||
* Executor provides spin functions (including spin_node_once and spin_some).
|
||||
@@ -50,33 +93,21 @@ namespace executor
|
||||
class Executor
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Executor);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Executor)
|
||||
|
||||
/// Default constructor.
|
||||
// \param[in] ms The memory strategy to be used with this executor.
|
||||
explicit Executor(memory_strategy::MemoryStrategy::SharedPtr ms =
|
||||
memory_strategies::create_default_strategy())
|
||||
: interrupt_guard_condition_(rmw_create_guard_condition()),
|
||||
memory_strategy_(ms)
|
||||
{
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
explicit Executor(const ExecutorArgs & args = create_default_executor_arguments());
|
||||
|
||||
/// Default destructor.
|
||||
virtual ~Executor()
|
||||
{
|
||||
// Try to deallocate the interrupt guard condition.
|
||||
if (interrupt_guard_condition_ != nullptr) {
|
||||
rmw_ret_t status = rmw_destroy_guard_condition(interrupt_guard_condition_);
|
||||
if (status != RMW_RET_OK) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] failed to destroy guard condition: %s\n", rmw_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~Executor();
|
||||
|
||||
/// Do work periodically as it becomes available to us. Blocking call, may block indefinitely.
|
||||
// It is up to the implementation of Executor to implement spin.
|
||||
virtual void spin() = 0;
|
||||
virtual void
|
||||
spin() = 0;
|
||||
|
||||
/// Add a node to the executor.
|
||||
/**
|
||||
@@ -86,26 +117,14 @@ public:
|
||||
* the executor is blocked at the rmw layer while waiting for work and it is notified that a new
|
||||
* node was added, it will wake up.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
add_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify = true)
|
||||
{
|
||||
// Check to ensure node not already added
|
||||
for (auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (node == node_ptr) {
|
||||
// TODO(jacquelinekay): Use a different error here?
|
||||
throw std::runtime_error("Cannot add node to executor, node already added.");
|
||||
}
|
||||
}
|
||||
weak_nodes_.push_back(node_ptr);
|
||||
if (notify) {
|
||||
// Interrupt waiting to handle new node
|
||||
rmw_ret_t status = rmw_trigger_guard_condition(interrupt_guard_condition_);
|
||||
if (status != RMW_RET_OK) {
|
||||
throw std::runtime_error(rmw_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
}
|
||||
add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify = true);
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
add_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify = true);
|
||||
|
||||
/// Remove a node from the executor.
|
||||
/**
|
||||
@@ -114,29 +133,14 @@ public:
|
||||
* This is useful if the last node was removed from the executor while the executor was blocked
|
||||
* waiting for work in another thread, because otherwise the executor would never be notified.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
remove_node(rclcpp::node::Node::SharedPtr node_ptr, bool notify = true)
|
||||
{
|
||||
bool node_removed = false;
|
||||
weak_nodes_.erase(
|
||||
std::remove_if(
|
||||
weak_nodes_.begin(), weak_nodes_.end(),
|
||||
[&](std::weak_ptr<rclcpp::node::Node> & i)
|
||||
{
|
||||
bool matched = (i.lock() == node_ptr);
|
||||
node_removed |= matched;
|
||||
return matched;
|
||||
}));
|
||||
if (notify) {
|
||||
// If the node was matched and removed, interrupt waiting
|
||||
if (node_removed) {
|
||||
rmw_ret_t status = rmw_trigger_guard_condition(interrupt_guard_condition_);
|
||||
if (status != RMW_RET_OK) {
|
||||
throw std::runtime_error(rmw_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify = true);
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
remove_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify = true);
|
||||
|
||||
/// Add a node to executor, execute the next available unit of work, and remove the node.
|
||||
/**
|
||||
@@ -146,28 +150,40 @@ public:
|
||||
* function to be non-blocking.
|
||||
*/
|
||||
template<typename T = std::milli>
|
||||
void spin_node_once(rclcpp::node::Node::SharedPtr node,
|
||||
void
|
||||
spin_node_once(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
|
||||
std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t, T>(-1))
|
||||
{
|
||||
this->add_node(node, false);
|
||||
// non-blocking = true
|
||||
auto any_exec = get_next_executable(timeout);
|
||||
if (any_exec) {
|
||||
execute_any_executable(any_exec);
|
||||
}
|
||||
this->remove_node(node, false);
|
||||
return spin_node_once_nanoseconds(
|
||||
node,
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
template<typename NodeT = rclcpp::node::Node, typename T = std::milli>
|
||||
void
|
||||
spin_node_once(std::shared_ptr<NodeT> node,
|
||||
std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t, T>(-1))
|
||||
{
|
||||
return spin_node_once_nanoseconds(
|
||||
node->get_node_base_interface(),
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
);
|
||||
}
|
||||
|
||||
/// Add a node, complete all immediately available work, and remove the node.
|
||||
/**
|
||||
* \param[in] node Shared pointer to the node to add.
|
||||
*/
|
||||
void spin_node_some(rclcpp::node::Node::SharedPtr node)
|
||||
{
|
||||
this->add_node(node, false);
|
||||
spin_some();
|
||||
this->remove_node(node, false);
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_node_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node);
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_node_some(std::shared_ptr<rclcpp::node::Node> node);
|
||||
|
||||
/// Complete all available queued work without blocking.
|
||||
/**
|
||||
@@ -176,438 +192,166 @@ public:
|
||||
* Adding subscriptions, timers, services, etc. with blocking callbacks will cause this function
|
||||
* to block (which may have unintended consequences).
|
||||
*/
|
||||
virtual void spin_some()
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
spin_some();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
spin_once(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
|
||||
|
||||
/// Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
|
||||
/**
|
||||
* \param[in] future The future to wait on. If SUCCESS, the future is safe to access after this
|
||||
* function.
|
||||
* \param[in] timeout Optional timeout parameter, which gets passed to Executor::spin_node_once.
|
||||
* `-1` is block forever, `0` is non-blocking.
|
||||
* If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return
|
||||
* code.
|
||||
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
|
||||
*/
|
||||
template<typename ResponseT, typename TimeT = std::milli>
|
||||
FutureReturnCode
|
||||
spin_until_future_complete(
|
||||
std::shared_future<ResponseT> & future,
|
||||
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
|
||||
{
|
||||
while (AnyExecutable::SharedPtr any_exec =
|
||||
get_next_executable(std::chrono::milliseconds::zero()))
|
||||
{
|
||||
execute_any_executable(any_exec);
|
||||
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
|
||||
// inside a callback executed by an executor.
|
||||
|
||||
// Check the future before entering the while loop.
|
||||
// If the future is already complete, don't try to spin.
|
||||
std::future_status status = future.wait_for(std::chrono::seconds(0));
|
||||
if (status == std::future_status::ready) {
|
||||
return FutureReturnCode::SUCCESS;
|
||||
}
|
||||
|
||||
auto end_time = std::chrono::steady_clock::now();
|
||||
std::chrono::nanoseconds timeout_ns = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
timeout);
|
||||
if (timeout_ns > std::chrono::nanoseconds::zero()) {
|
||||
end_time += timeout_ns;
|
||||
}
|
||||
std::chrono::nanoseconds timeout_left = timeout_ns;
|
||||
|
||||
while (rclcpp::utilities::ok()) {
|
||||
// Do one item of work.
|
||||
spin_once(timeout_left);
|
||||
// Check if the future is set, return SUCCESS if it is.
|
||||
status = future.wait_for(std::chrono::seconds(0));
|
||||
if (status == std::future_status::ready) {
|
||||
return FutureReturnCode::SUCCESS;
|
||||
}
|
||||
// If the original timeout is < 0, then this is blocking, never TIMEOUT.
|
||||
if (timeout_ns < std::chrono::nanoseconds::zero()) {
|
||||
continue;
|
||||
}
|
||||
// Otherwise check if we still have time to wait, return TIMEOUT if not.
|
||||
auto now = std::chrono::steady_clock::now();
|
||||
if (now >= end_time) {
|
||||
return FutureReturnCode::TIMEOUT;
|
||||
}
|
||||
// Subtract the elapsed time from the original timeout.
|
||||
timeout_left = std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - now);
|
||||
}
|
||||
|
||||
// The future did not complete before ok() returned false, return INTERRUPTED.
|
||||
return FutureReturnCode::INTERRUPTED;
|
||||
}
|
||||
|
||||
/// Cancel any running spin* function, causing it to return.
|
||||
/* This function can be called asynchonously from any thread. */
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
cancel();
|
||||
|
||||
/// Support dynamic switching of the memory strategy.
|
||||
/**
|
||||
* Switching the memory strategy while the executor is spinning in another threading could have
|
||||
* unintended consequences.
|
||||
* \param[in] memory_strategy Shared pointer to the memory strategy to set.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
set_memory_strategy(memory_strategy::MemoryStrategy::SharedPtr memory_strategy)
|
||||
{
|
||||
if (memory_strategy == nullptr) {
|
||||
throw std::runtime_error("Received NULL memory strategy in executor.");
|
||||
}
|
||||
memory_strategy_ = memory_strategy;
|
||||
}
|
||||
set_memory_strategy(memory_strategy::MemoryStrategy::SharedPtr memory_strategy);
|
||||
|
||||
protected:
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_node_once_nanoseconds(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
/// Find the next available executable and do the work associated with it.
|
||||
/** \param[in] any_exec Union structure that can hold any executable type (timer, subscription,
|
||||
* service, client).
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
execute_any_executable(AnyExecutable::SharedPtr any_exec)
|
||||
{
|
||||
if (!any_exec) {
|
||||
return;
|
||||
}
|
||||
if (any_exec->timer) {
|
||||
execute_timer(any_exec->timer);
|
||||
}
|
||||
if (any_exec->subscription) {
|
||||
execute_subscription(any_exec->subscription);
|
||||
}
|
||||
if (any_exec->subscription_intra_process) {
|
||||
execute_intra_process_subscription(any_exec->subscription_intra_process);
|
||||
}
|
||||
if (any_exec->service) {
|
||||
execute_service(any_exec->service);
|
||||
}
|
||||
if (any_exec->client) {
|
||||
execute_client(any_exec->client);
|
||||
}
|
||||
// Reset the callback_group, regardless of type
|
||||
any_exec->callback_group->can_be_taken_from().store(true);
|
||||
// Wake the wait, because it may need to be recalculated or work that
|
||||
// was previously blocked is now available.
|
||||
rmw_ret_t status = rmw_trigger_guard_condition(interrupt_guard_condition_);
|
||||
if (status != RMW_RET_OK) {
|
||||
throw std::runtime_error(rmw_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
execute_any_executable(AnyExecutable::SharedPtr any_exec);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
static void
|
||||
execute_subscription(
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr subscription)
|
||||
{
|
||||
std::shared_ptr<void> message = subscription->create_message();
|
||||
bool taken = false;
|
||||
rmw_message_info_t message_info;
|
||||
auto ret =
|
||||
rmw_take_with_info(subscription->get_subscription_handle(),
|
||||
message.get(), &taken, &message_info);
|
||||
if (ret == RMW_RET_OK) {
|
||||
if (taken) {
|
||||
message_info.from_intra_process = false;
|
||||
subscription->handle_message(message, message_info);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] take failed for subscription on topic '%s': %s\n",
|
||||
subscription->get_topic_name().c_str(), rmw_get_error_string_safe());
|
||||
}
|
||||
subscription->return_message(message);
|
||||
}
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr subscription);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
static void
|
||||
execute_intra_process_subscription(
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr subscription)
|
||||
{
|
||||
rcl_interfaces::msg::IntraProcessMessage ipm;
|
||||
bool taken = false;
|
||||
rmw_message_info_t message_info;
|
||||
rmw_ret_t status = rmw_take_with_info(
|
||||
subscription->get_intra_process_subscription_handle(),
|
||||
&ipm,
|
||||
&taken,
|
||||
&message_info);
|
||||
if (status == RMW_RET_OK) {
|
||||
if (taken) {
|
||||
message_info.from_intra_process = true;
|
||||
subscription->handle_intra_process_message(ipm, message_info);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] take failed for intra process subscription on topic '%s': %s\n",
|
||||
subscription->get_topic_name().c_str(), rmw_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr subscription);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
static void
|
||||
execute_timer(
|
||||
rclcpp::timer::TimerBase::SharedPtr timer)
|
||||
{
|
||||
timer->execute_callback();
|
||||
}
|
||||
execute_timer(rclcpp::timer::TimerBase::SharedPtr timer);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
static void
|
||||
execute_service(
|
||||
rclcpp::service::ServiceBase::SharedPtr service)
|
||||
{
|
||||
std::shared_ptr<void> request_header = service->create_request_header();
|
||||
std::shared_ptr<void> request = service->create_request();
|
||||
bool taken = false;
|
||||
rmw_ret_t status = rmw_take_request(
|
||||
service->get_service_handle(),
|
||||
request_header.get(),
|
||||
request.get(),
|
||||
&taken);
|
||||
if (status == RMW_RET_OK) {
|
||||
if (taken) {
|
||||
service->handle_request(request_header, request);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] take request failed for server of service '%s': %s\n",
|
||||
service->get_service_name().c_str(), rmw_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
execute_service(rclcpp::service::ServiceBase::SharedPtr service);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
static void
|
||||
execute_client(
|
||||
rclcpp::client::ClientBase::SharedPtr client)
|
||||
{
|
||||
std::shared_ptr<void> request_header = client->create_request_header();
|
||||
std::shared_ptr<void> response = client->create_response();
|
||||
bool taken = false;
|
||||
rmw_ret_t status = rmw_take_response(
|
||||
client->get_client_handle(),
|
||||
request_header.get(),
|
||||
response.get(),
|
||||
&taken);
|
||||
if (status == RMW_RET_OK) {
|
||||
if (taken) {
|
||||
client->handle_response(request_header, response);
|
||||
}
|
||||
} else {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] take response failed for client of service '%s': %s\n",
|
||||
client->get_service_name().c_str(), rmw_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
execute_client(rclcpp::client::ClientBase::SharedPtr client);
|
||||
|
||||
/*** Populate class storage from stored weak node pointers and wait. ***/
|
||||
|
||||
template<typename T = std::milli>
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
wait_for_work(std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t, T>(-1))
|
||||
{
|
||||
memory_strategy_->clear_active_entities();
|
||||
wait_for_work(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
|
||||
|
||||
// Collect the subscriptions and timers to be waited on
|
||||
bool has_invalid_weak_nodes = memory_strategy_->collect_entities(weak_nodes_);
|
||||
|
||||
// Clean up any invalid nodes, if they were detected
|
||||
if (has_invalid_weak_nodes) {
|
||||
weak_nodes_.erase(
|
||||
remove_if(weak_nodes_.begin(), weak_nodes_.end(),
|
||||
[](std::weak_ptr<rclcpp::node::Node> i)
|
||||
{
|
||||
return i.expired();
|
||||
}));
|
||||
}
|
||||
|
||||
// Use the number of subscriptions to allocate memory in the handles
|
||||
rmw_subscriptions_t subscriber_handles;
|
||||
subscriber_handles.subscriber_count =
|
||||
memory_strategy_->fill_subscriber_handles(subscriber_handles.subscribers);
|
||||
|
||||
rmw_services_t service_handles;
|
||||
service_handles.service_count =
|
||||
memory_strategy_->fill_service_handles(service_handles.services);
|
||||
|
||||
rmw_clients_t client_handles;
|
||||
client_handles.client_count =
|
||||
memory_strategy_->fill_client_handles(client_handles.clients);
|
||||
|
||||
// The number of guard conditions is fixed at 2: 1 for the ctrl-c guard cond,
|
||||
// and one for the executor's guard cond (interrupt_guard_condition_)
|
||||
size_t number_of_guard_conds = 2;
|
||||
rmw_guard_conditions_t guard_condition_handles;
|
||||
guard_condition_handles.guard_condition_count = number_of_guard_conds;
|
||||
guard_condition_handles.guard_conditions = static_cast<void **>(guard_cond_handles_.data());
|
||||
if (guard_condition_handles.guard_conditions == NULL &&
|
||||
number_of_guard_conds > 0)
|
||||
{
|
||||
// TODO(wjwwood): Use a different error here? maybe std::bad_alloc?
|
||||
throw std::runtime_error("Could not malloc for guard condition pointers.");
|
||||
}
|
||||
// Put the global ctrl-c guard condition in
|
||||
assert(guard_condition_handles.guard_condition_count > 1);
|
||||
guard_condition_handles.guard_conditions[0] = \
|
||||
rclcpp::utilities::get_global_sigint_guard_condition()->data;
|
||||
// Put the executor's guard condition in
|
||||
guard_condition_handles.guard_conditions[1] = \
|
||||
interrupt_guard_condition_->data;
|
||||
|
||||
rmw_time_t * wait_timeout = NULL;
|
||||
rmw_time_t rmw_timeout;
|
||||
|
||||
auto next_timer_duration = get_earliest_timer();
|
||||
// If the next timer timeout must preempt the requested timeout
|
||||
// or if the requested timeout blocks forever, and there exists a valid timer,
|
||||
// replace the requested timeout with the next timeout.
|
||||
bool has_valid_timer = next_timer_duration >= std::chrono::nanoseconds::zero();
|
||||
if ((next_timer_duration < timeout ||
|
||||
timeout < std::chrono::duration<int64_t, T>::zero()) && has_valid_timer)
|
||||
{
|
||||
rmw_timeout.sec =
|
||||
std::chrono::duration_cast<std::chrono::seconds>(next_timer_duration).count();
|
||||
rmw_timeout.nsec = next_timer_duration.count() % (1000 * 1000 * 1000);
|
||||
wait_timeout = &rmw_timeout;
|
||||
} else if (timeout >= std::chrono::duration<int64_t, T>::zero()) {
|
||||
// Convert timeout representation to rmw_time
|
||||
rmw_timeout.sec = std::chrono::duration_cast<std::chrono::seconds>(timeout).count();
|
||||
rmw_timeout.nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count() %
|
||||
(1000 * 1000 * 1000);
|
||||
wait_timeout = &rmw_timeout;
|
||||
}
|
||||
|
||||
// Now wait on the waitable subscriptions and timers
|
||||
rmw_ret_t status = rmw_wait(
|
||||
&subscriber_handles,
|
||||
&guard_condition_handles,
|
||||
&service_handles,
|
||||
&client_handles,
|
||||
wait_timeout);
|
||||
if (status != RMW_RET_OK && status != RMW_RET_TIMEOUT) {
|
||||
throw std::runtime_error(rmw_get_error_string_safe());
|
||||
}
|
||||
// If ctrl-c guard condition, return directly
|
||||
if (guard_condition_handles.guard_conditions[0] != 0) {
|
||||
// Make sure to free or clean memory
|
||||
memory_strategy_->clear_handles();
|
||||
return;
|
||||
}
|
||||
|
||||
memory_strategy_->revalidate_handles();
|
||||
}
|
||||
|
||||
/******************************/
|
||||
rclcpp::node::Node::SharedPtr
|
||||
get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
if (!group) {
|
||||
return rclcpp::node::Node::SharedPtr();
|
||||
}
|
||||
for (auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto callback_group = weak_group.lock();
|
||||
if (callback_group == group) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return rclcpp::node::Node::SharedPtr();
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
get_group_by_timer(
|
||||
rclcpp::timer::TimerBase::SharedPtr timer)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_timer : group->get_timer_ptrs()) {
|
||||
auto t = weak_timer.lock();
|
||||
if (t == timer) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rclcpp::callback_group::CallbackGroup::SharedPtr();
|
||||
}
|
||||
get_group_by_timer(rclcpp::timer::TimerBase::SharedPtr timer);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
get_next_timer(AnyExecutable::SharedPtr any_exec)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group || !group->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
for (auto & timer_ref : group->get_timer_ptrs()) {
|
||||
auto timer = timer_ref.lock();
|
||||
if (timer && timer->check_and_trigger()) {
|
||||
any_exec->timer = timer;
|
||||
any_exec->callback_group = group;
|
||||
node = get_node_by_group(group);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds
|
||||
get_earliest_timer()
|
||||
{
|
||||
std::chrono::nanoseconds latest = std::chrono::nanoseconds::max();
|
||||
bool timers_empty = true;
|
||||
for (auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group || !group->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
for (auto & timer_ref : group->get_timer_ptrs()) {
|
||||
timers_empty = false;
|
||||
// Check the expected trigger time
|
||||
auto timer = timer_ref.lock();
|
||||
if (timer && timer->time_until_trigger() < latest) {
|
||||
latest = timer->time_until_trigger();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timers_empty) {
|
||||
return std::chrono::nanoseconds(-1);
|
||||
}
|
||||
return latest;
|
||||
}
|
||||
get_next_timer(AnyExecutable::SharedPtr any_exec);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
AnyExecutable::SharedPtr
|
||||
get_next_ready_executable()
|
||||
{
|
||||
auto any_exec = memory_strategy_->instantiate_next_executable();
|
||||
// Check the timers to see if there are any that are ready, if so return
|
||||
get_next_timer(any_exec);
|
||||
if (any_exec->timer) {
|
||||
return any_exec;
|
||||
}
|
||||
// Check the subscriptions to see if there are any that are ready
|
||||
memory_strategy_->get_next_subscription(any_exec, weak_nodes_);
|
||||
if (any_exec->subscription || any_exec->subscription_intra_process) {
|
||||
return any_exec;
|
||||
}
|
||||
// Check the services to see if there are any that are ready
|
||||
memory_strategy_->get_next_service(any_exec, weak_nodes_);
|
||||
if (any_exec->service) {
|
||||
return any_exec;
|
||||
}
|
||||
// Check the clients to see if there are any that are ready
|
||||
memory_strategy_->get_next_client(any_exec, weak_nodes_);
|
||||
if (any_exec->client) {
|
||||
return any_exec;
|
||||
}
|
||||
// If there is no ready executable, return a null ptr
|
||||
return nullptr;
|
||||
}
|
||||
get_next_ready_executable();
|
||||
|
||||
template<typename T = std::milli>
|
||||
RCLCPP_PUBLIC
|
||||
AnyExecutable::SharedPtr
|
||||
get_next_executable(std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t,
|
||||
T>(-1))
|
||||
{
|
||||
// Check to see if there are any subscriptions or timers needing service
|
||||
// TODO(wjwwood): improve run to run efficiency of this function
|
||||
auto any_exec = get_next_ready_executable();
|
||||
// If there are none
|
||||
if (!any_exec) {
|
||||
// Wait for subscriptions or timers to work on
|
||||
wait_for_work(timeout);
|
||||
// Try again
|
||||
any_exec = get_next_ready_executable();
|
||||
}
|
||||
// At this point any_exec should be valid with either a valid subscription
|
||||
// or a valid timer, or it should be a null shared_ptr
|
||||
if (any_exec) {
|
||||
// If it is valid, check to see if the group is mutually exclusive or
|
||||
// not, then mark it accordingly
|
||||
if (any_exec->callback_group && any_exec->callback_group->type() == \
|
||||
callback_group::CallbackGroupType::MutuallyExclusive)
|
||||
{
|
||||
// It should not have been taken otherwise
|
||||
assert(any_exec->callback_group->can_be_taken_from().load());
|
||||
// Set to false to indicate something is being run from this group
|
||||
any_exec->callback_group->can_be_taken_from().store(false);
|
||||
}
|
||||
}
|
||||
return any_exec;
|
||||
}
|
||||
get_next_executable(std::chrono::nanoseconds timeout = std::chrono::nanoseconds(-1));
|
||||
|
||||
/// Spinning state, used to prevent multi threaded calls to spin and to cancel blocking spins.
|
||||
std::atomic_bool spinning;
|
||||
|
||||
/// Guard condition for signaling the rmw layer to wake up for special events.
|
||||
rmw_guard_condition_t * interrupt_guard_condition_;
|
||||
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
|
||||
|
||||
/// Waitset for managing entities that the rmw layer waits on.
|
||||
rcl_wait_set_t waitset_ = rcl_get_zero_initialized_wait_set();
|
||||
|
||||
/// The memory strategy: an interface for handling user-defined memory allocation strategies.
|
||||
memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Executor);
|
||||
RCLCPP_DISABLE_COPY(Executor)
|
||||
|
||||
std::vector<std::weak_ptr<rclcpp::node::Node>> weak_nodes_;
|
||||
std::array<void *, 2> guard_cond_handles_;
|
||||
std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr> weak_nodes_;
|
||||
};
|
||||
|
||||
} // namespace executor
|
||||
|
||||
@@ -12,76 +12,113 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_EXECUTORS_HPP_
|
||||
#define RCLCPP_RCLCPP_EXECUTORS_HPP_
|
||||
#ifndef RCLCPP__EXECUTORS_HPP_
|
||||
#define RCLCPP__EXECUTORS_HPP_
|
||||
|
||||
#include <future>
|
||||
#include <rclcpp/executors/multi_threaded_executor.hpp>
|
||||
#include <rclcpp/executors/single_threaded_executor.hpp>
|
||||
#include <rclcpp/node.hpp>
|
||||
#include <rclcpp/utilities.hpp>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/executors/multi_threaded_executor.hpp"
|
||||
#include "rclcpp/executors/single_threaded_executor.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Create a default single-threaded executor and execute any immediately available work.
|
||||
/** \param[in] node_ptr Shared pointer to the node to spin. */
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin_some(rclcpp::node::Node::SharedPtr node_ptr);
|
||||
|
||||
/// Create a default single-threaded executor and spin the specified node.
|
||||
/** \param[in] node_ptr Shared pointer to the node to spin. */
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin(rclcpp::node::Node::SharedPtr node_ptr);
|
||||
|
||||
namespace executors
|
||||
{
|
||||
|
||||
using rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor;
|
||||
using rclcpp::executors::single_threaded_executor::SingleThreadedExecutor;
|
||||
|
||||
/// Return codes to be used with spin_until_future_complete.
|
||||
/**
|
||||
* SUCCESS: The future is complete and can be accessed with "get" without blocking.
|
||||
* INTERRUPTED: The future is not complete, spinning was interrupted by Ctrl-C or another error.
|
||||
* TIMEOUT: Spinning timed out.
|
||||
*/
|
||||
enum FutureReturnCode {SUCCESS, INTERRUPTED, TIMEOUT};
|
||||
|
||||
/// Spin (blocking) until the future is complete, until the function times out (if applicable),
|
||||
/// or until rclcpp is interrupted.
|
||||
/// Spin (blocking) until the future is complete, it times out waiting, or rclcpp is interrupted.
|
||||
/**
|
||||
* \param[in] executor The executor which will spin the node.
|
||||
* \param[in] node_ptr The node to spin.
|
||||
* \param[in] future The future to wait on. If SUCCESS, the future is safe to access after this function
|
||||
* \param[in] timeout Optional timeout parameter, which gets passed to Executor::spin_node_once.
|
||||
-1 is block forever, 0 is non-blocking.
|
||||
If the time spent inside the blocking loop exceeds this timeout, return a TIMEOUT return code.
|
||||
* \return The return code, one of SUCCESS, INTERRUPTED, or TIMEOUT.
|
||||
* \param[in] future The future to wait on. If `SUCCESS`, the future is safe to
|
||||
* access after this function
|
||||
* \param[in] timeout Optional timeout parameter, which gets passed to
|
||||
* Executor::spin_node_once.
|
||||
* `-1` is block forever, `0` is non-blocking.
|
||||
* If the time spent inside the blocking loop exceeds this timeout, return a `TIMEOUT` return code.
|
||||
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
|
||||
*/
|
||||
template<typename ResponseT, typename TimeT = std::milli>
|
||||
FutureReturnCode
|
||||
rclcpp::executor::FutureReturnCode
|
||||
spin_node_until_future_complete(
|
||||
rclcpp::executor::Executor & executor, rclcpp::node::Node::SharedPtr node_ptr,
|
||||
rclcpp::executor::Executor & executor,
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
std::shared_future<ResponseT> & future,
|
||||
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
|
||||
{
|
||||
// TODO(wjwwood): does not work recursively right, can't call spin_node_until_future_complete
|
||||
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
|
||||
// inside a callback executed by an executor.
|
||||
|
||||
// Check the future before entering the while loop.
|
||||
// If the future is already complete, don't try to spin.
|
||||
std::future_status status = future.wait_for(std::chrono::seconds(0));
|
||||
|
||||
auto start_time = std::chrono::system_clock::now();
|
||||
|
||||
while (status != std::future_status::ready && rclcpp::utilities::ok()) {
|
||||
executor.spin_node_once(node_ptr, timeout);
|
||||
if (timeout.count() >= 0) {
|
||||
if (start_time + timeout < std::chrono::system_clock::now()) {
|
||||
return TIMEOUT;
|
||||
}
|
||||
}
|
||||
status = future.wait_for(std::chrono::seconds(0));
|
||||
}
|
||||
|
||||
// If the future completed, and we weren't interrupted by ctrl-C, return the response
|
||||
if (status == std::future_status::ready) {
|
||||
return FutureReturnCode::SUCCESS;
|
||||
}
|
||||
return FutureReturnCode::INTERRUPTED;
|
||||
executor.add_node(node_ptr);
|
||||
auto retcode = executor.spin_until_future_complete(future, timeout);
|
||||
executor.remove_node(node_ptr);
|
||||
return retcode;
|
||||
}
|
||||
|
||||
} // namespace executors
|
||||
} // namespace rclcpp
|
||||
template<typename NodeT = rclcpp::node::Node, typename ResponseT, typename TimeT = std::milli>
|
||||
rclcpp::executor::FutureReturnCode
|
||||
spin_node_until_future_complete(
|
||||
rclcpp::executor::Executor & executor,
|
||||
std::shared_ptr<NodeT> node_ptr,
|
||||
std::shared_future<ResponseT> & future,
|
||||
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
|
||||
{
|
||||
return rclcpp::executors::spin_node_until_future_complete(
|
||||
executor,
|
||||
node_ptr->get_node_base_interface(),
|
||||
future,
|
||||
timeout);
|
||||
}
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_EXECUTORS_HPP_ */
|
||||
} // namespace executors
|
||||
|
||||
template<typename FutureT, typename TimeT = std::milli>
|
||||
rclcpp::executor::FutureReturnCode
|
||||
spin_until_future_complete(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr,
|
||||
std::shared_future<FutureT> & future,
|
||||
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
return executors::spin_node_until_future_complete<FutureT>(executor, node_ptr, future, timeout);
|
||||
}
|
||||
|
||||
template<typename NodeT = rclcpp::node::Node, typename FutureT, typename TimeT = std::milli>
|
||||
rclcpp::executor::FutureReturnCode
|
||||
spin_until_future_complete(
|
||||
std::shared_ptr<NodeT> node_ptr,
|
||||
std::shared_future<FutureT> & future,
|
||||
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
|
||||
{
|
||||
return rclcpp::spin_until_future_complete(node_ptr->get_node_base_interface(), future, timeout);
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__EXECUTORS_HPP_
|
||||
|
||||
@@ -15,18 +15,14 @@
|
||||
#ifndef RCLCPP__EXECUTORS__MULTI_THREADED_EXECUTOR_HPP_
|
||||
#define RCLCPP__EXECUTORS__MULTI_THREADED_EXECUTOR_HPP_
|
||||
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
#include <thread>
|
||||
#include <unordered_map>
|
||||
|
||||
#include "rclcpp/executor.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/memory_strategies.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -38,62 +34,30 @@ namespace multi_threaded_executor
|
||||
class MultiThreadedExecutor : public executor::Executor
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(MultiThreadedExecutor);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(MultiThreadedExecutor)
|
||||
|
||||
MultiThreadedExecutor(memory_strategy::MemoryStrategy::SharedPtr ms =
|
||||
memory_strategies::create_default_strategy())
|
||||
: executor::Executor(ms)
|
||||
{
|
||||
number_of_threads_ = std::thread::hardware_concurrency();
|
||||
if (number_of_threads_ == 0) {
|
||||
number_of_threads_ = 1;
|
||||
}
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
MultiThreadedExecutor(
|
||||
const executor::ExecutorArgs & args = rclcpp::executor::create_default_executor_arguments());
|
||||
|
||||
virtual ~MultiThreadedExecutor() {}
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~MultiThreadedExecutor();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin()
|
||||
{
|
||||
std::vector<std::thread> threads;
|
||||
{
|
||||
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
|
||||
size_t thread_id_ = 1; // Use a _ suffix to avoid shadowing `rclcpp::thread_id`
|
||||
for (size_t i = number_of_threads_; i > 0; --i) {
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(100));
|
||||
auto func = std::bind(&MultiThreadedExecutor::run, this, thread_id_++);
|
||||
threads.emplace_back(func);
|
||||
}
|
||||
}
|
||||
for (auto & thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
spin();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_number_of_threads()
|
||||
{
|
||||
return number_of_threads_;
|
||||
}
|
||||
get_number_of_threads();
|
||||
|
||||
protected:
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
run(size_t this_thread_number);
|
||||
|
||||
private:
|
||||
void run(size_t this_thread_id)
|
||||
{
|
||||
rclcpp::thread_id = this_thread_id;
|
||||
while (rclcpp::utilities::ok()) {
|
||||
executor::AnyExecutable::SharedPtr any_exec;
|
||||
{
|
||||
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
|
||||
if (!rclcpp::utilities::ok()) {
|
||||
return;
|
||||
}
|
||||
any_exec = get_next_executable();
|
||||
}
|
||||
execute_any_executable(any_exec);
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_DISABLE_COPY(MultiThreadedExecutor);
|
||||
RCLCPP_DISABLE_COPY(MultiThreadedExecutor)
|
||||
|
||||
std::mutex wait_mutex_;
|
||||
size_t number_of_threads_;
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/rate.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -41,29 +42,26 @@ namespace single_threaded_executor
|
||||
class SingleThreadedExecutor : public executor::Executor
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SingleThreadedExecutor);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SingleThreadedExecutor)
|
||||
|
||||
/// Default constructor. See the default constructor for Executor.
|
||||
SingleThreadedExecutor(memory_strategy::MemoryStrategy::SharedPtr ms =
|
||||
memory_strategies::create_default_strategy())
|
||||
: executor::Executor(ms) {}
|
||||
RCLCPP_PUBLIC
|
||||
SingleThreadedExecutor(
|
||||
const executor::ExecutorArgs & args = rclcpp::executor::create_default_executor_arguments());
|
||||
|
||||
/// Default destrcutor.
|
||||
virtual ~SingleThreadedExecutor() {}
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~SingleThreadedExecutor();
|
||||
|
||||
/// Single-threaded implementation of spin.
|
||||
// This function will block until work comes in, execute it, and keep blocking.
|
||||
// It will only be interrupt by a CTRL-C (managed by the global signal handler).
|
||||
void spin()
|
||||
{
|
||||
while (rclcpp::utilities::ok()) {
|
||||
auto any_exec = get_next_executable();
|
||||
execute_any_executable(any_exec);
|
||||
}
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
spin();
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(SingleThreadedExecutor);
|
||||
RCLCPP_DISABLE_COPY(SingleThreadedExecutor)
|
||||
};
|
||||
|
||||
} // namespace single_threaded_executor
|
||||
|
||||
63
rclcpp/include/rclcpp/expand_topic_or_service_name.hpp
Normal file
63
rclcpp/include/rclcpp/expand_topic_or_service_name.hpp
Normal 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_
|
||||
@@ -12,13 +12,19 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_FUNCTION_TRAITS_HPP_
|
||||
#define RCLCPP_RCLCPP_FUNCTION_TRAITS_HPP_
|
||||
#ifndef RCLCPP__FUNCTION_TRAITS_HPP_
|
||||
#define RCLCPP__FUNCTION_TRAITS_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <tuple>
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace function_traits
|
||||
{
|
||||
|
||||
/* NOTE(esteve):
|
||||
* We support service callbacks that can optionally take the request id,
|
||||
* which should be possible with two overloaded create_service methods,
|
||||
@@ -49,6 +55,8 @@ struct function_traits
|
||||
|
||||
template<std::size_t N>
|
||||
using argument_type = typename std::tuple_element<N, arguments>::type;
|
||||
|
||||
using return_type = typename function_traits<decltype( & FunctionT::operator())>::return_type;
|
||||
};
|
||||
|
||||
// Free functions
|
||||
@@ -61,17 +69,57 @@ struct function_traits<ReturnTypeT(Args ...)>
|
||||
|
||||
template<std::size_t N>
|
||||
using argument_type = typename std::tuple_element<N, arguments>::type;
|
||||
|
||||
using return_type = ReturnTypeT;
|
||||
};
|
||||
|
||||
// Function pointers
|
||||
template<typename ReturnTypeT, typename ... Args>
|
||||
struct function_traits<ReturnTypeT (*)(Args ...)>: public function_traits<ReturnTypeT(Args ...)>
|
||||
struct function_traits<ReturnTypeT (*)(Args ...)>: function_traits<ReturnTypeT(Args ...)>
|
||||
{};
|
||||
|
||||
// std::bind for object methods
|
||||
template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ... FArgs>
|
||||
#if defined _LIBCPP_VERSION // libc++ (Clang)
|
||||
struct function_traits<std::__1::__bind<ReturnTypeT (ClassT::*)(Args ...), FArgs ...>>
|
||||
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
|
||||
struct function_traits<std::_Bind<std::_Mem_fn<ReturnTypeT (ClassT::*)(Args ...)>(FArgs ...)>>
|
||||
#elif defined _MSC_VER // MS Visual Studio
|
||||
struct function_traits<
|
||||
std::_Binder<std::_Unforced, ReturnTypeT(__cdecl ClassT::*)(Args ...), FArgs ...>
|
||||
>
|
||||
#else
|
||||
#error "Unsupported C++ compiler / standard library"
|
||||
#endif
|
||||
: function_traits<ReturnTypeT(Args ...)>
|
||||
{};
|
||||
|
||||
// std::bind for free functions
|
||||
template<typename ReturnTypeT, typename ... Args, typename ... FArgs>
|
||||
#if defined _LIBCPP_VERSION // libc++ (Clang)
|
||||
struct function_traits<std::__1::__bind<ReturnTypeT( &)(Args ...), FArgs ...>>
|
||||
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
|
||||
struct function_traits<std::_Bind<ReturnTypeT(*(FArgs ...))(Args ...)>>
|
||||
#elif defined _MSC_VER // MS Visual Studio
|
||||
struct function_traits<std::_Binder<std::_Unforced, ReturnTypeT(__cdecl &)(Args ...), FArgs ...>>
|
||||
#else
|
||||
#error "Unsupported C++ compiler / standard library"
|
||||
#endif
|
||||
: function_traits<ReturnTypeT(Args ...)>
|
||||
{};
|
||||
|
||||
// Lambdas
|
||||
template<typename ClassT, typename ReturnTypeT, typename ... Args>
|
||||
struct function_traits<ReturnTypeT (ClassT::*)(Args ...) const>
|
||||
: public function_traits<ReturnTypeT(ClassT &, Args ...)>
|
||||
: function_traits<ReturnTypeT(ClassT &, Args ...)>
|
||||
{};
|
||||
|
||||
template<typename FunctionT>
|
||||
struct function_traits<FunctionT &>: function_traits<FunctionT>
|
||||
{};
|
||||
|
||||
template<typename FunctionT>
|
||||
struct function_traits<FunctionT &&>: function_traits<FunctionT>
|
||||
{};
|
||||
|
||||
/* NOTE(esteve):
|
||||
@@ -95,6 +143,9 @@ struct same_arguments : std::is_same<
|
||||
typename function_traits<FunctorBT>::arguments
|
||||
>
|
||||
{};
|
||||
} /* namespace rclcpp */
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_FUNCTION_TRAITS_HPP_ */
|
||||
} // namespace function_traits
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__FUNCTION_TRAITS_HPP_
|
||||
|
||||
174
rclcpp/include/rclcpp/graph_listener.hpp
Normal file
174
rclcpp/include/rclcpp/graph_listener.hpp
Normal file
@@ -0,0 +1,174 @@
|
||||
// Copyright 2016 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__GRAPH_LISTENER_HPP_
|
||||
#define RCLCPP__GRAPH_LISTENER_HPP_
|
||||
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace graph_listener
|
||||
{
|
||||
|
||||
/// Thrown when a function is called on a GraphListener that is already shutdown.
|
||||
class GraphListenerShutdownError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
GraphListenerShutdownError()
|
||||
: std::runtime_error("GraphListener already shutdown") {}
|
||||
};
|
||||
|
||||
/// Thrown when a node has already been added to the GraphListener.
|
||||
class NodeAlreadyAddedError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
NodeAlreadyAddedError()
|
||||
: std::runtime_error("node already added") {}
|
||||
};
|
||||
|
||||
/// Thrown when the given node is not in the GraphListener.
|
||||
class NodeNotFoundError : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
NodeNotFoundError()
|
||||
: std::runtime_error("node not found") {}
|
||||
};
|
||||
|
||||
/// Notifies many nodes of graph changes by listening in a thread.
|
||||
class GraphListener : public std::enable_shared_from_this<GraphListener>
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
GraphListener();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~GraphListener();
|
||||
|
||||
/// Start the graph listener's listen thread if it hasn't been started.
|
||||
/**
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* \throws GraphListenerShutdownError if the GraphListener is shutdown
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
start_if_not_started();
|
||||
|
||||
/// Add a node to the graph listener's list of nodes.
|
||||
/**
|
||||
* \throws GraphListenerShutdownError if the GraphListener is shutdown
|
||||
* \throws NodeAlreadyAddedError if the given node is already in the list
|
||||
* \throws std::invalid_argument if node is nullptr
|
||||
* \throws std::system_error anything std::mutex::lock() throws
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
add_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
|
||||
|
||||
/// Return true if the given node is in the graph listener's list of nodes.
|
||||
/**
|
||||
* Also return false if given nullptr.
|
||||
*
|
||||
* \throws std::system_error anything std::mutex::lock() throws
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
has_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
|
||||
|
||||
/// Remove a node from the graph listener's list of nodes.
|
||||
/**
|
||||
*
|
||||
* \throws NodeNotFoundError if the given node is not in the list
|
||||
* \throws std::invalid_argument if node is nullptr
|
||||
* \throws std::system_error anything std::mutex::lock() throws
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph);
|
||||
|
||||
/// Stop the listening thread.
|
||||
/**
|
||||
* The thread cannot be restarted, and the class is defunct after calling.
|
||||
* This function is called by the ~GraphListener() and does nothing if
|
||||
* shutdown() was already called.
|
||||
* This function exists separately from the ~GraphListener() so that it can
|
||||
* be called before and exceptions can be caught.
|
||||
*
|
||||
* If start_if_not_started() was never called, this function still succeeds,
|
||||
* but start_if_not_started() still cannot be called after this function.
|
||||
*
|
||||
* \throws rclcpp::execptions::RCLError from rcl_guard_condition_fini()
|
||||
* \throws rclcpp::execptions::RCLError from rcl_wait_set_fini()
|
||||
* \throws std::system_error anything std::mutex::lock() throws
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
shutdown();
|
||||
|
||||
/// Return true if shutdown() has been called, else false.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
is_shutdown();
|
||||
|
||||
protected:
|
||||
/// Main function for the listening thread.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
run();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
run_loop();
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(GraphListener)
|
||||
|
||||
std::thread listener_thread_;
|
||||
bool is_started_;
|
||||
std::atomic_bool is_shutdown_;
|
||||
mutable std::mutex shutdown_mutex_;
|
||||
|
||||
mutable std::mutex node_graph_interfaces_barrier_mutex_;
|
||||
mutable std::mutex node_graph_interfaces_mutex_;
|
||||
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> node_graph_interfaces_;
|
||||
|
||||
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
|
||||
rcl_guard_condition_t * shutdown_guard_condition_;
|
||||
rcl_wait_set_t wait_set_ = rcl_get_zero_initialized_wait_set();
|
||||
};
|
||||
|
||||
} // namespace graph_listener
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__GRAPH_LISTENER_HPP_
|
||||
@@ -27,11 +27,12 @@
|
||||
#include <set>
|
||||
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/intra_process_manager_state.hpp"
|
||||
#include "rclcpp/intra_process_manager_impl.hpp"
|
||||
#include "rclcpp/mapped_ring_buffer.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/publisher.hpp"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -39,7 +40,8 @@ namespace intra_process_manager
|
||||
{
|
||||
|
||||
/// This class facilitates intra process communication between nodes.
|
||||
/* This class is used in the creation of publishers and subscriptions.
|
||||
/**
|
||||
* This class is used in the creation of publishers and subscriptions.
|
||||
* A singleton instance of this class is owned by a rclcpp::Context and a
|
||||
* rclcpp::Node can use an associated Context to get an instance of this class.
|
||||
* Nodes which do not have a common Context will not exchange intra process
|
||||
@@ -53,7 +55,7 @@ namespace intra_process_manager
|
||||
* When a publisher is created, it advertises on the topic the user provided,
|
||||
* as well as a "shadowing" topic of type rcl_interfaces/IntraProcessMessage.
|
||||
* For instance, if the user specified the topic '/namespace/chatter', then the
|
||||
* corresponding intra process topic might be '/namespace/chatter__intra'.
|
||||
* corresponding intra process topic might be '/namespace/chatter/_intra'.
|
||||
* The publisher is also allocated an id which is unique among all publishers
|
||||
* and subscriptions in this process.
|
||||
* Additionally, when registered with this class a ring buffer is created and
|
||||
@@ -119,20 +121,21 @@ namespace intra_process_manager
|
||||
class IntraProcessManager
|
||||
{
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(IntraProcessManager);
|
||||
RCLCPP_DISABLE_COPY(IntraProcessManager)
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(IntraProcessManager);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(IntraProcessManager)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit IntraProcessManager(
|
||||
IntraProcessManagerStateBase::SharedPtr state = create_default_state()
|
||||
)
|
||||
: state_(state)
|
||||
{
|
||||
}
|
||||
IntraProcessManagerImplBase::SharedPtr state = create_default_impl());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~IntraProcessManager();
|
||||
|
||||
/// Register a subscription with the manager, returns subscriptions unique id.
|
||||
/* In addition to generating a unique intra process id for the subscription,
|
||||
/**
|
||||
* In addition to generating a unique intra process id for the subscription,
|
||||
* this method also stores the topic name of the subscription.
|
||||
*
|
||||
* This method is normally called during the creation of a subscription,
|
||||
@@ -143,27 +146,23 @@ public:
|
||||
* \param subscription the Subscription to register.
|
||||
* \return an unsigned 64-bit integer which is the subscription's unique id.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
uint64_t
|
||||
add_subscription(subscription::SubscriptionBase::SharedPtr subscription)
|
||||
{
|
||||
auto id = IntraProcessManager::get_next_unique_id();
|
||||
state_->add_subscription(id, subscription);
|
||||
return id;
|
||||
}
|
||||
add_subscription(subscription::SubscriptionBase::SharedPtr subscription);
|
||||
|
||||
/// Unregister a subscription using the subscription's unique id.
|
||||
/* This method does not allocate memory.
|
||||
/**
|
||||
* This method does not allocate memory.
|
||||
*
|
||||
* \param intra_process_subscription_id id of the subscription to remove.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_subscription(uint64_t intra_process_subscription_id)
|
||||
{
|
||||
state_->remove_subscription(intra_process_subscription_id);
|
||||
}
|
||||
remove_subscription(uint64_t intra_process_subscription_id);
|
||||
|
||||
/// Register a publisher with the manager, returns the publisher unique id.
|
||||
/* In addition to generating and returning a unique id for the publisher,
|
||||
/**
|
||||
* In addition to generating and returning a unique id for the publisher,
|
||||
* this method creates internal ring buffer storage for "in-flight" intra
|
||||
* process messages which are stored when store_intra_process_message is
|
||||
* called with this publisher's unique id.
|
||||
@@ -194,23 +193,23 @@ public:
|
||||
auto mrb = mapped_ring_buffer::MappedRingBuffer<MessageT,
|
||||
typename publisher::Publisher<MessageT, Alloc>::MessageAlloc>::make_shared(
|
||||
size, publisher->get_allocator());
|
||||
state_->add_publisher(id, publisher, mrb, size);
|
||||
impl_->add_publisher(id, publisher, mrb, size);
|
||||
return id;
|
||||
}
|
||||
|
||||
/// Unregister a publisher using the publisher's unique id.
|
||||
/* This method does not allocate memory.
|
||||
/**
|
||||
* This method does not allocate memory.
|
||||
*
|
||||
* \param intra_process_publisher_id id of the publisher to remove.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_publisher(uint64_t intra_process_publisher_id)
|
||||
{
|
||||
state_->remove_publisher(intra_process_publisher_id);
|
||||
}
|
||||
remove_publisher(uint64_t intra_process_publisher_id);
|
||||
|
||||
/// Store a message in the manager, and return the message sequence number.
|
||||
/* The given message is stored in internal storage using the given publisher
|
||||
/**
|
||||
* The given message is stored in internal storage using the given publisher
|
||||
* id and the newly generated message sequence, which is also returned.
|
||||
* The combination of publisher id and message sequence number can later
|
||||
* be used with a subscription id to retrieve the message by calling
|
||||
@@ -249,7 +248,7 @@ public:
|
||||
using MRBMessageAlloc = typename std::allocator_traits<Alloc>::template rebind_alloc<MessageT>;
|
||||
using TypedMRB = typename mapped_ring_buffer::MappedRingBuffer<MessageT, MRBMessageAlloc>;
|
||||
uint64_t message_seq = 0;
|
||||
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = state_->get_publisher_info_for_id(
|
||||
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = impl_->get_publisher_info_for_id(
|
||||
intra_process_publisher_id, message_seq);
|
||||
typename TypedMRB::SharedPtr typed_buffer = std::static_pointer_cast<TypedMRB>(buffer);
|
||||
if (!typed_buffer) {
|
||||
@@ -261,14 +260,15 @@ public:
|
||||
// TODO(wjwwood): do something when a message was displaced. log debug?
|
||||
(void)did_replace; // Avoid unused variable warning.
|
||||
|
||||
state_->store_intra_process_message(intra_process_publisher_id, message_seq);
|
||||
impl_->store_intra_process_message(intra_process_publisher_id, message_seq);
|
||||
|
||||
// Return the message sequence which is sent to the subscription.
|
||||
return message_seq;
|
||||
}
|
||||
|
||||
/// Take an intra process message.
|
||||
/* The intra_process_publisher_id and message_sequence_number parameters
|
||||
/**
|
||||
* The intra_process_publisher_id and message_sequence_number parameters
|
||||
* uniquely identify a message instance, which should be taken.
|
||||
*
|
||||
* The requesting_subscriptions_intra_process_id parameter is used to make
|
||||
@@ -315,7 +315,7 @@ public:
|
||||
message = nullptr;
|
||||
|
||||
size_t target_subs_size = 0;
|
||||
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = state_->take_intra_process_message(
|
||||
mapped_ring_buffer::MappedRingBufferBase::SharedPtr buffer = impl_->take_intra_process_message(
|
||||
intra_process_publisher_id,
|
||||
message_sequence_number,
|
||||
requesting_subscriptions_intra_process_id,
|
||||
@@ -336,41 +336,18 @@ public:
|
||||
}
|
||||
|
||||
/// Return true if the given rmw_gid_t matches any stored Publishers.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
matches_any_publishers(const rmw_gid_t * id) const
|
||||
{
|
||||
return state_->matches_any_publishers(id);
|
||||
}
|
||||
matches_any_publishers(const rmw_gid_t * id) const;
|
||||
|
||||
private:
|
||||
static uint64_t get_next_unique_id()
|
||||
{
|
||||
auto next_id = next_unique_id_.fetch_add(1, std::memory_order_relaxed);
|
||||
// Check for rollover (we started at 1).
|
||||
if (0 == next_id) {
|
||||
// This puts a technical limit on the number of times you can add a publisher or subscriber.
|
||||
// But even if you could add (and remove) them at 1 kHz (very optimistic rate)
|
||||
// it would still be a very long time before you could exhaust the pool of id's:
|
||||
// 2^64 / 1000 times per sec / 60 sec / 60 min / 24 hours / 365 days = 584,942,417 years
|
||||
// So around 585 million years. Even at 1 GHz, it would take 585 years.
|
||||
// I think it's safe to avoid trying to handle overflow.
|
||||
// If we roll over then it's most likely a bug.
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::overflow_error(
|
||||
"exhausted the unique id's for publishers and subscribers in this process "
|
||||
"(congratulations your computer is either extremely fast or extremely old)");
|
||||
// *INDENT-ON*
|
||||
}
|
||||
return next_id;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
static uint64_t
|
||||
get_next_unique_id();
|
||||
|
||||
static std::atomic<uint64_t> next_unique_id_;
|
||||
|
||||
IntraProcessManagerStateBase::SharedPtr state_;
|
||||
IntraProcessManagerImplBase::SharedPtr impl_;
|
||||
};
|
||||
|
||||
std::atomic<uint64_t> IntraProcessManager::next_unique_id_ {1};
|
||||
|
||||
} // namespace intra_process_manager
|
||||
} // namespace rclcpp
|
||||
|
||||
|
||||
@@ -12,33 +12,40 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__INTRA_PROCESS_MANAGER_STATE_HPP_
|
||||
#define RCLCPP__INTRA_PROCESS_MANAGER_STATE_HPP_
|
||||
|
||||
#include <rclcpp/mapped_ring_buffer.hpp>
|
||||
#include <rclcpp/publisher.hpp>
|
||||
#include <rclcpp/subscription.hpp>
|
||||
#ifndef RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
|
||||
#define RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/mapped_ring_buffer.hpp"
|
||||
#include "rclcpp/publisher.hpp"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace intra_process_manager
|
||||
{
|
||||
|
||||
class IntraProcessManagerStateBase
|
||||
class IntraProcessManagerImplBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerStateBase);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(IntraProcessManagerImplBase)
|
||||
|
||||
IntraProcessManagerImplBase() = default;
|
||||
~IntraProcessManagerImplBase() = default;
|
||||
|
||||
virtual void
|
||||
add_subscription(uint64_t id, subscription::SubscriptionBase::SharedPtr subscription) = 0;
|
||||
@@ -70,16 +77,24 @@ public:
|
||||
|
||||
virtual bool
|
||||
matches_any_publishers(const rmw_gid_t * id) const = 0;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(IntraProcessManagerImplBase)
|
||||
};
|
||||
|
||||
template<typename Allocator = std::allocator<void>>
|
||||
class IntraProcessManagerState : public IntraProcessManagerStateBase
|
||||
class IntraProcessManagerImpl : public IntraProcessManagerImplBase
|
||||
{
|
||||
public:
|
||||
IntraProcessManagerImpl() = default;
|
||||
~IntraProcessManagerImpl() = default;
|
||||
|
||||
void
|
||||
add_subscription(uint64_t id, subscription::SubscriptionBase::SharedPtr subscription)
|
||||
{
|
||||
subscriptions_[id] = subscription;
|
||||
// subscription->get_topic_name() -> const char * can be used as the key,
|
||||
// since subscriptions_ shares the ownership of subscription
|
||||
subscription_ids_by_topic_[subscription->get_topic_name()].insert(id);
|
||||
}
|
||||
|
||||
@@ -127,13 +142,14 @@ public:
|
||||
uint64_t intra_process_publisher_id,
|
||||
uint64_t & message_seq)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(runtime_mutex_);
|
||||
auto it = publishers_.find(intra_process_publisher_id);
|
||||
if (it == publishers_.end()) {
|
||||
throw std::runtime_error("store_intra_process_message called with invalid publisher id");
|
||||
throw std::runtime_error("get_publisher_info_for_id called with invalid publisher id");
|
||||
}
|
||||
PublisherInfo & info = it->second;
|
||||
// Calculate the next message sequence number.
|
||||
message_seq = info.sequence_number.fetch_add(1, std::memory_order_relaxed);
|
||||
message_seq = info.sequence_number.fetch_add(1);
|
||||
|
||||
return info.buffer;
|
||||
}
|
||||
@@ -141,6 +157,7 @@ public:
|
||||
void
|
||||
store_intra_process_message(uint64_t intra_process_publisher_id, uint64_t message_seq)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(runtime_mutex_);
|
||||
auto it = publishers_.find(intra_process_publisher_id);
|
||||
if (it == publishers_.end()) {
|
||||
throw std::runtime_error("store_intra_process_message called with invalid publisher id");
|
||||
@@ -154,7 +171,12 @@ public:
|
||||
// Figure out what subscriptions should receive the message.
|
||||
auto & destined_subscriptions = subscription_ids_by_topic_[publisher->get_topic_name()];
|
||||
// Store the list for later comparison.
|
||||
info.target_subscriptions_by_message_sequence[message_seq].clear();
|
||||
if (info.target_subscriptions_by_message_sequence.count(message_seq) == 0) {
|
||||
info.target_subscriptions_by_message_sequence.emplace(
|
||||
message_seq, AllocSet(std::less<uint64_t>(), uint64_allocator));
|
||||
} else {
|
||||
info.target_subscriptions_by_message_sequence[message_seq].clear();
|
||||
}
|
||||
std::copy(
|
||||
destined_subscriptions.begin(), destined_subscriptions.end(),
|
||||
// Memory allocation occurs in info.target_subscriptions_by_message_sequence[message_seq]
|
||||
@@ -173,6 +195,7 @@ public:
|
||||
size_t & size
|
||||
)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(runtime_mutex_);
|
||||
PublisherInfo * info;
|
||||
{
|
||||
auto it = publishers_.find(intra_process_publisher_id);
|
||||
@@ -222,15 +245,31 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(IntraProcessManagerImpl)
|
||||
|
||||
template<typename T>
|
||||
using RebindAlloc = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
|
||||
|
||||
RebindAlloc<uint64_t> uint64_allocator;
|
||||
|
||||
using AllocSet = std::set<uint64_t, std::less<uint64_t>, RebindAlloc<uint64_t>>;
|
||||
using SubscriptionMap = std::unordered_map<uint64_t, subscription::SubscriptionBase::WeakPtr,
|
||||
std::hash<uint64_t>, std::equal_to<uint64_t>,
|
||||
RebindAlloc<std::pair<const uint64_t, subscription::SubscriptionBase::WeakPtr>>>;
|
||||
using IDTopicMap = std::map<std::string, AllocSet,
|
||||
std::less<std::string>, RebindAlloc<std::pair<std::string, AllocSet>>>;
|
||||
|
||||
struct strcmp_wrapper : public std::binary_function<const char *, const char *, bool>
|
||||
{
|
||||
bool
|
||||
operator()(const char * lhs, const char * rhs) const
|
||||
{
|
||||
return std::strcmp(lhs, rhs) < 0;
|
||||
}
|
||||
};
|
||||
using IDTopicMap = std::map<
|
||||
const char *,
|
||||
AllocSet,
|
||||
strcmp_wrapper,
|
||||
RebindAlloc<std::pair<const std::string, AllocSet>>>;
|
||||
|
||||
SubscriptionMap subscriptions_;
|
||||
|
||||
@@ -238,7 +277,7 @@ private:
|
||||
|
||||
struct PublisherInfo
|
||||
{
|
||||
RCLCPP_DISABLE_COPY(PublisherInfo);
|
||||
RCLCPP_DISABLE_COPY(PublisherInfo)
|
||||
|
||||
PublisherInfo() = default;
|
||||
|
||||
@@ -257,14 +296,15 @@ private:
|
||||
RebindAlloc<std::pair<const uint64_t, PublisherInfo>>>;
|
||||
|
||||
PublisherMap publishers_;
|
||||
|
||||
std::mutex runtime_mutex_;
|
||||
};
|
||||
|
||||
static IntraProcessManagerStateBase::SharedPtr create_default_state()
|
||||
{
|
||||
return std::make_shared<IntraProcessManagerState<>>();
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
IntraProcessManagerImplBase::SharedPtr
|
||||
create_default_impl();
|
||||
|
||||
} // namespace intra_process_manager
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__INTRA_PROCESS_MANAGER_STATE_HPP_
|
||||
#endif // RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
|
||||
@@ -12,10 +12,14 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_MACROS_HPP_
|
||||
#define RCLCPP_RCLCPP_MACROS_HPP_
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
/* Disables the copy constructor and operator= for the given class.
|
||||
#ifndef RCLCPP__MACROS_HPP_
|
||||
#define RCLCPP__MACROS_HPP_
|
||||
|
||||
/**
|
||||
* Disables the copy constructor and operator= for the given class.
|
||||
*
|
||||
* Use in the private section of the class.
|
||||
*/
|
||||
@@ -23,31 +27,50 @@
|
||||
__VA_ARGS__(const __VA_ARGS__ &) = delete; \
|
||||
__VA_ARGS__ & operator=(const __VA_ARGS__ &) = delete;
|
||||
|
||||
/* Defines aliases and static functions for using the Class with smart pointers.
|
||||
/**
|
||||
* Defines aliases and static functions for using the Class with smart pointers.
|
||||
*
|
||||
* Use in the public section of the class.
|
||||
* Make sure to include <memory> in the header when using this.
|
||||
* Make sure to include `<memory>` in the header when using this.
|
||||
*/
|
||||
#define RCLCPP_SMART_PTR_DEFINITIONS(...) \
|
||||
RCLCPP_SHARED_PTR_DEFINITIONS(__VA_ARGS__) \
|
||||
RCLCPP_WEAK_PTR_DEFINITIONS(__VA_ARGS__) \
|
||||
RCLCPP_UNIQUE_PTR_DEFINITIONS(__VA_ARGS__)
|
||||
|
||||
/* Defines aliases and static functions for using the Class with smart pointers.
|
||||
/**
|
||||
* Defines aliases and static functions for using the Class with smart pointers.
|
||||
*
|
||||
* Same as RCLCPP_SMART_PTR_DEFINITIONS expect it excludes the static
|
||||
* Class::make_unique() method definition which does not work on classes which
|
||||
* are not CopyConstructable.
|
||||
*
|
||||
* Use in the public section of the class.
|
||||
* Make sure to include <memory> in the header when using this.
|
||||
* Make sure to include `<memory>` in the header when using this.
|
||||
*/
|
||||
#define RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(...) \
|
||||
RCLCPP_SHARED_PTR_DEFINITIONS(__VA_ARGS__) \
|
||||
RCLCPP_WEAK_PTR_DEFINITIONS(__VA_ARGS__) \
|
||||
__RCLCPP_UNIQUE_PTR_ALIAS(__VA_ARGS__)
|
||||
|
||||
#define __RCLCPP_SHARED_PTR_ALIAS(...) using SharedPtr = std::shared_ptr<__VA_ARGS__>;
|
||||
/**
|
||||
* Defines aliases only for using the Class with smart pointers.
|
||||
*
|
||||
* Same as RCLCPP_SMART_PTR_DEFINITIONS expect it excludes the static
|
||||
* method definitions which do not work on pure virtual classes and classes
|
||||
* which are not CopyConstructable.
|
||||
*
|
||||
* Use in the public section of the class.
|
||||
* Make sure to include `<memory>` in the header when using this.
|
||||
*/
|
||||
#define RCLCPP_SMART_PTR_ALIASES_ONLY(...) \
|
||||
__RCLCPP_SHARED_PTR_ALIAS(__VA_ARGS__) \
|
||||
__RCLCPP_WEAK_PTR_ALIAS(__VA_ARGS__) \
|
||||
__RCLCPP_MAKE_SHARED_DEFINITION(__VA_ARGS__)
|
||||
|
||||
#define __RCLCPP_SHARED_PTR_ALIAS(...) \
|
||||
using SharedPtr = std::shared_ptr<__VA_ARGS__>; \
|
||||
using ConstSharedPtr = std::shared_ptr<const __VA_ARGS__>;
|
||||
|
||||
#define __RCLCPP_MAKE_SHARED_DEFINITION(...) \
|
||||
template<typename ... Args> \
|
||||
@@ -62,7 +85,9 @@
|
||||
__RCLCPP_SHARED_PTR_ALIAS(__VA_ARGS__) \
|
||||
__RCLCPP_MAKE_SHARED_DEFINITION(__VA_ARGS__)
|
||||
|
||||
#define __RCLCPP_WEAK_PTR_ALIAS(...) using WeakPtr = std::weak_ptr<__VA_ARGS__>;
|
||||
#define __RCLCPP_WEAK_PTR_ALIAS(...) \
|
||||
using WeakPtr = std::weak_ptr<__VA_ARGS__>; \
|
||||
using ConstWeakPtr = std::weak_ptr<const __VA_ARGS__>;
|
||||
|
||||
/// Defines aliases and static functions for using the Class with weak_ptrs.
|
||||
#define RCLCPP_WEAK_PTR_DEFINITIONS(...) __RCLCPP_WEAK_PTR_ALIAS(__VA_ARGS__)
|
||||
@@ -76,11 +101,15 @@
|
||||
{ \
|
||||
return std::unique_ptr<__VA_ARGS__>(new __VA_ARGS__(std::forward<Args>(args) ...)); \
|
||||
}
|
||||
|
||||
/// Defines aliases and static functions for using the Class with unique_ptrs.
|
||||
#define RCLCPP_UNIQUE_PTR_DEFINITIONS(...) \
|
||||
__RCLCPP_UNIQUE_PTR_ALIAS(__VA_ARGS__) \
|
||||
__RCLCPP_MAKE_UNIQUE_DEFINITION(__VA_ARGS__)
|
||||
|
||||
#define RCLCPP_STRING_JOIN(arg1, arg2) RCLCPP_DO_STRING_JOIN(arg1, arg2)
|
||||
#define RCLCPP_DO_STRING_JOIN(arg1, arg2) arg1 ## arg2
|
||||
|
||||
#define RCLCPP_INFO(Args) std::cout << Args << std::endl;
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_MACROS_HPP_ */
|
||||
#endif // RCLCPP__MACROS_HPP_
|
||||
|
||||
@@ -19,24 +19,28 @@
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace mapped_ring_buffer
|
||||
{
|
||||
|
||||
class MappedRingBufferBase
|
||||
class RCLCPP_PUBLIC MappedRingBufferBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBufferBase);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBufferBase)
|
||||
};
|
||||
|
||||
/// Ring buffer container of unique_ptr's of T, which can be accessed by a key.
|
||||
/* T must be a CopyConstructable and CopyAssignable.
|
||||
/**
|
||||
* T must be a CopyConstructable and CopyAssignable.
|
||||
* This class can be used in a container by using the base class MappedRingBufferBase.
|
||||
* This class must have a positive, non-zero size.
|
||||
* This class cannot be resized nor can it reserve additional space after construction.
|
||||
@@ -55,7 +59,7 @@ template<typename T, typename Alloc = std::allocator<void>>
|
||||
class MappedRingBuffer : public MappedRingBufferBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBuffer<T, Alloc>);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(MappedRingBuffer<T, Alloc>)
|
||||
using ElemAllocTraits = allocator::AllocRebind<T, Alloc>;
|
||||
using ElemAlloc = typename ElemAllocTraits::allocator_type;
|
||||
using ElemDeleter = allocator::Deleter<ElemAlloc, T>;
|
||||
@@ -63,9 +67,11 @@ public:
|
||||
using ElemUniquePtr = std::unique_ptr<T, ElemDeleter>;
|
||||
|
||||
/// Constructor.
|
||||
/* The constructor will allocate memory while reserving space.
|
||||
/**
|
||||
* The constructor will allocate memory while reserving space.
|
||||
*
|
||||
* \param size size of the ring buffer; must be positive and non-zero.
|
||||
* \param allocator optional custom allocator
|
||||
*/
|
||||
explicit MappedRingBuffer(size_t size, std::shared_ptr<Alloc> allocator = nullptr)
|
||||
: elements_(size), head_(0)
|
||||
@@ -83,7 +89,8 @@ public:
|
||||
virtual ~MappedRingBuffer() {}
|
||||
|
||||
/// Return a copy of the value stored in the ring buffer at the given key.
|
||||
/* The key is matched if an element in the ring buffer has a matching key.
|
||||
/**
|
||||
* The key is matched if an element in the ring buffer has a matching key.
|
||||
* This method will allocate in order to return a copy.
|
||||
*
|
||||
* The key is not guaranteed to be unique, see the class docs for more.
|
||||
@@ -96,6 +103,7 @@ public:
|
||||
void
|
||||
get_copy_at_key(uint64_t key, ElemUniquePtr & value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
auto it = get_iterator_of_key(key);
|
||||
value = nullptr;
|
||||
if (it != elements_.end() && it->in_use) {
|
||||
@@ -106,7 +114,8 @@ public:
|
||||
}
|
||||
|
||||
/// Return ownership of the value stored in the ring buffer, leaving a copy.
|
||||
/* The key is matched if an element in the ring bufer has a matching key.
|
||||
/**
|
||||
* The key is matched if an element in the ring bufer has a matching key.
|
||||
* This method will allocate in order to store a copy.
|
||||
*
|
||||
* The key is not guaranteed to be unique, see the class docs for more.
|
||||
@@ -127,6 +136,7 @@ public:
|
||||
void
|
||||
get_ownership_at_key(uint64_t key, ElemUniquePtr & value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
auto it = get_iterator_of_key(key);
|
||||
value = nullptr;
|
||||
if (it != elements_.end() && it->in_use) {
|
||||
@@ -142,7 +152,8 @@ public:
|
||||
}
|
||||
|
||||
/// Return ownership of the value stored in the ring buffer at the given key.
|
||||
/* The key is matched if an element in the ring buffer has a matching key.
|
||||
/**
|
||||
* The key is matched if an element in the ring buffer has a matching key.
|
||||
*
|
||||
* The key is not guaranteed to be unique, see the class docs for more.
|
||||
*
|
||||
@@ -154,6 +165,7 @@ public:
|
||||
void
|
||||
pop_at_key(uint64_t key, ElemUniquePtr & value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
auto it = get_iterator_of_key(key);
|
||||
value = nullptr;
|
||||
if (it != elements_.end() && it->in_use) {
|
||||
@@ -163,7 +175,8 @@ public:
|
||||
}
|
||||
|
||||
/// Insert a key-value pair, displacing an existing pair if necessary.
|
||||
/* The key's uniqueness is not checked on insertion.
|
||||
/**
|
||||
* The key's uniqueness is not checked on insertion.
|
||||
* It is up to the user to ensure the key is unique.
|
||||
* This method should not allocate memory.
|
||||
*
|
||||
@@ -176,6 +189,7 @@ public:
|
||||
bool
|
||||
push_and_replace(uint64_t key, ElemUniquePtr & value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
bool did_replace = elements_[head_].in_use;
|
||||
elements_[head_].key = key;
|
||||
elements_[head_].value.swap(value);
|
||||
@@ -195,11 +209,12 @@ public:
|
||||
bool
|
||||
has_key(uint64_t key)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(data_mutex_);
|
||||
return elements_.end() != get_iterator_of_key(key);
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(MappedRingBuffer<T, Alloc>);
|
||||
RCLCPP_DISABLE_COPY(MappedRingBuffer<T, Alloc>)
|
||||
|
||||
struct element
|
||||
{
|
||||
@@ -213,7 +228,7 @@ private:
|
||||
typename std::vector<element, VectorAlloc>::iterator
|
||||
get_iterator_of_key(uint64_t key)
|
||||
{
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
auto it = std::find_if(elements_.begin(), elements_.end(), [key](element & e) -> bool {
|
||||
return e.key == key && e.in_use;
|
||||
});
|
||||
@@ -224,6 +239,7 @@ private:
|
||||
std::vector<element, VectorAlloc> elements_;
|
||||
size_t head_;
|
||||
std::shared_ptr<ElemAlloc> allocator_;
|
||||
std::mutex data_mutex_;
|
||||
};
|
||||
|
||||
} // namespace mapped_ring_buffer
|
||||
|
||||
@@ -15,20 +15,17 @@
|
||||
#ifndef RCLCPP__MEMORY_STRATEGIES_HPP_
|
||||
#define RCLCPP__MEMORY_STRATEGIES_HPP_
|
||||
|
||||
#include <rclcpp/memory_strategy.hpp>
|
||||
#include <rclcpp/strategies/allocator_memory_strategy.hpp>
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace memory_strategies
|
||||
{
|
||||
|
||||
using rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy;
|
||||
|
||||
static memory_strategy::MemoryStrategy::SharedPtr create_default_strategy()
|
||||
{
|
||||
return std::make_shared<AllocatorMemoryStrategy<>>();
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
memory_strategy::MemoryStrategy::SharedPtr
|
||||
create_default_strategy();
|
||||
|
||||
} // namespace memory_strategies
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -18,9 +18,13 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/allocator.h"
|
||||
#include "rcl/wait.h"
|
||||
|
||||
#include "rclcpp/any_executable.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -33,223 +37,77 @@ namespace memory_strategy
|
||||
* the rmw implementation after the executor waits for work, based on the number of entities that
|
||||
* come through.
|
||||
*/
|
||||
class MemoryStrategy
|
||||
class RCLCPP_PUBLIC MemoryStrategy
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy);
|
||||
using WeakNodeVector = std::vector<std::weak_ptr<node::Node>>;
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(MemoryStrategy)
|
||||
using WeakNodeVector = std::vector<rclcpp::node_interfaces::NodeBaseInterface::WeakPtr>;
|
||||
|
||||
// return the new number of subscribers
|
||||
virtual size_t fill_subscriber_handles(void ** & ptr) = 0;
|
||||
|
||||
// return the new number of services
|
||||
virtual size_t fill_service_handles(void ** & ptr) = 0;
|
||||
|
||||
// return the new number of clients
|
||||
virtual size_t fill_client_handles(void ** & ptr) = 0;
|
||||
|
||||
virtual void clear_active_entities() = 0;
|
||||
|
||||
virtual void clear_handles() = 0;
|
||||
virtual void revalidate_handles() = 0;
|
||||
virtual bool collect_entities(const WeakNodeVector & weak_nodes) = 0;
|
||||
|
||||
virtual size_t number_of_ready_subscriptions() const = 0;
|
||||
virtual size_t number_of_ready_services() const = 0;
|
||||
virtual size_t number_of_ready_clients() const = 0;
|
||||
virtual size_t number_of_ready_timers() const = 0;
|
||||
virtual size_t number_of_guard_conditions() const = 0;
|
||||
|
||||
virtual bool add_handles_to_waitset(rcl_wait_set_t * wait_set) = 0;
|
||||
virtual void clear_handles() = 0;
|
||||
virtual void remove_null_handles(rcl_wait_set_t * wait_set) = 0;
|
||||
|
||||
/// Provide a newly initialized AnyExecutable object.
|
||||
// \return Shared pointer to the fresh executable.
|
||||
virtual executor::AnyExecutable::SharedPtr instantiate_next_executable() = 0;
|
||||
virtual rclcpp::executor::AnyExecutable::SharedPtr instantiate_next_executable() = 0;
|
||||
|
||||
virtual void add_guard_condition(const rcl_guard_condition_t * guard_condition) = 0;
|
||||
|
||||
virtual void remove_guard_condition(const rcl_guard_condition_t * guard_condition) = 0;
|
||||
|
||||
virtual void
|
||||
get_next_subscription(executor::AnyExecutable::SharedPtr any_exec,
|
||||
get_next_subscription(rclcpp::executor::AnyExecutable::SharedPtr any_exec,
|
||||
const WeakNodeVector & weak_nodes) = 0;
|
||||
|
||||
virtual void
|
||||
get_next_service(executor::AnyExecutable::SharedPtr any_exec,
|
||||
get_next_service(rclcpp::executor::AnyExecutable::SharedPtr any_exec,
|
||||
const WeakNodeVector & weak_nodes) = 0;
|
||||
|
||||
virtual void
|
||||
get_next_client(executor::AnyExecutable::SharedPtr any_exec,
|
||||
get_next_client(rclcpp::executor::AnyExecutable::SharedPtr any_exec,
|
||||
const WeakNodeVector & weak_nodes) = 0;
|
||||
|
||||
virtual rcl_allocator_t
|
||||
get_allocator() = 0;
|
||||
|
||||
static rclcpp::subscription::SubscriptionBase::SharedPtr
|
||||
get_subscription_by_handle(void * subscriber_handle,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_subscription : group->get_subscription_ptrs()) {
|
||||
auto subscription = weak_subscription.lock();
|
||||
if (subscription) {
|
||||
if (subscription->get_subscription_handle()->data == subscriber_handle) {
|
||||
return subscription;
|
||||
}
|
||||
if (subscription->get_intra_process_subscription_handle() &&
|
||||
subscription->get_intra_process_subscription_handle()->data == subscriber_handle)
|
||||
{
|
||||
return subscription;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
get_subscription_by_handle(const rcl_subscription_t * subscriber_handle,
|
||||
const WeakNodeVector & weak_nodes);
|
||||
|
||||
static rclcpp::service::ServiceBase::SharedPtr
|
||||
get_service_by_handle(void * service_handle, const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & service : group->get_service_ptrs()) {
|
||||
if (service->get_service_handle()->data == service_handle) {
|
||||
return service;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
get_service_by_handle(const rcl_service_t * service_handle, const WeakNodeVector & weak_nodes);
|
||||
|
||||
static rclcpp::client::ClientBase::SharedPtr
|
||||
get_client_by_handle(void * client_handle, const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & client : group->get_client_ptrs()) {
|
||||
if (client->get_client_handle()->data == client_handle) {
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
get_client_by_handle(const rcl_client_t * client_handle, const WeakNodeVector & weak_nodes);
|
||||
|
||||
static rclcpp::node::Node::SharedPtr
|
||||
static rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
if (!group) {
|
||||
return nullptr;
|
||||
}
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto callback_group = weak_group.lock();
|
||||
if (callback_group == group) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
const WeakNodeVector & weak_nodes);
|
||||
|
||||
static rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
get_group_by_subscription(
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_sub : group->get_subscription_ptrs()) {
|
||||
auto sub = weak_sub.lock();
|
||||
if (sub == subscription) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
const WeakNodeVector & weak_nodes);
|
||||
|
||||
static rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
get_group_by_service(
|
||||
rclcpp::service::ServiceBase::SharedPtr service,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & serv : group->get_service_ptrs()) {
|
||||
if (serv == service) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
const WeakNodeVector & weak_nodes);
|
||||
|
||||
static rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
get_group_by_client(rclcpp::client::ClientBase::SharedPtr client,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & cli : group->get_client_ptrs()) {
|
||||
if (cli == client) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
const WeakNodeVector & weak_nodes);
|
||||
};
|
||||
|
||||
} // namespace memory_strategy
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__MEMORY_STRATEGY_HPP_
|
||||
|
||||
@@ -16,9 +16,11 @@
|
||||
#define RCLCPP__MESSAGE_MEMORY_STRATEGY_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -26,12 +28,12 @@ namespace message_memory_strategy
|
||||
{
|
||||
|
||||
/// Default allocation strategy for messages received by subscriptions.
|
||||
// A message memory strategy must be templated on the type of the subscription it belongs to.
|
||||
/** A message memory strategy must be templated on the type of the subscription it belongs to. */
|
||||
template<typename MessageT, typename Alloc = std::allocator<void>>
|
||||
class MessageMemoryStrategy
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(MessageMemoryStrategy);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(MessageMemoryStrategy)
|
||||
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
@@ -54,14 +56,14 @@ public:
|
||||
}
|
||||
|
||||
/// By default, dynamically allocate a new message.
|
||||
// \return Shared pointer to the new message.
|
||||
/** \return Shared pointer to the new message. */
|
||||
virtual std::shared_ptr<MessageT> borrow_message()
|
||||
{
|
||||
return std::allocate_shared<MessageT, MessageAlloc>(*message_allocator_.get());
|
||||
}
|
||||
|
||||
/// Release ownership of the message, which will deallocate it if it has no more owners.
|
||||
// \param[in] Shared pointer to the message we are returning.
|
||||
/** \param[in] msg Shared pointer to the message we are returning. */
|
||||
virtual void return_message(std::shared_ptr<MessageT> & msg)
|
||||
{
|
||||
msg.reset();
|
||||
|
||||
@@ -15,84 +15,119 @@
|
||||
#ifndef RCLCPP__NODE_HPP_
|
||||
#define RCLCPP__NODE_HPP_
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/node.h"
|
||||
|
||||
#include "rcl_interfaces/msg/list_parameters_result.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_event.hpp"
|
||||
#include "rcl_interfaces/msg/set_parameters_result.hpp"
|
||||
#include "rosidl_generator_cpp/message_type_support.hpp"
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/client.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/event.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/message_memory_strategy.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_services_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/publisher.hpp"
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/timer.hpp"
|
||||
|
||||
|
||||
// Forward declaration of ROS middleware class
|
||||
namespace rmw
|
||||
{
|
||||
struct rmw_node_t;
|
||||
} // namespace rmw
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace node
|
||||
{
|
||||
|
||||
/// Node is the single point of entry for creating publishers and subscribers.
|
||||
class Node
|
||||
class Node : public std::enable_shared_from_this<Node>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Node);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Node)
|
||||
|
||||
/// Create a new node with the specified name.
|
||||
/**
|
||||
* \param[in] node_name Name of the node.
|
||||
* \param[in] namespace_ Namespace of the node.
|
||||
* \param[in] use_intra_process_comms True to use the optimized intra-process communication
|
||||
* pipeline to pass messages between nodes in the same process using shared memory.
|
||||
*/
|
||||
explicit Node(const std::string & node_name, bool use_intra_process_comms = false);
|
||||
RCLCPP_PUBLIC
|
||||
explicit Node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_ = "",
|
||||
bool use_intra_process_comms = false);
|
||||
|
||||
/// Create a node based on the node name and a rclcpp::context::Context.
|
||||
/**
|
||||
* \param[in] node_name Name of the node.
|
||||
* \param[in] namespace_ Namespace of the node.
|
||||
* \param[in] context The context for the node (usually represents the state of a process).
|
||||
* \param[in] use_intra_process_comms True to use the optimized intra-process communication
|
||||
* pipeline to pass messages between nodes in the same process using shared memory.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
Node(
|
||||
const std::string & node_name, rclcpp::context::Context::SharedPtr context,
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
rclcpp::context::Context::SharedPtr context,
|
||||
bool use_intra_process_comms = false);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~Node();
|
||||
|
||||
/// Get the name of the node.
|
||||
// \return The name of the node.
|
||||
const std::string &
|
||||
get_name() const {return name_; }
|
||||
/** \return The name of the node. */
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
get_name() const;
|
||||
|
||||
/// Get the namespace of the node.
|
||||
/** \return The namespace of the node. */
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
get_namespace() const;
|
||||
|
||||
/// Create and return a callback group.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
create_callback_group(rclcpp::callback_group::CallbackGroupType group_type);
|
||||
|
||||
/// Return the list of callback groups in the node.
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
|
||||
get_callback_groups() const;
|
||||
|
||||
/// Create and return a Publisher.
|
||||
/**
|
||||
* \param[in] topic_name The topic for this publisher to publish on.
|
||||
* \param[in] qos_history_depth The depth of the publisher message queue.
|
||||
* \param[in] allocator Optional custom allocator.
|
||||
* \return Shared pointer to the created publisher.
|
||||
*/
|
||||
template<typename MessageT, typename Alloc = std::allocator<void>>
|
||||
typename rclcpp::publisher::Publisher<MessageT, Alloc>::SharedPtr
|
||||
template<
|
||||
typename MessageT, typename Alloc = std::allocator<void>,
|
||||
typename PublisherT = ::rclcpp::publisher::Publisher<MessageT, Alloc>>
|
||||
std::shared_ptr<PublisherT>
|
||||
create_publisher(
|
||||
const std::string & topic_name, size_t qos_history_depth,
|
||||
std::shared_ptr<Alloc> allocator = nullptr);
|
||||
@@ -101,10 +136,13 @@ public:
|
||||
/**
|
||||
* \param[in] topic_name The topic for this publisher to publish on.
|
||||
* \param[in] qos_profile The quality of service profile to pass on to the rmw implementation.
|
||||
* \param[in] allocator Optional custom allocator.
|
||||
* \return Shared pointer to the created publisher.
|
||||
*/
|
||||
template<typename MessageT, typename Alloc = std::allocator<void>>
|
||||
typename rclcpp::publisher::Publisher<MessageT, Alloc>::SharedPtr
|
||||
template<
|
||||
typename MessageT, typename Alloc = std::allocator<void>,
|
||||
typename PublisherT = ::rclcpp::publisher::Publisher<MessageT, Alloc>>
|
||||
std::shared_ptr<PublisherT>
|
||||
create_publisher(
|
||||
const std::string & topic_name,
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
|
||||
@@ -118,17 +156,22 @@ public:
|
||||
* \param[in] group The callback group for this subscription. NULL for no callback group.
|
||||
* \param[in] ignore_local_publications True to ignore local publications.
|
||||
* \param[in] msg_mem_strat The message memory strategy to use for allocating messages.
|
||||
* \param[in] allocator Optional custom allocator.
|
||||
* \return Shared pointer to the created subscription.
|
||||
*/
|
||||
/* TODO(jacquelinekay):
|
||||
Windows build breaks when static member function passed as default
|
||||
argument to msg_mem_strat, nullptr is a workaround.
|
||||
*/
|
||||
template<typename MessageT, typename CallbackT, typename Alloc = std::allocator<void>>
|
||||
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
|
||||
template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename SubscriptionT = rclcpp::subscription::Subscription<MessageT, Alloc>>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
const std::string & topic_name,
|
||||
CallbackT callback,
|
||||
CallbackT && callback,
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
|
||||
bool ignore_local_publications = false,
|
||||
@@ -144,18 +187,23 @@ public:
|
||||
* \param[in] group The callback group for this subscription. NULL for no callback group.
|
||||
* \param[in] ignore_local_publications True to ignore local publications.
|
||||
* \param[in] msg_mem_strat The message memory strategy to use for allocating messages.
|
||||
* \param[in] allocator Optional custom allocator.
|
||||
* \return Shared pointer to the created subscription.
|
||||
*/
|
||||
/* TODO(jacquelinekay):
|
||||
Windows build breaks when static member function passed as default
|
||||
argument to msg_mem_strat, nullptr is a workaround.
|
||||
*/
|
||||
template<typename MessageT, typename CallbackT, typename Alloc = std::allocator<void>>
|
||||
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
|
||||
template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename SubscriptionT = rclcpp::subscription::Subscription<MessageT, Alloc>>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
const std::string & topic_name,
|
||||
size_t qos_history_depth,
|
||||
CallbackT callback,
|
||||
CallbackT && callback,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
|
||||
bool ignore_local_publications = false,
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
|
||||
@@ -168,115 +216,198 @@ public:
|
||||
* \param[in] callback User-defined callback function.
|
||||
* \param[in] group Callback group to execute this timer's callback in.
|
||||
*/
|
||||
rclcpp::timer::WallTimer::SharedPtr
|
||||
template<typename DurationT = std::milli, typename CallbackT>
|
||||
typename rclcpp::timer::WallTimer<CallbackT>::SharedPtr
|
||||
create_wall_timer(
|
||||
std::chrono::nanoseconds period,
|
||||
rclcpp::timer::CallbackType callback,
|
||||
std::chrono::duration<int64_t, DurationT> period,
|
||||
CallbackT callback,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/// Create a timer with a sub-nanosecond precision update period.
|
||||
/**
|
||||
* \param[in] period Time interval between triggers of the callback.
|
||||
* \param[in] callback User-defined callback function.
|
||||
* \param[in] group Callback group to execute this timer's callback in.
|
||||
*/
|
||||
// TODO(wjwwood): reenable this once I figure out why the demo doesn't build with it.
|
||||
// rclcpp::timer::WallTimer::SharedPtr
|
||||
// create_wall_timer(
|
||||
// std::chrono::duration<long double, std::nano> period,
|
||||
// rclcpp::timer::CallbackType callback,
|
||||
// rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
using CallbackGroup = rclcpp::callback_group::CallbackGroup;
|
||||
using CallbackGroupWeakPtr = std::weak_ptr<CallbackGroup>;
|
||||
using CallbackGroupWeakPtrList = std::list<CallbackGroupWeakPtr>;
|
||||
|
||||
/* Create and return a Client. */
|
||||
template<typename ServiceT>
|
||||
typename rclcpp::client::Client<ServiceT>::SharedPtr
|
||||
create_client(
|
||||
const std::string & service_name,
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
/* Create and return a Service. */
|
||||
template<typename ServiceT, typename FunctorT>
|
||||
template<typename ServiceT, typename CallbackT>
|
||||
typename rclcpp::service::Service<ServiceT>::SharedPtr
|
||||
create_service(
|
||||
const std::string & service_name,
|
||||
FunctorT callback,
|
||||
CallbackT && callback,
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_services_default,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult> set_parameters(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
set_parameters(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult set_parameters_atomically(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
|
||||
RCLCPP_PUBLIC
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
|
||||
|
||||
std::vector<rclcpp::parameter::ParameterVariant> get_parameters(
|
||||
const std::vector<std::string> & names) const;
|
||||
template<typename ParameterT>
|
||||
void
|
||||
set_parameter_if_not_set(
|
||||
const std::string & name,
|
||||
const ParameterT & value);
|
||||
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor> describe_parameters(
|
||||
const std::vector<std::string> & names) const;
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::parameter::ParameterVariant>
|
||||
get_parameters(const std::vector<std::string> & names) const;
|
||||
|
||||
std::vector<uint8_t> get_parameter_types(
|
||||
const std::vector<std::string> & names) const;
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::parameter::ParameterVariant
|
||||
get_parameter(const std::string & name) const;
|
||||
|
||||
rcl_interfaces::msg::ListParametersResult list_parameters(
|
||||
const std::vector<std::string> & prefixes, uint64_t depth) const;
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
get_parameter(
|
||||
const std::string & name,
|
||||
rclcpp::parameter::ParameterVariant & parameter) const;
|
||||
|
||||
std::map<std::string, std::string> get_topic_names_and_types() const;
|
||||
/// Assign the value of the parameter if set into the parameter argument.
|
||||
/**
|
||||
* If the parameter was not set, then the "parameter" argument is never assigned a value.
|
||||
*
|
||||
* \param[in] name The name of the parameter to get.
|
||||
* \param[out] parameter The output where the value of the parameter should be assigned.
|
||||
* \returns true if the parameter was set, false otherwise
|
||||
*/
|
||||
template<typename ParameterT>
|
||||
bool
|
||||
get_parameter(const std::string & name, ParameterT & parameter) const;
|
||||
|
||||
size_t count_publishers(const std::string & topic_name) const;
|
||||
/// Get the parameter value, or the "alternative value" if not set, and assign it to "value".
|
||||
/**
|
||||
* If the parameter was not set, then the "value" argument is assigned
|
||||
* the "alternative_value".
|
||||
* In all cases, the parameter remains not set after this function is called.
|
||||
*
|
||||
* \param[in] name The name of the parameter to get.
|
||||
* \param[out] value The output where the value of the parameter should be assigned.
|
||||
* \param[in] alternative_value Value to be stored in output if the parameter was not set.
|
||||
* \returns true if the parameter was set, false otherwise
|
||||
*/
|
||||
template<typename ParameterT>
|
||||
bool
|
||||
get_parameter_or(
|
||||
const std::string & name,
|
||||
ParameterT & value,
|
||||
const ParameterT & alternative_value) const;
|
||||
|
||||
size_t count_subscribers(const std::string & topic_name) const;
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor>
|
||||
describe_parameters(const std::vector<std::string> & names) const;
|
||||
|
||||
const CallbackGroupWeakPtrList & get_callback_groups() const;
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<uint8_t>
|
||||
get_parameter_types(const std::vector<std::string> & names) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
list_parameters(const std::vector<std::string> & prefixes, uint64_t depth) const;
|
||||
|
||||
/// Register the callback for parameter changes
|
||||
/**
|
||||
* \param[in] callback User defined callback function.
|
||||
* It is expected to atomically set parameters.
|
||||
* \note Repeated invocations of this function will overwrite previous callbacks
|
||||
*/
|
||||
template<typename CallbackT>
|
||||
void
|
||||
register_param_change_callback(CallbackT && callback);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_topic_names_and_types() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
get_service_names_and_types() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
count_publishers(const std::string & topic_name) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
count_subscribers(const std::string & topic_name) const;
|
||||
|
||||
/// Return a graph event, which will be set anytime a graph change occurs.
|
||||
/* The graph Event object is a loan which must be returned.
|
||||
* The Event object is scoped and therefore to return the load just let it go
|
||||
* out of scope.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::event::Event::SharedPtr
|
||||
get_graph_event();
|
||||
|
||||
/// Wait for a graph event to occur by waiting on an Event to become set.
|
||||
/**
|
||||
* The given Event must be acquire through the get_graph_event() method.
|
||||
*
|
||||
* \throws InvalidEventError if the given event is nullptr
|
||||
* \throws EventNotRegisteredError if the given event was not acquired with
|
||||
* get_graph_event().
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
wait_for_graph_change(
|
||||
rclcpp::event::Event::SharedPtr event,
|
||||
std::chrono::nanoseconds timeout);
|
||||
|
||||
/// Return the Node's internal NodeBaseInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
get_node_base_interface();
|
||||
|
||||
/// Return the Node's internal NodeGraphInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
|
||||
get_node_graph_interface();
|
||||
|
||||
/// Return the Node's internal NodeTimersInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
|
||||
get_node_timers_interface();
|
||||
|
||||
/// Return the Node's internal NodeTopicsInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr
|
||||
get_node_topics_interface();
|
||||
|
||||
/// Return the Node's internal NodeServicesInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
|
||||
get_node_services_interface();
|
||||
|
||||
/// Return the Node's internal NodeParametersInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
|
||||
get_node_parameters_interface();
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Node);
|
||||
|
||||
static const rosidl_message_type_support_t * ipm_ts_;
|
||||
RCLCPP_DISABLE_COPY(Node)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
group_in_node(callback_group::CallbackGroup::SharedPtr group);
|
||||
|
||||
std::string name_;
|
||||
|
||||
std::shared_ptr<rmw_node_t> node_handle_;
|
||||
|
||||
rclcpp::context::Context::SharedPtr context_;
|
||||
|
||||
CallbackGroup::SharedPtr default_callback_group_;
|
||||
CallbackGroupWeakPtrList callback_groups_;
|
||||
|
||||
size_t number_of_subscriptions_;
|
||||
size_t number_of_timers_;
|
||||
size_t number_of_services_;
|
||||
size_t number_of_clients_;
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
|
||||
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr node_timers_;
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
|
||||
|
||||
bool use_intra_process_comms_;
|
||||
|
||||
mutable std::mutex mutex_;
|
||||
|
||||
std::map<std::string, rclcpp::parameter::ParameterVariant> parameters_;
|
||||
|
||||
publisher::Publisher<rcl_interfaces::msg::ParameterEvent>::SharedPtr events_publisher_;
|
||||
};
|
||||
|
||||
const rosidl_message_type_support_t * Node::ipm_ts_ =
|
||||
rosidl_generator_cpp::get_message_type_support_handle<rcl_interfaces::msg::IntraProcessMessage>();
|
||||
|
||||
} /* namespace node */
|
||||
} /* namespace rclcpp */
|
||||
|
||||
#define RCLCPP_REGISTER_NODE(Class) RMW_EXPORT \
|
||||
rclcpp::node::Node::SharedPtr \
|
||||
create_node() \
|
||||
{ \
|
||||
return rclcpp::node::Node::SharedPtr(new Class( \
|
||||
rclcpp::contexts::default_context::DefaultContext:: \
|
||||
make_shared())); \
|
||||
}
|
||||
} // namespace node
|
||||
} // namespace rclcpp
|
||||
|
||||
#ifndef RCLCPP__NODE_IMPL_HPP_
|
||||
// Template implementations
|
||||
|
||||
@@ -30,95 +30,30 @@
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/publisher.h"
|
||||
#include "rcl/subscription.h"
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
#include "rosidl_generator_cpp/message_type_support.hpp"
|
||||
#include "rosidl_generator_cpp/service_type_support.hpp"
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/intra_process_manager.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/create_publisher.hpp"
|
||||
#include "rclcpp/create_subscription.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#ifndef RCLCPP__NODE_HPP_
|
||||
#include "node.hpp"
|
||||
#endif
|
||||
|
||||
using namespace rclcpp;
|
||||
using namespace node;
|
||||
|
||||
Node::Node(const std::string & node_name, bool use_intra_process_comms)
|
||||
: Node(
|
||||
node_name,
|
||||
rclcpp::contexts::default_context::get_global_default_context(),
|
||||
use_intra_process_comms)
|
||||
{}
|
||||
|
||||
Node::Node(
|
||||
const std::string & node_name,
|
||||
context::Context::SharedPtr context,
|
||||
bool use_intra_process_comms)
|
||||
: name_(node_name), context_(context),
|
||||
number_of_subscriptions_(0), number_of_timers_(0), number_of_services_(0),
|
||||
use_intra_process_comms_(use_intra_process_comms)
|
||||
namespace rclcpp
|
||||
{
|
||||
size_t domain_id = 0;
|
||||
char * ros_domain_id = nullptr;
|
||||
const char * env_var = "ROS_DOMAIN_ID";
|
||||
#ifndef _WIN32
|
||||
ros_domain_id = getenv(env_var);
|
||||
#else
|
||||
size_t ros_domain_id_size;
|
||||
_dupenv_s(&ros_domain_id, &ros_domain_id_size, env_var);
|
||||
#endif
|
||||
if (ros_domain_id) {
|
||||
uint32_t number = strtoul(ros_domain_id, NULL, 0);
|
||||
if (number == (std::numeric_limits<uint32_t>::max)()) {
|
||||
throw std::runtime_error("failed to interpret ROS_DOMAIN_ID as integral number");
|
||||
}
|
||||
domain_id = static_cast<size_t>(number);
|
||||
#ifdef _WIN32
|
||||
free(ros_domain_id);
|
||||
#endif
|
||||
}
|
||||
|
||||
auto node = rmw_create_node(name_.c_str(), domain_id);
|
||||
if (!node) {
|
||||
// *INDENT-OFF*
|
||||
throw std::runtime_error(
|
||||
std::string("could not create node: ") +
|
||||
rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
// Initialize node handle shared_ptr with custom deleter.
|
||||
// *INDENT-OFF*
|
||||
node_handle_.reset(node, [](rmw_node_t * node) {
|
||||
auto ret = rmw_destroy_node(node);
|
||||
if (ret != RMW_RET_OK) {
|
||||
fprintf(
|
||||
stderr, "Error in destruction of rmw node handle: %s\n", rmw_get_error_string_safe());
|
||||
}
|
||||
});
|
||||
// *INDENT-ON*
|
||||
|
||||
using rclcpp::callback_group::CallbackGroupType;
|
||||
default_callback_group_ = create_callback_group(
|
||||
CallbackGroupType::MutuallyExclusive);
|
||||
events_publisher_ = create_publisher<rcl_interfaces::msg::ParameterEvent>(
|
||||
"parameter_events", rmw_qos_profile_parameter_events);
|
||||
}
|
||||
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
Node::create_callback_group(
|
||||
rclcpp::callback_group::CallbackGroupType group_type)
|
||||
namespace node
|
||||
{
|
||||
using rclcpp::callback_group::CallbackGroup;
|
||||
using rclcpp::callback_group::CallbackGroupType;
|
||||
auto group = CallbackGroup::SharedPtr(new CallbackGroup(group_type));
|
||||
callback_groups_.push_back(group);
|
||||
return group;
|
||||
}
|
||||
|
||||
template<typename MessageT, typename Alloc>
|
||||
typename rclcpp::publisher::Publisher<MessageT, Alloc>::SharedPtr
|
||||
template<typename MessageT, typename Alloc, typename PublisherT>
|
||||
std::shared_ptr<PublisherT>
|
||||
Node::create_publisher(
|
||||
const std::string & topic_name, size_t qos_history_depth,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
@@ -128,11 +63,11 @@ Node::create_publisher(
|
||||
}
|
||||
rmw_qos_profile_t qos = rmw_qos_profile_default;
|
||||
qos.depth = qos_history_depth;
|
||||
return this->create_publisher<MessageT, Alloc>(topic_name, qos, allocator);
|
||||
return this->create_publisher<MessageT, Alloc, PublisherT>(topic_name, qos, allocator);
|
||||
}
|
||||
|
||||
template<typename MessageT, typename Alloc>
|
||||
typename publisher::Publisher<MessageT, Alloc>::SharedPtr
|
||||
template<typename MessageT, typename Alloc, typename PublisherT>
|
||||
std::shared_ptr<PublisherT>
|
||||
Node::create_publisher(
|
||||
const std::string & topic_name, const rmw_qos_profile_t & qos_profile,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
@@ -140,199 +75,53 @@ Node::create_publisher(
|
||||
if (!allocator) {
|
||||
allocator = std::make_shared<Alloc>();
|
||||
}
|
||||
using rosidl_generator_cpp::get_message_type_support_handle;
|
||||
auto type_support_handle = get_message_type_support_handle<MessageT>();
|
||||
rmw_publisher_t * publisher_handle = rmw_create_publisher(
|
||||
node_handle_.get(), type_support_handle, topic_name.c_str(), qos_profile);
|
||||
if (!publisher_handle) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("could not create publisher: ") +
|
||||
rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
|
||||
auto publisher = publisher::Publisher<MessageT, Alloc>::make_shared(
|
||||
node_handle_, publisher_handle, topic_name, qos_profile.depth, allocator);
|
||||
|
||||
if (use_intra_process_comms_) {
|
||||
rmw_publisher_t * intra_process_publisher_handle = rmw_create_publisher(
|
||||
node_handle_.get(), ipm_ts_, (topic_name + "__intra").c_str(), qos_profile);
|
||||
if (!intra_process_publisher_handle) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("could not create intra process publisher: ") +
|
||||
rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
|
||||
auto intra_process_manager =
|
||||
context_->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
|
||||
uint64_t intra_process_publisher_id =
|
||||
intra_process_manager->add_publisher<MessageT, Alloc>(publisher);
|
||||
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = intra_process_manager;
|
||||
// *INDENT-OFF*
|
||||
auto shared_publish_callback =
|
||||
[weak_ipm](uint64_t publisher_id, void * msg, const std::type_info & type_info) -> uint64_t
|
||||
{
|
||||
auto ipm = weak_ipm.lock();
|
||||
if (!ipm) {
|
||||
// TODO(wjwwood): should this just return silently? Or maybe return with a logged warning?
|
||||
throw std::runtime_error(
|
||||
"intra process publish called after destruction of intra process manager");
|
||||
}
|
||||
if (!msg) {
|
||||
throw std::runtime_error("cannot publisher msg which is a null pointer");
|
||||
}
|
||||
auto & message_type_info = typeid(MessageT);
|
||||
if (message_type_info != type_info) {
|
||||
throw std::runtime_error(
|
||||
std::string("published type '") + type_info.name() +
|
||||
"' is incompatible from the publisher type '" + message_type_info.name() + "'");
|
||||
}
|
||||
MessageT * typed_message_ptr = static_cast<MessageT *>(msg);
|
||||
using MessageDeleter = typename publisher::Publisher<MessageT, Alloc>::MessageDeleter;
|
||||
std::unique_ptr<MessageT, MessageDeleter> unique_msg(typed_message_ptr);
|
||||
uint64_t message_seq =
|
||||
ipm->store_intra_process_message<MessageT, Alloc>(publisher_id, unique_msg);
|
||||
return message_seq;
|
||||
};
|
||||
// *INDENT-ON*
|
||||
publisher->setup_intra_process(
|
||||
intra_process_publisher_id,
|
||||
shared_publish_callback,
|
||||
intra_process_publisher_handle);
|
||||
}
|
||||
return publisher;
|
||||
return rclcpp::create_publisher<MessageT, Alloc, PublisherT>(
|
||||
this->node_topics_.get(),
|
||||
topic_name,
|
||||
qos_profile,
|
||||
use_intra_process_comms_,
|
||||
allocator);
|
||||
}
|
||||
|
||||
bool
|
||||
Node::group_in_node(callback_group::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
bool group_belongs_to_this_node = false;
|
||||
for (auto & weak_group : this->callback_groups_) {
|
||||
auto cur_group = weak_group.lock();
|
||||
if (cur_group && (cur_group == group)) {
|
||||
group_belongs_to_this_node = true;
|
||||
}
|
||||
}
|
||||
return group_belongs_to_this_node;
|
||||
}
|
||||
|
||||
template<typename MessageT, typename CallbackT, typename Alloc>
|
||||
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
|
||||
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
Node::create_subscription(
|
||||
const std::string & topic_name,
|
||||
CallbackT callback,
|
||||
CallbackT && callback,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group,
|
||||
bool ignore_local_publications,
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
|
||||
msg_mem_strat,
|
||||
typename std::shared_ptr<Alloc> allocator)
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
{
|
||||
if (!allocator) {
|
||||
allocator = std::make_shared<Alloc>();
|
||||
}
|
||||
|
||||
rclcpp::subscription::AnySubscriptionCallback<MessageT,
|
||||
Alloc> any_subscription_callback(allocator);
|
||||
any_subscription_callback.set(callback);
|
||||
|
||||
using rosidl_generator_cpp::get_message_type_support_handle;
|
||||
|
||||
if (!msg_mem_strat) {
|
||||
msg_mem_strat =
|
||||
rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::create_default();
|
||||
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
|
||||
msg_mem_strat = MessageMemoryStrategy<MessageT, Alloc>::create_default();
|
||||
}
|
||||
|
||||
auto type_support_handle = get_message_type_support_handle<MessageT>();
|
||||
rmw_subscription_t * subscriber_handle = rmw_create_subscription(
|
||||
node_handle_.get(), type_support_handle,
|
||||
topic_name.c_str(), qos_profile, ignore_local_publications);
|
||||
if (!subscriber_handle) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("could not create subscription: ") + rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
|
||||
using namespace rclcpp::subscription;
|
||||
|
||||
auto sub = Subscription<MessageT, Alloc>::make_shared(
|
||||
node_handle_,
|
||||
subscriber_handle,
|
||||
return rclcpp::create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
|
||||
this->node_topics_.get(),
|
||||
topic_name,
|
||||
std::forward<CallbackT>(callback),
|
||||
qos_profile,
|
||||
group,
|
||||
ignore_local_publications,
|
||||
any_subscription_callback,
|
||||
msg_mem_strat);
|
||||
auto sub_base_ptr = std::dynamic_pointer_cast<SubscriptionBase>(sub);
|
||||
// Setup intra process.
|
||||
if (use_intra_process_comms_) {
|
||||
rmw_subscription_t * intra_process_subscriber_handle = rmw_create_subscription(
|
||||
node_handle_.get(), ipm_ts_,
|
||||
(topic_name + "__intra").c_str(), qos_profile, false);
|
||||
if (!subscriber_handle) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("could not create intra process subscription: ") + rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
auto intra_process_manager =
|
||||
context_->get_sub_context<rclcpp::intra_process_manager::IntraProcessManager>();
|
||||
rclcpp::intra_process_manager::IntraProcessManager::WeakPtr weak_ipm = intra_process_manager;
|
||||
uint64_t intra_process_subscription_id =
|
||||
intra_process_manager->add_subscription(sub_base_ptr);
|
||||
// *INDENT-OFF*
|
||||
sub->setup_intra_process(
|
||||
intra_process_subscription_id,
|
||||
intra_process_subscriber_handle,
|
||||
[weak_ipm](
|
||||
uint64_t publisher_id,
|
||||
uint64_t message_sequence,
|
||||
uint64_t subscription_id,
|
||||
typename Subscription<MessageT, Alloc>::MessageUniquePtr & message)
|
||||
{
|
||||
auto ipm = weak_ipm.lock();
|
||||
if (!ipm) {
|
||||
// TODO(wjwwood): should this just return silently? Or maybe return with a logged warning?
|
||||
throw std::runtime_error(
|
||||
"intra process take called after destruction of intra process manager");
|
||||
}
|
||||
ipm->take_intra_process_message<MessageT, Alloc>(
|
||||
publisher_id, message_sequence, subscription_id, message);
|
||||
},
|
||||
[weak_ipm](const rmw_gid_t * sender_gid) -> bool {
|
||||
auto ipm = weak_ipm.lock();
|
||||
if (!ipm) {
|
||||
throw std::runtime_error(
|
||||
"intra process publisher check called after destruction of intra process manager");
|
||||
}
|
||||
return ipm->matches_any_publishers(sender_gid);
|
||||
}
|
||||
);
|
||||
// *INDENT-ON*
|
||||
}
|
||||
// Assign to a group.
|
||||
if (group) {
|
||||
if (!group_in_node(group)) {
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create subscription, group not in node.");
|
||||
}
|
||||
group->add_subscription(sub_base_ptr);
|
||||
} else {
|
||||
default_callback_group_->add_subscription(sub_base_ptr);
|
||||
}
|
||||
number_of_subscriptions_++;
|
||||
return sub;
|
||||
use_intra_process_comms_,
|
||||
msg_mem_strat,
|
||||
allocator);
|
||||
}
|
||||
|
||||
template<typename MessageT, typename CallbackT, typename Alloc>
|
||||
typename rclcpp::subscription::Subscription<MessageT, Alloc>::SharedPtr
|
||||
template<typename MessageT, typename CallbackT, typename Alloc, typename SubscriptionT>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
Node::create_subscription(
|
||||
const std::string & topic_name,
|
||||
size_t qos_history_depth,
|
||||
CallbackT callback,
|
||||
CallbackT && callback,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group,
|
||||
bool ignore_local_publications,
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
|
||||
@@ -341,9 +130,9 @@ Node::create_subscription(
|
||||
{
|
||||
rmw_qos_profile_t qos = rmw_qos_profile_default;
|
||||
qos.depth = qos_history_depth;
|
||||
return this->create_subscription<MessageT, CallbackT, Alloc>(
|
||||
return this->create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
|
||||
topic_name,
|
||||
callback,
|
||||
std::forward<CallbackT>(callback),
|
||||
qos,
|
||||
group,
|
||||
ignore_local_publications,
|
||||
@@ -351,326 +140,115 @@ Node::create_subscription(
|
||||
allocator);
|
||||
}
|
||||
|
||||
rclcpp::timer::WallTimer::SharedPtr
|
||||
template<typename DurationT, typename CallbackT>
|
||||
typename rclcpp::timer::WallTimer<CallbackT>::SharedPtr
|
||||
Node::create_wall_timer(
|
||||
std::chrono::nanoseconds period,
|
||||
rclcpp::timer::CallbackType callback,
|
||||
std::chrono::duration<int64_t, DurationT> period,
|
||||
CallbackT callback,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
auto timer = rclcpp::timer::WallTimer::make_shared(period, callback);
|
||||
if (group) {
|
||||
if (!group_in_node(group)) {
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create timer, group not in node.");
|
||||
}
|
||||
group->add_timer(timer);
|
||||
} else {
|
||||
default_callback_group_->add_timer(timer);
|
||||
}
|
||||
number_of_timers_++;
|
||||
auto timer = rclcpp::timer::WallTimer<CallbackT>::make_shared(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(period),
|
||||
std::move(callback));
|
||||
node_timers_->add_timer(timer, group);
|
||||
return timer;
|
||||
}
|
||||
|
||||
// TODO(wjwwood): reenable this once I figure out why the demo doesn't build with it.
|
||||
// rclcpp::timer::WallTimer::SharedPtr
|
||||
// Node::create_wall_timer(
|
||||
// std::chrono::duration<long double, std::nano> period,
|
||||
// rclcpp::timer::CallbackType callback,
|
||||
// rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
// {
|
||||
// return create_wall_timer(
|
||||
// std::chrono::duration_cast<std::chrono::nanoseconds>(period),
|
||||
// callback,
|
||||
// group);
|
||||
// }
|
||||
|
||||
template<typename ServiceT>
|
||||
typename client::Client<ServiceT>::SharedPtr
|
||||
Node::create_client(
|
||||
const std::string & service_name,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
using rosidl_generator_cpp::get_service_type_support_handle;
|
||||
auto service_type_support_handle =
|
||||
get_service_type_support_handle<ServiceT>();
|
||||
rcl_client_options_t options = rcl_client_get_default_options();
|
||||
options.qos = qos_profile;
|
||||
|
||||
rmw_client_t * client_handle = rmw_create_client(
|
||||
this->node_handle_.get(), service_type_support_handle, service_name.c_str());
|
||||
if (!client_handle) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("could not create client: ") +
|
||||
rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
|
||||
using namespace rclcpp::client;
|
||||
using rclcpp::client::Client;
|
||||
using rclcpp::client::ClientBase;
|
||||
|
||||
auto cli = Client<ServiceT>::make_shared(
|
||||
node_handle_,
|
||||
client_handle,
|
||||
service_name);
|
||||
node_base_.get(),
|
||||
node_graph_,
|
||||
service_name,
|
||||
options);
|
||||
|
||||
auto cli_base_ptr = std::dynamic_pointer_cast<ClientBase>(cli);
|
||||
if (group) {
|
||||
if (!group_in_node(group)) {
|
||||
// TODO(esteve): use custom exception
|
||||
throw std::runtime_error("Cannot create client, group not in node.");
|
||||
}
|
||||
group->add_client(cli_base_ptr);
|
||||
} else {
|
||||
default_callback_group_->add_client(cli_base_ptr);
|
||||
}
|
||||
number_of_clients_++;
|
||||
|
||||
node_services_->add_client(cli_base_ptr, group);
|
||||
return cli;
|
||||
}
|
||||
|
||||
template<typename ServiceT, typename FunctorT>
|
||||
template<typename ServiceT, typename CallbackT>
|
||||
typename rclcpp::service::Service<ServiceT>::SharedPtr
|
||||
Node::create_service(
|
||||
const std::string & service_name,
|
||||
FunctorT callback,
|
||||
CallbackT && callback,
|
||||
const rmw_qos_profile_t & qos_profile,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
using rosidl_generator_cpp::get_service_type_support_handle;
|
||||
auto service_type_support_handle =
|
||||
get_service_type_support_handle<ServiceT>();
|
||||
|
||||
rclcpp::service::AnyServiceCallback<ServiceT> any_service_callback;
|
||||
any_service_callback.set(callback);
|
||||
any_service_callback.set(std::forward<CallbackT>(callback));
|
||||
|
||||
rmw_service_t * service_handle = rmw_create_service(
|
||||
node_handle_.get(), service_type_support_handle, service_name.c_str());
|
||||
if (!service_handle) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("could not create service: ") +
|
||||
rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
rcl_service_options_t service_options = rcl_service_get_default_options();
|
||||
service_options.qos = qos_profile;
|
||||
|
||||
auto serv = service::Service<ServiceT>::make_shared(
|
||||
node_handle_, service_handle, service_name, any_service_callback);
|
||||
node_base_->get_shared_rcl_node_handle(),
|
||||
service_name, any_service_callback, service_options);
|
||||
auto serv_base_ptr = std::dynamic_pointer_cast<service::ServiceBase>(serv);
|
||||
if (group) {
|
||||
if (!group_in_node(group)) {
|
||||
// TODO(jacquelinekay): use custom exception
|
||||
throw std::runtime_error("Cannot create service, group not in node.");
|
||||
}
|
||||
group->add_service(serv_base_ptr);
|
||||
} else {
|
||||
default_callback_group_->add_service(serv_base_ptr);
|
||||
}
|
||||
number_of_services_++;
|
||||
node_services_->add_service(serv_base_ptr, group);
|
||||
return serv;
|
||||
}
|
||||
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
Node::set_parameters(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
|
||||
template<typename CallbackT>
|
||||
void
|
||||
Node::register_param_change_callback(CallbackT && callback)
|
||||
{
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult> results;
|
||||
for (auto p : parameters) {
|
||||
auto result = set_parameters_atomically({{p}});
|
||||
results.push_back(result);
|
||||
}
|
||||
return results;
|
||||
this->node_parameters_->register_param_change_callback(std::forward<CallbackT>(callback));
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
Node::set_parameters_atomically(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
|
||||
template<typename ParameterT>
|
||||
void
|
||||
Node::set_parameter_if_not_set(
|
||||
const std::string & name,
|
||||
const ParameterT & value)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::map<std::string, rclcpp::parameter::ParameterVariant> tmp_map;
|
||||
auto parameter_event = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
|
||||
|
||||
for (auto p : parameters) {
|
||||
if (parameters_.find(p.get_name()) == parameters_.end()) {
|
||||
if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
|
||||
parameter_event->new_parameters.push_back(p.to_parameter());
|
||||
}
|
||||
} else if (p.get_type() != rclcpp::parameter::ParameterType::PARAMETER_NOT_SET) {
|
||||
parameter_event->changed_parameters.push_back(p.to_parameter());
|
||||
} else {
|
||||
parameter_event->deleted_parameters.push_back(p.to_parameter());
|
||||
}
|
||||
tmp_map[p.get_name()] = p;
|
||||
rclcpp::parameter::ParameterVariant parameter_variant;
|
||||
if (!this->get_parameter(name, parameter_variant)) {
|
||||
this->set_parameters({
|
||||
rclcpp::parameter::ParameterVariant(name, value),
|
||||
});
|
||||
}
|
||||
// std::map::insert will not overwrite elements, so we'll keep the new
|
||||
// ones and add only those that already exist in the Node's internal map
|
||||
tmp_map.insert(parameters_.begin(), parameters_.end());
|
||||
std::swap(tmp_map, parameters_);
|
||||
}
|
||||
|
||||
// TODO(jacquelinekay): handle parameter constraints
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
result.successful = true;
|
||||
|
||||
events_publisher_->publish(parameter_event);
|
||||
template<typename ParameterT>
|
||||
bool
|
||||
Node::get_parameter(const std::string & name, ParameterT & value) const
|
||||
{
|
||||
rclcpp::parameter::ParameterVariant parameter_variant;
|
||||
bool result = get_parameter(name, parameter_variant);
|
||||
if (result) {
|
||||
value = parameter_variant.get_value<ParameterT>();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::parameter::ParameterVariant>
|
||||
Node::get_parameters(
|
||||
const std::vector<std::string> & names) const
|
||||
template<typename ParameterT>
|
||||
bool
|
||||
Node::get_parameter_or(
|
||||
const std::string & name,
|
||||
ParameterT & value,
|
||||
const ParameterT & alternative_value) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<rclcpp::parameter::ParameterVariant> results;
|
||||
|
||||
for (auto & name : names) {
|
||||
if (std::any_of(parameters_.cbegin(), parameters_.cend(),
|
||||
[&name](const std::pair<std::string, rclcpp::parameter::ParameterVariant> & kv) {
|
||||
return name == kv.first;
|
||||
}))
|
||||
{
|
||||
results.push_back(parameters_.at(name));
|
||||
}
|
||||
bool got_parameter = get_parameter(name, value);
|
||||
if (!got_parameter) {
|
||||
value = alternative_value;
|
||||
}
|
||||
return results;
|
||||
return got_parameter;
|
||||
}
|
||||
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor>
|
||||
Node::describe_parameters(
|
||||
const std::vector<std::string> & names) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor> results;
|
||||
for (auto & kv : parameters_) {
|
||||
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
|
||||
return name == kv.first;
|
||||
}))
|
||||
{
|
||||
rcl_interfaces::msg::ParameterDescriptor parameter_descriptor;
|
||||
parameter_descriptor.name = kv.first;
|
||||
parameter_descriptor.type = kv.second.get_type();
|
||||
results.push_back(parameter_descriptor);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
std::vector<uint8_t>
|
||||
Node::get_parameter_types(
|
||||
const std::vector<std::string> & names) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::vector<uint8_t> results;
|
||||
for (auto & kv : parameters_) {
|
||||
if (std::any_of(names.cbegin(), names.cend(), [&kv](const std::string & name) {
|
||||
return name == kv.first;
|
||||
}))
|
||||
{
|
||||
results.push_back(kv.second.get_type());
|
||||
} else {
|
||||
results.push_back(rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET);
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
Node::list_parameters(
|
||||
const std::vector<std::string> & prefixes, uint64_t depth) const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
rcl_interfaces::msg::ListParametersResult result;
|
||||
|
||||
// TODO(esteve): define parameter separator, use "." for now
|
||||
for (auto & kv : parameters_) {
|
||||
if (std::any_of(prefixes.cbegin(), prefixes.cend(), [&kv, &depth](const std::string & prefix) {
|
||||
if (kv.first == prefix) {
|
||||
return true;
|
||||
} else if (kv.first.find(prefix + ".") == 0) {
|
||||
size_t length = prefix.length();
|
||||
std::string substr = kv.first.substr(length);
|
||||
// Cast as unsigned integer to avoid warning
|
||||
return static_cast<uint64_t>(std::count(substr.begin(), substr.end(), '.')) < depth;
|
||||
}
|
||||
return false;
|
||||
}))
|
||||
{
|
||||
result.names.push_back(kv.first);
|
||||
size_t last_separator = kv.first.find_last_of('.');
|
||||
if (std::string::npos != last_separator) {
|
||||
std::string prefix = kv.first.substr(0, last_separator);
|
||||
if (std::find(result.prefixes.cbegin(), result.prefixes.cend(), prefix) ==
|
||||
result.prefixes.cend())
|
||||
{
|
||||
result.prefixes.push_back(prefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<std::string, std::string>
|
||||
Node::get_topic_names_and_types() const
|
||||
{
|
||||
rmw_topic_names_and_types_t topic_names_and_types;
|
||||
topic_names_and_types.topic_count = 0;
|
||||
topic_names_and_types.topic_names = nullptr;
|
||||
topic_names_and_types.type_names = nullptr;
|
||||
|
||||
auto ret = rmw_get_topic_names_and_types(node_handle_.get(), &topic_names_and_types);
|
||||
if (ret != RMW_RET_OK) {
|
||||
// *INDENT-OFF*
|
||||
throw std::runtime_error(
|
||||
std::string("could not get topic names and types: ") + rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
|
||||
std::map<std::string, std::string> topics;
|
||||
for (size_t i = 0; i < topic_names_and_types.topic_count; ++i) {
|
||||
topics[topic_names_and_types.topic_names[i]] = topic_names_and_types.type_names[i];
|
||||
}
|
||||
|
||||
ret = rmw_destroy_topic_names_and_types(&topic_names_and_types);
|
||||
if (ret != RMW_RET_OK) {
|
||||
// *INDENT-OFF*
|
||||
throw std::runtime_error(
|
||||
std::string("could not destroy topic names and types: ") + rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
|
||||
return topics;
|
||||
}
|
||||
|
||||
size_t
|
||||
Node::count_publishers(const std::string & topic_name) const
|
||||
{
|
||||
size_t count;
|
||||
auto ret = rmw_count_publishers(node_handle_.get(), topic_name.c_str(), &count);
|
||||
if (ret != RMW_RET_OK) {
|
||||
// *INDENT-OFF*
|
||||
throw std::runtime_error(
|
||||
std::string("could not count publishers: ") + rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
size_t
|
||||
Node::count_subscribers(const std::string & topic_name) const
|
||||
{
|
||||
size_t count;
|
||||
auto ret = rmw_count_subscribers(node_handle_.get(), topic_name.c_str(), &count);
|
||||
if (ret != RMW_RET_OK) {
|
||||
// *INDENT-OFF*
|
||||
throw std::runtime_error(
|
||||
std::string("could not count subscribers: ") + rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
const Node::CallbackGroupWeakPtrList &
|
||||
Node::get_callback_groups() const
|
||||
{
|
||||
return callback_groups_;
|
||||
}
|
||||
} // namespace node
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_IMPL_HPP_
|
||||
|
||||
139
rclcpp/include/rclcpp/node_interfaces/node_base.hpp
Normal file
139
rclcpp/include/rclcpp/node_interfaces/node_base.hpp
Normal 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_
|
||||
146
rclcpp/include/rclcpp/node_interfaces/node_base_interface.hpp
Normal file
146
rclcpp/include/rclcpp/node_interfaces/node_base_interface.hpp
Normal 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_
|
||||
139
rclcpp/include/rclcpp/node_interfaces/node_graph.hpp
Normal file
139
rclcpp/include/rclcpp/node_interfaces/node_graph.hpp
Normal 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_
|
||||
147
rclcpp/include/rclcpp/node_interfaces/node_graph_interface.hpp
Normal file
147
rclcpp/include/rclcpp/node_interfaces/node_graph_interface.hpp
Normal 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_
|
||||
122
rclcpp/include/rclcpp/node_interfaces/node_parameters.hpp
Normal file
122
rclcpp/include/rclcpp/node_interfaces/node_parameters.hpp
Normal 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_
|
||||
@@ -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_
|
||||
67
rclcpp/include/rclcpp/node_interfaces/node_services.hpp
Normal file
67
rclcpp/include/rclcpp/node_interfaces/node_services.hpp
Normal 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_
|
||||
@@ -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_
|
||||
60
rclcpp/include/rclcpp/node_interfaces/node_timers.hpp
Normal file
60
rclcpp/include/rclcpp/node_interfaces/node_timers.hpp
Normal 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_
|
||||
@@ -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_
|
||||
88
rclcpp/include/rclcpp/node_interfaces/node_topics.hpp
Normal file
88
rclcpp/include/rclcpp/node_interfaces/node_topics.hpp
Normal 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_
|
||||
@@ -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_
|
||||
@@ -12,253 +12,203 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_PARAMETER_HPP_
|
||||
#define RCLCPP_RCLCPP_PARAMETER_HPP_
|
||||
#ifndef RCLCPP__PARAMETER_HPP_
|
||||
#define RCLCPP__PARAMETER_HPP_
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <rcl_interfaces/msg/parameter.hpp>
|
||||
#include <rcl_interfaces/msg/parameter_type.hpp>
|
||||
#include <rcl_interfaces/msg/parameter_value.hpp>
|
||||
#include "rcl_interfaces/msg/parameter.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_type.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_value.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace parameter
|
||||
{
|
||||
|
||||
enum ParameterType
|
||||
{
|
||||
PARAMETER_NOT_SET=rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET,
|
||||
PARAMETER_BOOL=rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
|
||||
PARAMETER_INTEGER=rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
|
||||
PARAMETER_DOUBLE=rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
|
||||
PARAMETER_STRING=rcl_interfaces::msg::ParameterType::PARAMETER_STRING,
|
||||
PARAMETER_BYTES=rcl_interfaces::msg::ParameterType::PARAMETER_BYTES,
|
||||
PARAMETER_NOT_SET = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET,
|
||||
PARAMETER_BOOL = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL,
|
||||
PARAMETER_INTEGER = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER,
|
||||
PARAMETER_DOUBLE = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE,
|
||||
PARAMETER_STRING = rcl_interfaces::msg::ParameterType::PARAMETER_STRING,
|
||||
PARAMETER_BYTES = rcl_interfaces::msg::ParameterType::PARAMETER_BYTES,
|
||||
};
|
||||
|
||||
// Structure to store an arbitrary parameter with templated get/set methods
|
||||
class ParameterVariant
|
||||
{
|
||||
public:
|
||||
ParameterVariant()
|
||||
: name_("")
|
||||
{
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET;
|
||||
}
|
||||
explicit ParameterVariant(const std::string & name, const bool bool_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.bool_value = bool_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL;
|
||||
}
|
||||
explicit ParameterVariant(const std::string & name, const int int_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.integer_value = int_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
|
||||
}
|
||||
explicit ParameterVariant(const std::string & name, const int64_t int_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.integer_value = int_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
|
||||
}
|
||||
explicit ParameterVariant(const std::string & name, const float double_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.double_value = double_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
|
||||
}
|
||||
explicit ParameterVariant(const std::string & name, const double double_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.double_value = double_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
|
||||
}
|
||||
explicit ParameterVariant(const std::string & name, const std::string & string_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.string_value = string_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_STRING;
|
||||
}
|
||||
explicit ParameterVariant(const std::string & name, const char * string_value)
|
||||
: ParameterVariant(name, std::string(string_value)) {}
|
||||
explicit ParameterVariant(const std::string & name, const std::vector<uint8_t> & bytes_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.bytes_value = bytes_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BYTES;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
ParameterVariant();
|
||||
RCLCPP_PUBLIC
|
||||
explicit ParameterVariant(const std::string & name, const bool bool_value);
|
||||
RCLCPP_PUBLIC
|
||||
explicit ParameterVariant(const std::string & name, const int int_value);
|
||||
RCLCPP_PUBLIC
|
||||
explicit ParameterVariant(const std::string & name, const int64_t int_value);
|
||||
RCLCPP_PUBLIC
|
||||
explicit ParameterVariant(const std::string & name, const float double_value);
|
||||
RCLCPP_PUBLIC
|
||||
explicit ParameterVariant(const std::string & name, const double double_value);
|
||||
RCLCPP_PUBLIC
|
||||
explicit ParameterVariant(const std::string & name, const std::string & string_value);
|
||||
RCLCPP_PUBLIC
|
||||
explicit ParameterVariant(const std::string & name, const char * string_value);
|
||||
RCLCPP_PUBLIC
|
||||
explicit ParameterVariant(const std::string & name, const std::vector<uint8_t> & bytes_value);
|
||||
|
||||
inline ParameterType get_type() const {return static_cast<ParameterType>(value_.type); }
|
||||
RCLCPP_PUBLIC
|
||||
ParameterType
|
||||
get_type() const;
|
||||
|
||||
inline std::string get_type_name() const
|
||||
{
|
||||
switch (get_type()) {
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_BOOL:
|
||||
return "bool";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_INTEGER:
|
||||
return "integer";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_DOUBLE:
|
||||
return "double";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_STRING:
|
||||
return "string";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_BYTES:
|
||||
return "bytes";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_NOT_SET:
|
||||
return "not set";
|
||||
default:
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
"Unexpected type from ParameterVariant: " + std::to_string(get_type()));
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
get_type_name() const;
|
||||
|
||||
inline std::string get_name() const & {return name_; }
|
||||
RCLCPP_PUBLIC
|
||||
const std::string &
|
||||
get_name() const;
|
||||
|
||||
inline rcl_interfaces::msg::ParameterValue get_parameter_value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
rcl_interfaces::msg::ParameterValue
|
||||
get_parameter_value() const;
|
||||
|
||||
// The following get_value() variants require the use of ParameterType
|
||||
|
||||
template<ParameterType type>
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_INTEGER, int64_t>::type
|
||||
get_value() const
|
||||
{
|
||||
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER) {
|
||||
// TODO: use custom exception
|
||||
// TODO(wjwwood): use custom exception
|
||||
throw std::runtime_error("Invalid type");
|
||||
}
|
||||
return value_.integer_value;
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_DOUBLE, double>::type
|
||||
get_value() const
|
||||
{
|
||||
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE) {
|
||||
// TODO: use custom exception
|
||||
// TODO(wjwwood): use custom exception
|
||||
throw std::runtime_error("Invalid type");
|
||||
}
|
||||
return value_.double_value;
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_STRING, const std::string &>::type
|
||||
get_value() const
|
||||
{
|
||||
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_STRING) {
|
||||
// TODO: use custom exception
|
||||
// TODO(wjwwood): use custom exception
|
||||
throw std::runtime_error("Invalid type");
|
||||
}
|
||||
return value_.string_value;
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_BOOL, bool>::type
|
||||
get_value() const
|
||||
{
|
||||
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_BOOL) {
|
||||
// TODO: use custom exception
|
||||
// TODO(wjwwood): use custom exception
|
||||
throw std::runtime_error("Invalid type");
|
||||
}
|
||||
return value_.bool_value;
|
||||
}
|
||||
|
||||
template<ParameterType type>
|
||||
typename std::enable_if<type == ParameterType::PARAMETER_BYTES,
|
||||
const std::vector<uint8_t> &>::type
|
||||
typename std::enable_if<
|
||||
type == ParameterType::PARAMETER_BYTES, const std::vector<uint8_t> &>::type
|
||||
get_value() const
|
||||
{
|
||||
if (value_.type != rcl_interfaces::msg::ParameterType::PARAMETER_BYTES) {
|
||||
// TODO: use custom exception
|
||||
// TODO(wjwwood): use custom exception
|
||||
throw std::runtime_error("Invalid type");
|
||||
}
|
||||
return value_.bytes_value;
|
||||
}
|
||||
|
||||
int64_t as_int() const {return get_value<ParameterType::PARAMETER_INTEGER>(); }
|
||||
// The following get_value() variants allow the use of primitive types
|
||||
|
||||
double as_double() const {return get_value<ParameterType::PARAMETER_DOUBLE>(); }
|
||||
template<typename type>
|
||||
typename std::enable_if<
|
||||
std::is_integral<type>::value && !std::is_same<type, bool>::value, int64_t>::type
|
||||
get_value() const
|
||||
{
|
||||
return get_value<ParameterType::PARAMETER_INTEGER>();
|
||||
}
|
||||
|
||||
const std::string & as_string() const {return get_value<ParameterType::PARAMETER_STRING>(); }
|
||||
template<typename type>
|
||||
typename std::enable_if<std::is_floating_point<type>::value, double>::type
|
||||
get_value() const
|
||||
{
|
||||
return get_value<ParameterType::PARAMETER_DOUBLE>();
|
||||
}
|
||||
|
||||
bool as_bool() const {return get_value<ParameterType::PARAMETER_BOOL>(); }
|
||||
template<typename type>
|
||||
typename std::enable_if<std::is_convertible<type, std::string>::value, const std::string &>::type
|
||||
get_value() const
|
||||
{
|
||||
return get_value<ParameterType::PARAMETER_STRING>();
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> & as_bytes() const
|
||||
template<typename type>
|
||||
typename std::enable_if<std::is_same<type, bool>::value, bool>::type
|
||||
get_value() const
|
||||
{
|
||||
return get_value<ParameterType::PARAMETER_BOOL>();
|
||||
}
|
||||
|
||||
template<typename type>
|
||||
typename std::enable_if<
|
||||
std::is_convertible<
|
||||
type, const std::vector<uint8_t> &>::value, const std::vector<uint8_t> &>::type
|
||||
get_value() const
|
||||
{
|
||||
return get_value<ParameterType::PARAMETER_BYTES>();
|
||||
}
|
||||
|
||||
static ParameterVariant from_parameter(const rcl_interfaces::msg::Parameter & parameter)
|
||||
{
|
||||
switch (parameter.value.type) {
|
||||
case PARAMETER_BOOL:
|
||||
return ParameterVariant(parameter.name, parameter.value.bool_value);
|
||||
case PARAMETER_INTEGER:
|
||||
return ParameterVariant(parameter.name, parameter.value.integer_value);
|
||||
case PARAMETER_DOUBLE:
|
||||
return ParameterVariant(parameter.name, parameter.value.double_value);
|
||||
case PARAMETER_STRING:
|
||||
return ParameterVariant(parameter.name, parameter.value.string_value);
|
||||
case PARAMETER_BYTES:
|
||||
return ParameterVariant(parameter.name, parameter.value.bytes_value);
|
||||
case PARAMETER_NOT_SET:
|
||||
throw std::runtime_error("Type from ParameterValue is not set");
|
||||
default:
|
||||
// TODO(wjwwood): use custom exception
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
"Unexpected type from ParameterVariant: " + std::to_string(parameter.value.type));
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
int64_t
|
||||
as_int() const;
|
||||
|
||||
rcl_interfaces::msg::Parameter to_parameter()
|
||||
{
|
||||
rcl_interfaces::msg::Parameter parameter;
|
||||
parameter.name = name_;
|
||||
parameter.value = value_;
|
||||
return parameter;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
double
|
||||
as_double() const;
|
||||
|
||||
std::string value_to_string() const
|
||||
{
|
||||
switch (get_type()) {
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_BOOL:
|
||||
return as_bool() ? "true" : "false";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_INTEGER:
|
||||
return std::to_string(as_int());
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_DOUBLE:
|
||||
return std::to_string(as_double());
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_STRING:
|
||||
return as_string();
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_BYTES:
|
||||
{
|
||||
std::stringstream bytes;
|
||||
bool first_byte = true;
|
||||
bytes << "[" << std::hex;
|
||||
for (auto & byte : as_bytes()) {
|
||||
bytes << "0x" << byte;
|
||||
if (!first_byte) {
|
||||
bytes << ", ";
|
||||
} else {
|
||||
first_byte = false;
|
||||
}
|
||||
}
|
||||
return bytes.str();
|
||||
}
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_NOT_SET:
|
||||
return "not set";
|
||||
default:
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
"Unexpected type from ParameterVariant: " + std::to_string(get_type()));
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
const std::string &
|
||||
as_string() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
as_bool() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<uint8_t> &
|
||||
as_bytes() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
static ParameterVariant
|
||||
from_parameter(const rcl_interfaces::msg::Parameter & parameter);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_interfaces::msg::Parameter
|
||||
to_parameter();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
value_to_string() const;
|
||||
|
||||
private:
|
||||
std::string name_;
|
||||
@@ -266,72 +216,35 @@ private:
|
||||
};
|
||||
|
||||
|
||||
/* Return a json encoded version of the parameter intended for a dict. */
|
||||
std::string _to_json_dict_entry(const ParameterVariant & param)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "\"" << param.get_name() << "\": ";
|
||||
ss << "{\"type\": \"" << param.get_type_name() << "\", ";
|
||||
ss << "\"value\": \"" << param.value_to_string() << "\"}";
|
||||
return ss.str();
|
||||
}
|
||||
/// Return a json encoded version of the parameter intended for a dict.
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
_to_json_dict_entry(const ParameterVariant & param);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::ostream &
|
||||
operator<<(std::ostream & os, const rclcpp::parameter::ParameterVariant & pv);
|
||||
|
||||
} /* namespace parameter */
|
||||
RCLCPP_PUBLIC
|
||||
std::ostream &
|
||||
operator<<(std::ostream & os, const std::vector<ParameterVariant> & parameters);
|
||||
|
||||
} /* namespace rclcpp */
|
||||
} // namespace parameter
|
||||
} // namespace rclcpp
|
||||
|
||||
namespace std
|
||||
{
|
||||
/* Return a json encoded version of the parameter intended for a list. */
|
||||
inline std::string to_string(const rclcpp::parameter::ParameterVariant & param)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "{\"name\": \"" << param.get_name() << "\", ";
|
||||
ss << "\"type\": \"" << param.get_type_name() << "\", ";
|
||||
ss << "\"value\": \"" << param.value_to_string() << "\"}";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/* Return a json encoded version of a vector of parameters, as a string*/
|
||||
inline std::string to_string(const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "{";
|
||||
bool first = true;
|
||||
for (const auto & pv : parameters) {
|
||||
if (first == false) {
|
||||
ss << ", ";
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
ss << rclcpp::parameter::_to_json_dict_entry(pv);
|
||||
}
|
||||
ss << "}";
|
||||
return ss.str();
|
||||
}
|
||||
/// Return a json encoded version of the parameter intended for a list.
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
to_string(const rclcpp::parameter::ParameterVariant & param);
|
||||
|
||||
} /* namespace std */
|
||||
/// Return a json encoded version of a vector of parameters, as a string.
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
to_string(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace parameter
|
||||
{
|
||||
} // namespace std
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, const rclcpp::parameter::ParameterVariant & pv)
|
||||
{
|
||||
os << std::to_string(pv);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream & operator<<(std::ostream & os, const std::vector<ParameterVariant> & parameters)
|
||||
{
|
||||
os << std::to_string(parameters);
|
||||
return os;
|
||||
}
|
||||
|
||||
} /* namespace parameter */
|
||||
|
||||
} /* namespace rclcpp */
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_PARAMETER_HPP_ */
|
||||
#endif // RCLCPP__PARAMETER_HPP_
|
||||
|
||||
@@ -12,227 +12,93 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_PARAMETER_CLIENT_HPP_
|
||||
#define RCLCPP_RCLCPP_PARAMETER_CLIENT_HPP_
|
||||
#ifndef RCLCPP__PARAMETER_CLIENT_HPP_
|
||||
#define RCLCPP__PARAMETER_CLIENT_HPP_
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <rclcpp/executors.hpp>
|
||||
#include <rclcpp/macros.hpp>
|
||||
#include <rclcpp/node.hpp>
|
||||
#include <rclcpp/parameter.hpp>
|
||||
|
||||
#include <rcl_interfaces/msg/parameter.hpp>
|
||||
#include <rcl_interfaces/msg/parameter_event.hpp>
|
||||
#include <rcl_interfaces/msg/parameter_value.hpp>
|
||||
#include <rcl_interfaces/srv/describe_parameters.hpp>
|
||||
#include <rcl_interfaces/srv/get_parameters.hpp>
|
||||
#include <rcl_interfaces/srv/get_parameter_types.hpp>
|
||||
#include <rcl_interfaces/srv/list_parameters.hpp>
|
||||
#include <rcl_interfaces/srv/set_parameters.hpp>
|
||||
#include <rcl_interfaces/srv/set_parameters_atomically.hpp>
|
||||
#include "rcl_interfaces/msg/parameter.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_event.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_value.hpp"
|
||||
#include "rcl_interfaces/srv/describe_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/get_parameter_types.hpp"
|
||||
#include "rcl_interfaces/srv/get_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/list_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/set_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/set_parameters_atomically.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace parameter_client
|
||||
{
|
||||
|
||||
class AsyncParametersClient
|
||||
{
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(AsyncParametersClient);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(AsyncParametersClient)
|
||||
|
||||
AsyncParametersClient(const rclcpp::node::Node::SharedPtr node,
|
||||
const std::string & remote_node_name = "")
|
||||
: node_(node)
|
||||
{
|
||||
if (remote_node_name != "") {
|
||||
remote_node_name_ = remote_node_name;
|
||||
} else {
|
||||
remote_node_name_ = node_->get_name();
|
||||
}
|
||||
get_parameters_client_ = node_->create_client<rcl_interfaces::srv::GetParameters>(
|
||||
remote_node_name_ + "__get_parameters");
|
||||
get_parameter_types_client_ = node_->create_client<rcl_interfaces::srv::GetParameterTypes>(
|
||||
remote_node_name_ + "__get_parameter_types");
|
||||
set_parameters_client_ = node_->create_client<rcl_interfaces::srv::SetParameters>(
|
||||
remote_node_name_ + "__set_parameters");
|
||||
list_parameters_client_ = node_->create_client<rcl_interfaces::srv::ListParameters>(
|
||||
remote_node_name_ + "__list_parameters");
|
||||
describe_parameters_client_ = node_->create_client<rcl_interfaces::srv::DescribeParameters>(
|
||||
remote_node_name_ + "__describe_parameters");
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
AsyncParametersClient(
|
||||
const rclcpp::node::Node::SharedPtr node,
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>
|
||||
get_parameters(
|
||||
const std::vector<std::string> & names,
|
||||
std::function<void(
|
||||
std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>)> callback = nullptr)
|
||||
{
|
||||
auto promise_result =
|
||||
std::make_shared<std::promise<std::vector<rclcpp::parameter::ParameterVariant>>>();
|
||||
auto future_result = promise_result->get_future().share();
|
||||
|
||||
auto request = std::make_shared<rcl_interfaces::srv::GetParameters::Request>();
|
||||
request->names = names;
|
||||
|
||||
get_parameters_client_->async_send_request(
|
||||
request,
|
||||
[request, promise_result, future_result, &callback](
|
||||
rclcpp::client::Client<rcl_interfaces::srv::GetParameters>::SharedFuture cb_f) {
|
||||
std::vector<rclcpp::parameter::ParameterVariant> parameter_variants;
|
||||
auto & pvalues = cb_f.get()->values;
|
||||
|
||||
for (auto & pvalue : pvalues) {
|
||||
auto i = &pvalue - &pvalues[0];
|
||||
rcl_interfaces::msg::Parameter parameter;
|
||||
parameter.name = request->names[i];
|
||||
parameter.value = pvalue;
|
||||
parameter_variants.push_back(rclcpp::parameter::ParameterVariant::from_parameter(
|
||||
parameter));
|
||||
}
|
||||
|
||||
promise_result->set_value(parameter_variants);
|
||||
if (callback != nullptr) {
|
||||
callback(future_result);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return future_result;
|
||||
}
|
||||
std::function<
|
||||
void(std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>)
|
||||
> callback = nullptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rclcpp::parameter::ParameterType>>
|
||||
get_parameter_types(
|
||||
const std::vector<std::string> & names,
|
||||
std::function<void(
|
||||
std::shared_future<std::vector<rclcpp::parameter::ParameterType>>)> callback = nullptr)
|
||||
{
|
||||
auto promise_result =
|
||||
std::make_shared<std::promise<std::vector<rclcpp::parameter::ParameterType>>>();
|
||||
auto future_result = promise_result->get_future().share();
|
||||
|
||||
auto request = std::make_shared<rcl_interfaces::srv::GetParameterTypes::Request>();
|
||||
request->names = names;
|
||||
|
||||
get_parameter_types_client_->async_send_request(
|
||||
request,
|
||||
[promise_result, future_result, &callback](
|
||||
rclcpp::client::Client<rcl_interfaces::srv::GetParameterTypes>::SharedFuture cb_f) {
|
||||
std::vector<rclcpp::parameter::ParameterType> types;
|
||||
auto & pts = cb_f.get()->types;
|
||||
for (auto & pt : pts) {
|
||||
pts.push_back(static_cast<rclcpp::parameter::ParameterType>(pt));
|
||||
}
|
||||
promise_result->set_value(types);
|
||||
if (callback != nullptr) {
|
||||
callback(future_result);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return future_result;
|
||||
}
|
||||
std::function<
|
||||
void(std::shared_future<std::vector<rclcpp::parameter::ParameterType>>)
|
||||
> callback = nullptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
|
||||
set_parameters(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
|
||||
std::function<void(std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>)> callback =
|
||||
nullptr)
|
||||
{
|
||||
auto promise_result =
|
||||
std::make_shared<std::promise<std::vector<rcl_interfaces::msg::SetParametersResult>>>();
|
||||
auto future_result = promise_result->get_future().share();
|
||||
|
||||
auto request = std::make_shared<rcl_interfaces::srv::SetParameters::Request>();
|
||||
|
||||
std::transform(parameters.begin(), parameters.end(), std::back_inserter(
|
||||
request->parameters), [](
|
||||
rclcpp::parameter::ParameterVariant p) {return p.to_parameter(); });
|
||||
|
||||
set_parameters_client_->async_send_request(
|
||||
request,
|
||||
[promise_result, future_result, &callback](
|
||||
rclcpp::client::Client<rcl_interfaces::srv::SetParameters>::SharedFuture cb_f) {
|
||||
promise_result->set_value(cb_f.get()->results);
|
||||
if (callback != nullptr) {
|
||||
callback(future_result);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return future_result;
|
||||
}
|
||||
std::function<
|
||||
void(std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>)
|
||||
> callback = nullptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<rcl_interfaces::msg::SetParametersResult>
|
||||
set_parameters_atomically(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
|
||||
std::function<void(std::shared_future<rcl_interfaces::msg::SetParametersResult>)> callback =
|
||||
nullptr)
|
||||
{
|
||||
auto promise_result =
|
||||
std::make_shared<std::promise<rcl_interfaces::msg::SetParametersResult>>();
|
||||
auto future_result = promise_result->get_future().share();
|
||||
|
||||
auto request = std::make_shared<rcl_interfaces::srv::SetParametersAtomically::Request>();
|
||||
|
||||
std::transform(parameters.begin(), parameters.end(), std::back_inserter(
|
||||
request->parameters), [](
|
||||
rclcpp::parameter::ParameterVariant p) {return p.to_parameter(); });
|
||||
|
||||
set_parameters_atomically_client_->async_send_request(
|
||||
request,
|
||||
[promise_result, future_result, &callback](
|
||||
rclcpp::client::Client<rcl_interfaces::srv::SetParametersAtomically>::SharedFuture cb_f) {
|
||||
promise_result->set_value(cb_f.get()->result);
|
||||
if (callback != nullptr) {
|
||||
callback(future_result);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return future_result;
|
||||
}
|
||||
std::function<
|
||||
void(std::shared_future<rcl_interfaces::msg::SetParametersResult>)
|
||||
> callback = nullptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_future<rcl_interfaces::msg::ListParametersResult>
|
||||
list_parameters(
|
||||
const std::vector<std::string> & prefixes,
|
||||
uint64_t depth,
|
||||
std::function<void(std::shared_future<rcl_interfaces::msg::ListParametersResult>)> callback =
|
||||
nullptr)
|
||||
{
|
||||
auto promise_result =
|
||||
std::make_shared<std::promise<rcl_interfaces::msg::ListParametersResult>>();
|
||||
auto future_result = promise_result->get_future().share();
|
||||
std::function<
|
||||
void(std::shared_future<rcl_interfaces::msg::ListParametersResult>)
|
||||
> callback = nullptr);
|
||||
|
||||
auto request = std::make_shared<rcl_interfaces::srv::ListParameters::Request>();
|
||||
request->prefixes = prefixes;
|
||||
request->depth = depth;
|
||||
|
||||
list_parameters_client_->async_send_request(
|
||||
request,
|
||||
[promise_result, future_result, &callback](
|
||||
rclcpp::client::Client<rcl_interfaces::srv::ListParameters>::SharedFuture cb_f) {
|
||||
promise_result->set_value(cb_f.get()->result);
|
||||
if (callback != nullptr) {
|
||||
callback(future_result);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return future_result;
|
||||
}
|
||||
|
||||
template<typename FunctorT>
|
||||
template<typename CallbackT>
|
||||
typename rclcpp::subscription::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
|
||||
on_parameter_event(FunctorT callback)
|
||||
on_parameter_event(CallbackT && callback)
|
||||
{
|
||||
return node_->create_subscription<rcl_interfaces::msg::ParameterEvent>(
|
||||
"parameter_events", callback, rmw_qos_profile_parameter_events);
|
||||
"parameter_events", std::forward<CallbackT>(callback), rmw_qos_profile_parameter_events);
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -252,98 +118,85 @@ private:
|
||||
class SyncParametersClient
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(SyncParametersClient)
|
||||
|
||||
SyncParametersClient(
|
||||
rclcpp::node::Node::SharedPtr node)
|
||||
: node_(node)
|
||||
{
|
||||
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
async_parameters_client_ = std::make_shared<AsyncParametersClient>(node);
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
explicit SyncParametersClient(
|
||||
rclcpp::node::Node::SharedPtr node,
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
SyncParametersClient(
|
||||
rclcpp::executor::Executor::SharedPtr executor,
|
||||
rclcpp::node::Node::SharedPtr node)
|
||||
: executor_(executor), node_(node)
|
||||
{
|
||||
async_parameters_client_ = std::make_shared<AsyncParametersClient>(node);
|
||||
}
|
||||
rclcpp::node::Node::SharedPtr node,
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::parameter::ParameterVariant>
|
||||
get_parameters(const std::vector<std::string> & parameter_names)
|
||||
get_parameters(const std::vector<std::string> & parameter_names);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
has_parameter(const std::string & parameter_name);
|
||||
|
||||
template<typename T>
|
||||
T
|
||||
get_parameter_impl(
|
||||
const std::string & parameter_name, std::function<T()> parameter_not_found_handler)
|
||||
{
|
||||
auto f = async_parameters_client_->get_parameters(parameter_names);
|
||||
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
|
||||
rclcpp::executors::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
std::vector<std::string> names;
|
||||
names.push_back(parameter_name);
|
||||
auto vars = get_parameters(names);
|
||||
if ((vars.size() != 1) || (vars[0].get_type() == rclcpp::parameter::PARAMETER_NOT_SET)) {
|
||||
return parameter_not_found_handler();
|
||||
} else {
|
||||
return static_cast<T>(vars[0].get_value<T>());
|
||||
}
|
||||
// Return an empty vector if unsuccessful
|
||||
return std::vector<rclcpp::parameter::ParameterVariant>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T
|
||||
get_parameter(const std::string & parameter_name, const T & default_value)
|
||||
{
|
||||
// *INDENT-OFF*
|
||||
return get_parameter_impl(parameter_name,
|
||||
std::function<T()>([&default_value]() -> T {return default_value; }));
|
||||
// *INDENT-ON*
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
T
|
||||
get_parameter(const std::string & parameter_name)
|
||||
{
|
||||
// *INDENT-OFF*
|
||||
return get_parameter_impl(parameter_name,
|
||||
std::function<T()>([]() -> T {throw std::runtime_error("Parameter not set"); }));
|
||||
// *INDENT-ON*
|
||||
}
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::parameter::ParameterType>
|
||||
get_parameter_types(const std::vector<std::string> & parameter_names)
|
||||
{
|
||||
auto f = async_parameters_client_->get_parameter_types(parameter_names);
|
||||
|
||||
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
|
||||
rclcpp::executors::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
}
|
||||
return std::vector<rclcpp::parameter::ParameterType>();
|
||||
}
|
||||
get_parameter_types(const std::vector<std::string> & parameter_names);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
set_parameters(const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
|
||||
{
|
||||
auto f = async_parameters_client_->set_parameters(parameters);
|
||||
|
||||
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
|
||||
rclcpp::executors::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
}
|
||||
return std::vector<rcl_interfaces::msg::SetParametersResult>();
|
||||
}
|
||||
set_parameters(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
|
||||
{
|
||||
auto f = async_parameters_client_->set_parameters_atomically(parameters);
|
||||
|
||||
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
|
||||
rclcpp::executors::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unable to get result of set parameters service call.");
|
||||
}
|
||||
set_parameters_atomically(const std::vector<rclcpp::parameter::ParameterVariant> & parameters);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
list_parameters(
|
||||
const std::vector<std::string> & parameter_prefixes,
|
||||
uint64_t depth)
|
||||
{
|
||||
auto f = async_parameters_client_->list_parameters(parameter_prefixes, depth);
|
||||
uint64_t depth);
|
||||
|
||||
if (rclcpp::executors::spin_node_until_future_complete(*executor_, node_, f) ==
|
||||
rclcpp::executors::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unable to get result of list parameters service call.");
|
||||
}
|
||||
|
||||
template<typename FunctorT>
|
||||
template<typename CallbackT>
|
||||
typename rclcpp::subscription::Subscription<rcl_interfaces::msg::ParameterEvent>::SharedPtr
|
||||
on_parameter_event(FunctorT callback)
|
||||
on_parameter_event(CallbackT && callback)
|
||||
{
|
||||
return async_parameters_client_->on_parameter_event(callback);
|
||||
return async_parameters_client_->on_parameter_event(std::forward<CallbackT>(callback));
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -352,8 +205,7 @@ private:
|
||||
AsyncParametersClient::SharedPtr async_parameters_client_;
|
||||
};
|
||||
|
||||
} /* namespace parameter_client */
|
||||
} // namespace parameter_client
|
||||
} // namespace rclcpp
|
||||
|
||||
} /* namespace rclcpp */
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_PARAMETER_CLIENT_HPP_ */
|
||||
#endif // RCLCPP__PARAMETER_CLIENT_HPP_
|
||||
|
||||
@@ -12,150 +12,38 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_PARAMETER_SERVICE_HPP_
|
||||
#define RCLCPP_RCLCPP_PARAMETER_SERVICE_HPP_
|
||||
#ifndef RCLCPP__PARAMETER_SERVICE_HPP_
|
||||
#define RCLCPP__PARAMETER_SERVICE_HPP_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <rclcpp/executors.hpp>
|
||||
#include <rclcpp/macros.hpp>
|
||||
#include <rclcpp/node.hpp>
|
||||
#include <rclcpp/parameter.hpp>
|
||||
|
||||
#include <rcl_interfaces/srv/describe_parameters.hpp>
|
||||
#include <rcl_interfaces/srv/get_parameters.hpp>
|
||||
#include <rcl_interfaces/srv/get_parameter_types.hpp>
|
||||
#include <rcl_interfaces/srv/list_parameters.hpp>
|
||||
#include <rcl_interfaces/srv/set_parameters.hpp>
|
||||
#include <rcl_interfaces/srv/set_parameters_atomically.hpp>
|
||||
#include "rcl_interfaces/srv/describe_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/get_parameter_types.hpp"
|
||||
#include "rcl_interfaces/srv/get_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/list_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/set_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/set_parameters_atomically.hpp"
|
||||
#include "rclcpp/executors.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace parameter_service
|
||||
{
|
||||
|
||||
class ParameterService
|
||||
{
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ParameterService);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(ParameterService)
|
||||
|
||||
ParameterService(const rclcpp::node::Node::SharedPtr node)
|
||||
: node_(node)
|
||||
{
|
||||
std::weak_ptr<rclcpp::node::Node> captured_node = node_;
|
||||
get_parameters_service_ = node_->create_service<rcl_interfaces::srv::GetParameters>(
|
||||
node_->get_name() + "__get_parameters", [captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::GetParameters::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::GetParameters::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
auto values = node->get_parameters(request->names);
|
||||
for (auto & pvariant : values) {
|
||||
response->values.push_back(pvariant.get_parameter_value());
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
get_parameter_types_service_ = node_->create_service<rcl_interfaces::srv::GetParameterTypes>(
|
||||
node_->get_name() + "__get_parameter_types", [captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
auto types = node->get_parameter_types(request->names);
|
||||
std::transform(types.cbegin(), types.cend(),
|
||||
std::back_inserter(response->types), [](const uint8_t & type) {
|
||||
return static_cast<rclcpp::parameter::ParameterType>(type);
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
set_parameters_service_ = node_->create_service<rcl_interfaces::srv::SetParameters>(
|
||||
node_->get_name() + "__set_parameters", [captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::SetParameters::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::SetParameters::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
std::vector<rclcpp::parameter::ParameterVariant> pvariants;
|
||||
for (auto & p : request->parameters) {
|
||||
pvariants.push_back(rclcpp::parameter::ParameterVariant::from_parameter(p));
|
||||
}
|
||||
auto results = node->set_parameters(pvariants);
|
||||
response->results = results;
|
||||
}
|
||||
);
|
||||
|
||||
set_parameters_atomically_service_ =
|
||||
node_->create_service<rcl_interfaces::srv::SetParametersAtomically>(
|
||||
node_->get_name() + "__set_parameters_atomically", [captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
std::vector<rclcpp::parameter::ParameterVariant> pvariants;
|
||||
std::transform(request->parameters.cbegin(), request->parameters.cend(),
|
||||
std::back_inserter(pvariants),
|
||||
[](const rcl_interfaces::msg::Parameter & p) {
|
||||
return rclcpp::parameter::ParameterVariant::
|
||||
from_parameter(p);
|
||||
});
|
||||
auto result = node->set_parameters_atomically(pvariants);
|
||||
response->result = result;
|
||||
}
|
||||
);
|
||||
|
||||
describe_parameters_service_ = node_->create_service<rcl_interfaces::srv::DescribeParameters>(
|
||||
node_->get_name() + "__describe_parameters", [captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
auto descriptors = node->describe_parameters(request->names);
|
||||
response->descriptors = descriptors;
|
||||
}
|
||||
);
|
||||
|
||||
list_parameters_service_ = node_->create_service<rcl_interfaces::srv::ListParameters>(
|
||||
node_->get_name() + "__list_parameters", [captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::ListParameters::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::ListParameters::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
auto result = node->list_parameters(request->prefixes, request->depth);
|
||||
response->result = result;
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
explicit ParameterService(
|
||||
const rclcpp::node::Node::SharedPtr node,
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
|
||||
private:
|
||||
const rclcpp::node::Node::SharedPtr node_;
|
||||
@@ -170,8 +58,7 @@ private:
|
||||
rclcpp::service::Service<rcl_interfaces::srv::ListParameters>::SharedPtr list_parameters_service_;
|
||||
};
|
||||
|
||||
} /* namespace parameter_service */
|
||||
} // namespace parameter_service
|
||||
} // namespace rclcpp
|
||||
|
||||
} /* namespace rclcpp */
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_PARAMETER_SERVICE_HPP_ */
|
||||
#endif // RCLCPP__PARAMETER_SERVICE_HPP_
|
||||
|
||||
@@ -18,114 +18,85 @@
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/publisher.h"
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
// Forward declaration for friend statement
|
||||
namespace node
|
||||
// Forward declaration is used for friend statement.
|
||||
namespace node_interfaces
|
||||
{
|
||||
class Node;
|
||||
} // namespace node
|
||||
class NodeTopicsInterface;
|
||||
}
|
||||
|
||||
namespace publisher
|
||||
{
|
||||
|
||||
class PublisherBase
|
||||
{
|
||||
friend rclcpp::node_interfaces::NodeTopicsInterface;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(PublisherBase)
|
||||
|
||||
/// Default constructor.
|
||||
/**
|
||||
* Typically, a publisher is not created through this method, but instead is created through a
|
||||
* call to `Node::create_publisher`.
|
||||
* \param[in] node_handle The corresponding rmw representation of the owner node.
|
||||
* \param[in] publisher_handle The rmw publisher handle corresponding to this publisher.
|
||||
* \param[in] node_base A pointer to the NodeBaseInterface for the parent node.
|
||||
* \param[in] topic The topic that this publisher publishes on.
|
||||
* \param[in] queue_size The maximum number of unpublished messages to queue.
|
||||
* \param[in] type_support The type support structure for the type to be published.
|
||||
* \param[in] publisher_options QoS settings for this publisher.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
PublisherBase(
|
||||
std::shared_ptr<rmw_node_t> node_handle,
|
||||
rmw_publisher_t * publisher_handle,
|
||||
std::string topic,
|
||||
size_t queue_size)
|
||||
: node_handle_(node_handle), publisher_handle_(publisher_handle),
|
||||
intra_process_publisher_handle_(nullptr),
|
||||
topic_(topic), queue_size_(queue_size),
|
||||
intra_process_publisher_id_(0), store_intra_process_message_(nullptr)
|
||||
{
|
||||
// Life time of this object is tied to the publisher handle.
|
||||
if (rmw_get_gid_for_publisher(publisher_handle_, &rmw_gid_) != RMW_RET_OK) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("failed to get publisher gid: ") + rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::string & topic,
|
||||
const rosidl_message_type_support_t & type_support,
|
||||
const rcl_publisher_options_t & publisher_options);
|
||||
|
||||
/// Default destructor.
|
||||
virtual ~PublisherBase()
|
||||
{
|
||||
if (intra_process_publisher_handle_) {
|
||||
if (rmw_destroy_publisher(node_handle_.get(), intra_process_publisher_handle_)) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Error in destruction of intra process rmw publisher handle: %s\n",
|
||||
rmw_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
if (publisher_handle_) {
|
||||
if (rmw_destroy_publisher(node_handle_.get(), publisher_handle_) != RMW_RET_OK) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Error in destruction of rmw publisher handle: %s\n",
|
||||
rmw_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~PublisherBase();
|
||||
|
||||
/// Get the topic that this publisher publishes on.
|
||||
// \return The topic name.
|
||||
const std::string &
|
||||
get_topic_name() const
|
||||
{
|
||||
return topic_;
|
||||
}
|
||||
/** \return The topic name. */
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
get_topic_name() const;
|
||||
|
||||
/// Get the queue size for this publisher.
|
||||
// \return The queue size.
|
||||
/** \return The queue size. */
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_queue_size() const
|
||||
{
|
||||
return queue_size_;
|
||||
}
|
||||
get_queue_size() const;
|
||||
|
||||
/// Get the global identifier for this publisher (used in rmw and by DDS).
|
||||
// \return The gid.
|
||||
/** \return The gid. */
|
||||
RCLCPP_PUBLIC
|
||||
const rmw_gid_t &
|
||||
get_gid() const
|
||||
{
|
||||
return rmw_gid_;
|
||||
}
|
||||
get_gid() const;
|
||||
|
||||
/// Get the global identifier for this publisher used by intra-process communication.
|
||||
// \return The intra-process gid.
|
||||
/** \return The intra-process gid. */
|
||||
RCLCPP_PUBLIC
|
||||
const rmw_gid_t &
|
||||
get_intra_process_gid() const
|
||||
{
|
||||
return intra_process_rmw_gid_;
|
||||
}
|
||||
get_intra_process_gid() const;
|
||||
|
||||
/// Compare this publisher to a gid.
|
||||
/**
|
||||
@@ -133,11 +104,9 @@ public:
|
||||
* \param[in] gid Reference to a gid.
|
||||
* \return True if the publisher's gid matches the input.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator==(const rmw_gid_t & gid) const
|
||||
{
|
||||
return *this == &gid;
|
||||
}
|
||||
operator==(const rmw_gid_t & gid) const;
|
||||
|
||||
/// Compare this publisher to a pointer gid.
|
||||
/**
|
||||
@@ -145,98 +114,69 @@ public:
|
||||
* \param[in] gid A pointer to a gid.
|
||||
* \return True if this publisher's gid matches the input.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
operator==(const rmw_gid_t * gid) const
|
||||
{
|
||||
bool result = false;
|
||||
auto ret = rmw_compare_gids_equal(gid, &this->get_gid(), &result);
|
||||
if (ret != RMW_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to compare gids: ") + rmw_get_error_string_safe());
|
||||
}
|
||||
if (!result) {
|
||||
ret = rmw_compare_gids_equal(gid, &this->get_intra_process_gid(), &result);
|
||||
if (ret != RMW_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("failed to compare gids: ") + rmw_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
operator==(const rmw_gid_t * gid) const;
|
||||
|
||||
typedef std::function<uint64_t(uint64_t, void *, const std::type_info &)> StoreMessageCallbackT;
|
||||
using StoreMessageCallbackT = std::function<uint64_t(uint64_t, void *, const std::type_info &)>;
|
||||
|
||||
protected:
|
||||
/// Implementation utility function used to setup intra process publishing after creation.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
setup_intra_process(
|
||||
uint64_t intra_process_publisher_id,
|
||||
StoreMessageCallbackT callback,
|
||||
rmw_publisher_t * intra_process_publisher_handle)
|
||||
{
|
||||
intra_process_publisher_id_ = intra_process_publisher_id;
|
||||
store_intra_process_message_ = callback;
|
||||
intra_process_publisher_handle_ = intra_process_publisher_handle;
|
||||
// Life time of this object is tied to the publisher handle.
|
||||
auto ret = rmw_get_gid_for_publisher(intra_process_publisher_handle_, &intra_process_rmw_gid_);
|
||||
if (ret != RMW_RET_OK) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("failed to create intra process publisher gid: ") +
|
||||
rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
const rcl_publisher_options_t & intra_process_options);
|
||||
|
||||
std::shared_ptr<rmw_node_t> node_handle_;
|
||||
protected:
|
||||
std::shared_ptr<rcl_node_t> rcl_node_handle_;
|
||||
|
||||
rmw_publisher_t * publisher_handle_;
|
||||
rmw_publisher_t * intra_process_publisher_handle_;
|
||||
|
||||
std::string topic_;
|
||||
size_t queue_size_;
|
||||
rcl_publisher_t publisher_handle_ = rcl_get_zero_initialized_publisher();
|
||||
rcl_publisher_t intra_process_publisher_handle_ = rcl_get_zero_initialized_publisher();
|
||||
|
||||
uint64_t intra_process_publisher_id_;
|
||||
StoreMessageCallbackT store_intra_process_message_;
|
||||
|
||||
rmw_gid_t rmw_gid_;
|
||||
rmw_gid_t intra_process_rmw_gid_;
|
||||
|
||||
std::mutex intra_process_publish_mutex_;
|
||||
};
|
||||
|
||||
/// A publisher publishes messages of any type to a topic.
|
||||
template<typename MessageT, typename Alloc = std::allocator<void>>
|
||||
class Publisher : public PublisherBase
|
||||
{
|
||||
friend rclcpp::node::Node;
|
||||
|
||||
public:
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
using MessageAlloc = typename MessageAllocTraits::allocator_type;
|
||||
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, Alloc>);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Publisher<MessageT, Alloc>)
|
||||
|
||||
Publisher(
|
||||
std::shared_ptr<rmw_node_t> node_handle,
|
||||
rmw_publisher_t * publisher_handle,
|
||||
std::string topic,
|
||||
size_t queue_size,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
: PublisherBase(node_handle, publisher_handle, topic, queue_size)
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::string & topic,
|
||||
const rcl_publisher_options_t & publisher_options,
|
||||
const std::shared_ptr<MessageAlloc> & allocator)
|
||||
: PublisherBase(
|
||||
node_base,
|
||||
topic,
|
||||
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
|
||||
publisher_options),
|
||||
message_allocator_(allocator)
|
||||
{
|
||||
message_allocator_ = std::make_shared<MessageAlloc>(*allocator.get());
|
||||
allocator::set_allocator_for_deleter(&message_deleter_, message_allocator_.get());
|
||||
}
|
||||
|
||||
virtual ~Publisher()
|
||||
{}
|
||||
|
||||
/// Send a message to the topic for this publisher.
|
||||
/**
|
||||
* This function is templated on the input message type, MessageT.
|
||||
* \param[in] msg A shared pointer to the message to send.
|
||||
*/
|
||||
void
|
||||
virtual void
|
||||
publish(std::unique_ptr<MessageT, MessageDeleter> & msg)
|
||||
{
|
||||
this->do_inter_process_publish(msg.get());
|
||||
@@ -250,20 +190,16 @@ public:
|
||||
// See: http://stackoverflow.com/questions/11002641/dynamic-casting-for-unique-ptr
|
||||
MessageT * msg_ptr = msg.get();
|
||||
msg.release();
|
||||
uint64_t message_seq;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(intra_process_publish_mutex_);
|
||||
message_seq =
|
||||
store_intra_process_message_(intra_process_publisher_id_, msg_ptr, typeid(MessageT));
|
||||
}
|
||||
uint64_t message_seq =
|
||||
store_intra_process_message_(intra_process_publisher_id_, msg_ptr, typeid(MessageT));
|
||||
rcl_interfaces::msg::IntraProcessMessage ipm;
|
||||
ipm.publisher_id = intra_process_publisher_id_;
|
||||
ipm.message_sequence = message_seq;
|
||||
auto status = rmw_publish(intra_process_publisher_handle_, &ipm);
|
||||
if (status != RMW_RET_OK) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
auto status = rcl_publish(&intra_process_publisher_handle_, &ipm);
|
||||
if (status != RCL_RET_OK) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("failed to publish intra process message: ") + rmw_get_error_string_safe());
|
||||
std::string("failed to publish intra process message: ") + rcl_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
} else {
|
||||
@@ -272,7 +208,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
virtual void
|
||||
publish(const std::shared_ptr<MessageT> & msg)
|
||||
{
|
||||
// Avoid allocating when not using intra process.
|
||||
@@ -291,7 +227,7 @@ public:
|
||||
return this->publish(unique_msg);
|
||||
}
|
||||
|
||||
void
|
||||
virtual void
|
||||
publish(std::shared_ptr<const MessageT> msg)
|
||||
{
|
||||
// Avoid allocating when not using intra process.
|
||||
@@ -310,7 +246,7 @@ public:
|
||||
return this->publish(unique_msg);
|
||||
}
|
||||
|
||||
void
|
||||
virtual void
|
||||
publish(const MessageT & msg)
|
||||
{
|
||||
// Avoid allocating when not using intra process.
|
||||
@@ -325,6 +261,15 @@ public:
|
||||
return this->publish(unique_msg);
|
||||
}
|
||||
|
||||
virtual void
|
||||
publish(const MessageT * msg)
|
||||
{
|
||||
if (!msg) {
|
||||
throw std::runtime_error("msg argument is nullptr");
|
||||
}
|
||||
return this->publish(*msg);
|
||||
}
|
||||
|
||||
std::shared_ptr<MessageAlloc> get_allocator() const
|
||||
{
|
||||
return message_allocator_;
|
||||
@@ -334,11 +279,11 @@ protected:
|
||||
void
|
||||
do_inter_process_publish(const MessageT * msg)
|
||||
{
|
||||
auto status = rmw_publish(publisher_handle_, msg);
|
||||
if (status != RMW_RET_OK) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
auto status = rcl_publish(&publisher_handle_, msg);
|
||||
if (status != RCL_RET_OK) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("failed to publish message: ") + rmw_get_error_string_safe());
|
||||
std::string("failed to publish message: ") + rcl_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
@@ -349,6 +294,7 @@ protected:
|
||||
};
|
||||
|
||||
} // namespace publisher
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__PUBLISHER_HPP_
|
||||
|
||||
148
rclcpp/include/rclcpp/publisher_factory.hpp
Normal file
148
rclcpp/include/rclcpp/publisher_factory.hpp
Normal 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_
|
||||
@@ -12,15 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_RATE_HPP_
|
||||
#define RCLCPP_RCLCPP_RATE_HPP_
|
||||
#ifndef RCLCPP__RATE_HPP_
|
||||
#define RCLCPP__RATE_HPP_
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
|
||||
#include <rclcpp/macros.hpp>
|
||||
#include <rclcpp/utilities.hpp>
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -30,10 +31,10 @@ namespace rate
|
||||
class RateBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(RateBase);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(RateBase)
|
||||
|
||||
virtual bool sleep() = 0;
|
||||
virtual bool is_steady() = 0;
|
||||
virtual bool is_steady() const = 0;
|
||||
virtual void reset() = 0;
|
||||
};
|
||||
|
||||
@@ -45,13 +46,13 @@ template<class Clock = std::chrono::high_resolution_clock>
|
||||
class GenericRate : public RateBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericRate)
|
||||
|
||||
GenericRate(double rate)
|
||||
explicit GenericRate(double rate)
|
||||
: GenericRate<Clock>(
|
||||
duration_cast<nanoseconds>(duration<double>(1.0 / rate)))
|
||||
{}
|
||||
GenericRate(std::chrono::nanoseconds period)
|
||||
explicit GenericRate(std::chrono::nanoseconds period)
|
||||
: period_(period), last_interval_(Clock::now())
|
||||
{}
|
||||
|
||||
@@ -88,7 +89,7 @@ public:
|
||||
}
|
||||
|
||||
virtual bool
|
||||
is_steady()
|
||||
is_steady() const
|
||||
{
|
||||
return Clock::is_steady;
|
||||
}
|
||||
@@ -105,17 +106,17 @@ public:
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(GenericRate);
|
||||
RCLCPP_DISABLE_COPY(GenericRate)
|
||||
|
||||
std::chrono::nanoseconds period_;
|
||||
std::chrono::time_point<Clock> last_interval_;
|
||||
|
||||
using ClockDurationNano = std::chrono::duration<typename Clock::rep, std::nano>;
|
||||
std::chrono::time_point<Clock, ClockDurationNano> last_interval_;
|
||||
};
|
||||
|
||||
using Rate = GenericRate<std::chrono::system_clock>;
|
||||
using WallRate = GenericRate<std::chrono::steady_clock>;
|
||||
|
||||
} // namespace rate
|
||||
} // namespace rclcpp
|
||||
} // namespace rate
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_RATE_HPP_ */
|
||||
#endif // RCLCPP__RATE_HPP_
|
||||
|
||||
@@ -12,58 +12,131 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_RCLCPP_HPP_
|
||||
#define RCLCPP_RCLCPP_RCLCPP_HPP_
|
||||
/** \mainpage rclcpp: ROS Client Library for C++
|
||||
*
|
||||
* `rclcpp` provides the canonical C++ API for interacting with ROS.
|
||||
* It consists of these main components:
|
||||
*
|
||||
* - Nodes
|
||||
* - rclcpp::node::Node
|
||||
* - rclcpp/node.hpp
|
||||
* - Publisher
|
||||
* - rclcpp::node::Node::create_publisher()
|
||||
* - rclcpp::publisher::Publisher
|
||||
* - rclcpp::publisher::Publisher::publish()
|
||||
* - rclcpp/publisher.hpp
|
||||
* - Subscription
|
||||
* - rclcpp::node::Node::create_subscription()
|
||||
* - rclcpp::subscription::Subscription
|
||||
* - rclcpp/subscription.hpp
|
||||
* - Service Client
|
||||
* - rclcpp::node::Node::create_client()
|
||||
* - rclcpp::client::Client
|
||||
* - rclcpp/client.hpp
|
||||
* - Service Server
|
||||
* - rclcpp::node::Node::create_service()
|
||||
* - rclcpp::service::Service
|
||||
* - rclcpp/service.hpp
|
||||
* - Timer
|
||||
* - rclcpp::node::Node::create_wall_timer()
|
||||
* - rclcpp::timer::WallTimer
|
||||
* - rclcpp::timer::TimerBase
|
||||
* - rclcpp/timer.hpp
|
||||
* - Parameters:
|
||||
* - rclcpp::node::Node::set_parameters()
|
||||
* - rclcpp::node::Node::get_parameters()
|
||||
* - rclcpp::node::Node::get_parameter()
|
||||
* - rclcpp::node::Node::describe_parameters()
|
||||
* - rclcpp::node::Node::list_parameters()
|
||||
* - rclcpp::node::Node::register_param_change_callback()
|
||||
* - rclcpp::parameter::ParameterVariant
|
||||
* - rclcpp::parameter_client::AsyncParametersClient
|
||||
* - rclcpp::parameter_client::SyncParametersClient
|
||||
* - rclcpp/parameter.hpp
|
||||
* - rclcpp/parameter_client.hpp
|
||||
* - rclcpp/parameter_service.hpp
|
||||
* - Rate:
|
||||
* - rclcpp::rate::Rate
|
||||
* - rclcpp::rate::WallRate
|
||||
* - rclcpp/rate.hpp
|
||||
*
|
||||
* There are also some components which help control the execution of callbacks:
|
||||
*
|
||||
* - Executors (responsible for execution of callbacks through a blocking spin):
|
||||
* - rclcpp::spin()
|
||||
* - rclcpp::spin_some()
|
||||
* - rclcpp::spin_until_future_complete()
|
||||
* - rclcpp::executors::single_threaded_executor::SingleThreadedExecutor
|
||||
* - rclcpp::executors::single_threaded_executor::SingleThreadedExecutor::add_node()
|
||||
* - rclcpp::executors::single_threaded_executor::SingleThreadedExecutor::spin()
|
||||
* - rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor
|
||||
* - rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor::add_node()
|
||||
* - rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor::spin()
|
||||
* - rclcpp/executor.hpp
|
||||
* - rclcpp/executors.hpp
|
||||
* - rclcpp/executors/single_threaded_executor.hpp
|
||||
* - rclcpp/executors/multi_threaded_executor.hpp
|
||||
* - CallbackGroups (mechanism for enforcing concurrency rules for callbacks):
|
||||
* - rclcpp::node::Node::create_callback_group()
|
||||
* - rclcpp::callback_group::CallbackGroup
|
||||
* - rclcpp/callback_group.hpp
|
||||
*
|
||||
* Additionally, there are some methods for introspecting the ROS graph:
|
||||
*
|
||||
* - Graph Events (a waitable event object that wakes up when the graph changes):
|
||||
* - rclcpp::node::Node::get_graph_event()
|
||||
* - rclcpp::node::Node::wait_for_graph_change()
|
||||
* - rclcpp::event::Event
|
||||
* - List topic names and types:
|
||||
* - rclcpp::node::Node::get_topic_names_and_types()
|
||||
* - Get the number of publishers or subscribers on a topic:
|
||||
* - rclcpp::node::Node::count_publishers()
|
||||
* - rclcpp::node::Node::count_subscribers()
|
||||
*
|
||||
* Finally, there are many internal API's and utilities:
|
||||
*
|
||||
* - Exceptions:
|
||||
* - rclcpp/exceptions.hpp
|
||||
* - Allocator related items:
|
||||
* - rclcpp/allocator/allocator_common.hpp
|
||||
* - rclcpp/allocator/allocator_deleter.hpp
|
||||
* - Memory management tools:
|
||||
* - rclcpp/memory_strategies.hpp
|
||||
* - rclcpp/memory_strategy.hpp
|
||||
* - rclcpp/message_memory_strategy.hpp
|
||||
* - rclcpp/strategies/allocator_memory_strategy.hpp
|
||||
* - rclcpp/strategies/message_pool_memory_strategy.hpp
|
||||
* - Context object which is shared amongst multiple Nodes:
|
||||
* - rclcpp::context::Context
|
||||
* - rclcpp/context.hpp
|
||||
* - rclcpp/contexts/default_context.hpp
|
||||
* - Various utilities:
|
||||
* - rclcpp/function_traits.hpp
|
||||
* - rclcpp/macros.hpp
|
||||
* - rclcpp/scope_exit.hpp
|
||||
* - rclcpp/time.hpp
|
||||
* - rclcpp/utilities.hpp
|
||||
* - rclcpp/visibility_control.hpp
|
||||
*/
|
||||
|
||||
#ifndef RCLCPP__RCLCPP_HPP_
|
||||
#define RCLCPP__RCLCPP_HPP_
|
||||
|
||||
#include <csignal>
|
||||
#include <memory>
|
||||
|
||||
#include <rclcpp/node.hpp>
|
||||
#include <rclcpp/parameter.hpp>
|
||||
#include <rclcpp/parameter_client.hpp>
|
||||
#include <rclcpp/parameter_service.hpp>
|
||||
#include <rclcpp/executors.hpp>
|
||||
#include <rclcpp/rate.hpp>
|
||||
#include <rclcpp/utilities.hpp>
|
||||
#include "rclcpp/executors.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/parameter_client.hpp"
|
||||
#include "rclcpp/parameter_service.hpp"
|
||||
#include "rclcpp/rate.hpp"
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
const std::chrono::seconds operator"" _s(unsigned long long s)
|
||||
{
|
||||
return std::chrono::seconds(s);
|
||||
}
|
||||
const std::chrono::nanoseconds operator"" _s(long double s)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::duration<long double>(s));
|
||||
}
|
||||
|
||||
const std::chrono::nanoseconds
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
operator"" _ms(unsigned long long ms)
|
||||
{
|
||||
return std::chrono::milliseconds(ms);
|
||||
}
|
||||
const std::chrono::nanoseconds
|
||||
operator"" _ms(long double ms)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::duration<long double, std::milli>(ms));
|
||||
}
|
||||
|
||||
const std::chrono::nanoseconds
|
||||
// NOLINTNEXTLINE(runtime/int)
|
||||
operator"" _ns(unsigned long long ns)
|
||||
{
|
||||
return std::chrono::nanoseconds(ns);
|
||||
}
|
||||
const std::chrono::nanoseconds
|
||||
operator"" _ns(long double ns)
|
||||
{
|
||||
return std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
std::chrono::duration<long double, std::nano>(ns));
|
||||
}
|
||||
|
||||
// Namespace escalations.
|
||||
// For example, this next line escalates type "rclcpp:node::Node" to "rclcpp::Node"
|
||||
@@ -82,34 +155,6 @@ using rclcpp::utilities::shutdown;
|
||||
using rclcpp::utilities::init;
|
||||
using rclcpp::utilities::sleep_for;
|
||||
|
||||
/// Create a default single-threaded executor and execute any immediately available work.
|
||||
// \param[in] node_ptr Shared pointer to the node to spin.
|
||||
void spin_some(Node::SharedPtr node_ptr)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.spin_node_some(node_ptr);
|
||||
}
|
||||
} // namespace rclcpp
|
||||
|
||||
/// Create a default single-threaded executor and spin the specified node.
|
||||
// \param[in] node_ptr Shared pointer to the node to spin.
|
||||
void spin(Node::SharedPtr node_ptr)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node_ptr);
|
||||
executor.spin();
|
||||
}
|
||||
|
||||
template<typename FutureT, typename TimeT = std::milli>
|
||||
rclcpp::executors::FutureReturnCode
|
||||
spin_until_future_complete(
|
||||
Node::SharedPtr node_ptr, std::shared_future<FutureT> & future,
|
||||
std::chrono::duration<int64_t, TimeT> timeout = std::chrono::duration<int64_t, TimeT>(-1))
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
return rclcpp::executors::spin_node_until_future_complete<FutureT>(
|
||||
executor, node_ptr, future, timeout);
|
||||
}
|
||||
|
||||
} /* namespace rclcpp */
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_RCLCPP_HPP_ */
|
||||
#endif // RCLCPP__RCLCPP_HPP_
|
||||
|
||||
52
rclcpp/include/rclcpp/scope_exit.hpp
Normal file
52
rclcpp/include/rclcpp/scope_exit.hpp
Normal file
@@ -0,0 +1,52 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Based on: http://the-witness.net/news/2012/11/scopeexit-in-c11/
|
||||
// But I changed the lambda to include by reference rather than value, see:
|
||||
// http://the-witness.net/news/2012/11/scopeexit-in-c11/comment-page-1/#comment-86873
|
||||
|
||||
#ifndef RCLCPP__SCOPE_EXIT_HPP_
|
||||
#define RCLCPP__SCOPE_EXIT_HPP_
|
||||
|
||||
#include <functional>
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
template<typename Callable>
|
||||
struct ScopeExit
|
||||
{
|
||||
explicit ScopeExit(Callable callable)
|
||||
: callable_(callable) {}
|
||||
~ScopeExit() {callable_(); }
|
||||
|
||||
private:
|
||||
Callable callable_;
|
||||
};
|
||||
|
||||
template<typename Callable>
|
||||
ScopeExit<Callable>
|
||||
make_scope_exit(Callable callable)
|
||||
{
|
||||
return ScopeExit<Callable>(callable);
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#define RCLCPP_SCOPE_EXIT(code) \
|
||||
auto RCLCPP_STRING_JOIN(scope_exit_, __LINE__) = rclcpp::make_scope_exit([&]() {code; })
|
||||
|
||||
#endif // RCLCPP__SCOPE_EXIT_HPP_
|
||||
@@ -12,8 +12,8 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_SERVICE_HPP_
|
||||
#define RCLCPP_RCLCPP_SERVICE_HPP_
|
||||
#ifndef RCLCPP__SERVICE_HPP_
|
||||
#define RCLCPP__SERVICE_HPP_
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
@@ -21,70 +21,69 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/service.h"
|
||||
|
||||
#include <rclcpp/macros.hpp>
|
||||
#include <rclcpp/any_service_callback.hpp>
|
||||
#include "rclcpp/any_service_callback.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace service
|
||||
{
|
||||
|
||||
class ServiceBase
|
||||
{
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ServiceBase);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(ServiceBase)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
ServiceBase(
|
||||
std::shared_ptr<rmw_node_t> node_handle,
|
||||
rmw_service_t * service_handle,
|
||||
const std::string service_name)
|
||||
: node_handle_(node_handle), service_handle_(service_handle), service_name_(service_name)
|
||||
{}
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const std::string & service_name);
|
||||
|
||||
virtual ~ServiceBase()
|
||||
{
|
||||
if (service_handle_) {
|
||||
if (rmw_destroy_service(service_handle_) != RMW_RET_OK) {
|
||||
std::stringstream ss;
|
||||
ss << "Error in destruction of rmw service_handle_ handle: " <<
|
||||
rmw_get_error_string_safe() << '\n';
|
||||
(std::cerr << ss.str()).flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
explicit ServiceBase(
|
||||
std::shared_ptr<rcl_node_t> node_handle);
|
||||
|
||||
std::string get_service_name()
|
||||
{
|
||||
return this->service_name_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~ServiceBase();
|
||||
|
||||
const rmw_service_t * get_service_handle()
|
||||
{
|
||||
return this->service_handle_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
get_service_name();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_service_t *
|
||||
get_service_handle();
|
||||
|
||||
virtual std::shared_ptr<void> create_request() = 0;
|
||||
virtual std::shared_ptr<void> create_request_header() = 0;
|
||||
virtual std::shared_ptr<rmw_request_id_t> create_request_header() = 0;
|
||||
virtual void handle_request(
|
||||
std::shared_ptr<void> request_header,
|
||||
std::shared_ptr<rmw_request_id_t> request_header,
|
||||
std::shared_ptr<void> request) = 0;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(ServiceBase);
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(ServiceBase)
|
||||
|
||||
std::shared_ptr<rmw_node_t> node_handle_;
|
||||
RCLCPP_PUBLIC
|
||||
rcl_node_t *
|
||||
get_rcl_node_handle() const;
|
||||
|
||||
rmw_service_t * service_handle_;
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
|
||||
rcl_service_t * service_handle_ = nullptr;
|
||||
std::string service_name_;
|
||||
|
||||
bool owns_rcl_handle_ = true;
|
||||
};
|
||||
|
||||
using namespace any_service_callback;
|
||||
using any_service_callback::AnyServiceCallback;
|
||||
|
||||
template<typename ServiceT>
|
||||
class Service : public ServiceBase
|
||||
@@ -100,59 +99,121 @@ public:
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<typename ServiceT::Request>,
|
||||
std::shared_ptr<typename ServiceT::Response>)>;
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Service);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Service)
|
||||
|
||||
Service(
|
||||
std::shared_ptr<rmw_node_t> node_handle,
|
||||
rmw_service_t * service_handle,
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const std::string & service_name,
|
||||
AnyServiceCallback<ServiceT> any_callback,
|
||||
rcl_service_options_t & service_options)
|
||||
: ServiceBase(node_handle, service_name), any_callback_(any_callback)
|
||||
{
|
||||
using rosidl_typesupport_cpp::get_service_type_support_handle;
|
||||
auto service_type_support_handle = get_service_type_support_handle<ServiceT>();
|
||||
|
||||
// rcl does the static memory allocation here
|
||||
service_handle_ = new rcl_service_t;
|
||||
*service_handle_ = rcl_get_zero_initialized_service();
|
||||
|
||||
rcl_ret_t ret = rcl_service_init(
|
||||
service_handle_,
|
||||
node_handle.get(),
|
||||
service_type_support_handle,
|
||||
service_name.c_str(),
|
||||
&service_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
if (ret == RCL_RET_SERVICE_NAME_INVALID) {
|
||||
auto rcl_node_handle = get_rcl_node_handle();
|
||||
// this will throw on any validation problem
|
||||
rcl_reset_error();
|
||||
expand_topic_or_service_name(
|
||||
service_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle),
|
||||
true);
|
||||
}
|
||||
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create service");
|
||||
}
|
||||
}
|
||||
|
||||
Service(
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
rcl_service_t * service_handle,
|
||||
AnyServiceCallback<ServiceT> any_callback)
|
||||
: ServiceBase(node_handle, service_handle, service_name), any_callback_(any_callback)
|
||||
{}
|
||||
: ServiceBase(node_handle),
|
||||
any_callback_(any_callback)
|
||||
{
|
||||
// check if service handle was initialized
|
||||
// TODO(karsten1987): Take this verification
|
||||
// directly in rcl_*_t
|
||||
// see: https://github.com/ros2/rcl/issues/81
|
||||
if (!service_handle->impl) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("rcl_service_t in constructor argument must be initialized beforehand."));
|
||||
// *INDENT-ON*
|
||||
}
|
||||
service_handle_ = service_handle;
|
||||
service_name_ = std::string(rcl_service_get_service_name(service_handle));
|
||||
owns_rcl_handle_ = false;
|
||||
}
|
||||
|
||||
Service() = delete;
|
||||
|
||||
virtual ~Service()
|
||||
{
|
||||
// check if you have ownership of the handle
|
||||
if (owns_rcl_handle_) {
|
||||
if (rcl_service_fini(service_handle_, node_handle_.get()) != RCL_RET_OK) {
|
||||
std::stringstream ss;
|
||||
ss << "Error in destruction of rcl service_handle_ handle: " <<
|
||||
rcl_get_error_string_safe() << '\n';
|
||||
(std::cerr << ss.str()).flush();
|
||||
rcl_reset_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<void> create_request()
|
||||
{
|
||||
return std::shared_ptr<void>(new typename ServiceT::Request());
|
||||
}
|
||||
|
||||
std::shared_ptr<void> create_request_header()
|
||||
std::shared_ptr<rmw_request_id_t> create_request_header()
|
||||
{
|
||||
// TODO(wjwwood): This should probably use rmw_request_id's allocator.
|
||||
// (since it is a C type)
|
||||
return std::shared_ptr<void>(new rmw_request_id_t);
|
||||
return std::shared_ptr<rmw_request_id_t>(new rmw_request_id_t);
|
||||
}
|
||||
|
||||
void handle_request(std::shared_ptr<void> request_header, std::shared_ptr<void> request)
|
||||
void handle_request(std::shared_ptr<rmw_request_id_t> request_header,
|
||||
std::shared_ptr<void> request)
|
||||
{
|
||||
auto typed_request = std::static_pointer_cast<typename ServiceT::Request>(request);
|
||||
auto typed_request_header = std::static_pointer_cast<rmw_request_id_t>(request_header);
|
||||
auto response = std::shared_ptr<typename ServiceT::Response>(new typename ServiceT::Response);
|
||||
any_callback_.dispatch(typed_request_header, typed_request, response);
|
||||
send_response(typed_request_header, response);
|
||||
any_callback_.dispatch(request_header, typed_request, response);
|
||||
send_response(request_header, response);
|
||||
}
|
||||
|
||||
void send_response(
|
||||
std::shared_ptr<rmw_request_id_t> req_id,
|
||||
std::shared_ptr<typename ServiceT::Response> response)
|
||||
{
|
||||
rmw_ret_t status = rmw_send_response(get_service_handle(), req_id.get(), response.get());
|
||||
if (status != RMW_RET_OK) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("failed to send response: ") + rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
rcl_ret_t status = rcl_send_response(get_service_handle(), req_id.get(), response.get());
|
||||
|
||||
if (status != RCL_RET_OK) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(status, "failed to send response");
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Service);
|
||||
RCLCPP_DISABLE_COPY(Service)
|
||||
|
||||
AnyServiceCallback<ServiceT> any_callback_;
|
||||
};
|
||||
|
||||
} /* namespace service */
|
||||
} /* namespace rclcpp */
|
||||
} // namespace service
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_SERVICE_HPP_ */
|
||||
#endif // RCLCPP__SERVICE_HPP_
|
||||
|
||||
@@ -18,16 +18,19 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/allocator.h"
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rmw/types.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace memory_strategies
|
||||
{
|
||||
|
||||
namespace allocator_memory_strategy
|
||||
{
|
||||
|
||||
@@ -41,7 +44,7 @@ template<typename Alloc = std::allocator<void>>
|
||||
class AllocatorMemoryStrategy : public memory_strategy::MemoryStrategy
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(AllocatorMemoryStrategy<Alloc>);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(AllocatorMemoryStrategy<Alloc>)
|
||||
|
||||
using ExecAllocTraits = allocator::AllocRebind<executor::AnyExecutable, Alloc>;
|
||||
using ExecAlloc = typename ExecAllocTraits::allocator_type;
|
||||
@@ -49,8 +52,6 @@ public:
|
||||
using VoidAllocTraits = typename allocator::AllocRebind<void *, Alloc>;
|
||||
using VoidAlloc = typename VoidAllocTraits::allocator_type;
|
||||
|
||||
using WeakNodeVector = std::vector<std::weak_ptr<node::Node>>;
|
||||
|
||||
explicit AllocatorMemoryStrategy(std::shared_ptr<Alloc> allocator)
|
||||
{
|
||||
executable_allocator_ = std::make_shared<ExecAlloc>(*allocator.get());
|
||||
@@ -63,81 +64,76 @@ public:
|
||||
allocator_ = std::make_shared<VoidAlloc>();
|
||||
}
|
||||
|
||||
size_t fill_subscriber_handles(void ** & ptr)
|
||||
void add_guard_condition(const rcl_guard_condition_t * guard_condition)
|
||||
{
|
||||
for (auto & subscription : subscriptions_) {
|
||||
subscriber_handles_.push_back(subscription->get_subscription_handle()->data);
|
||||
if (subscription->get_intra_process_subscription_handle()) {
|
||||
subscriber_handles_.push_back(subscription->get_intra_process_subscription_handle()->data);
|
||||
for (const auto & existing_guard_condition : guard_conditions_) {
|
||||
if (existing_guard_condition == guard_condition) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
ptr = subscriber_handles_.data();
|
||||
return subscriber_handles_.size();
|
||||
guard_conditions_.push_back(guard_condition);
|
||||
}
|
||||
|
||||
// return the new number of services
|
||||
size_t fill_service_handles(void ** & ptr)
|
||||
void remove_guard_condition(const rcl_guard_condition_t * guard_condition)
|
||||
{
|
||||
for (auto & service : services_) {
|
||||
service_handles_.push_back(service->get_service_handle()->data);
|
||||
for (auto it = guard_conditions_.begin(); it != guard_conditions_.end(); ++it) {
|
||||
if (*it == guard_condition) {
|
||||
guard_conditions_.erase(it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
ptr = service_handles_.data();
|
||||
return service_handles_.size();
|
||||
}
|
||||
|
||||
// return the new number of clients
|
||||
size_t fill_client_handles(void ** & ptr)
|
||||
{
|
||||
for (auto & client : clients_) {
|
||||
client_handles_.push_back(client->get_client_handle()->data);
|
||||
}
|
||||
ptr = client_handles_.data();
|
||||
return client_handles_.size();
|
||||
}
|
||||
|
||||
void clear_active_entities()
|
||||
{
|
||||
subscriptions_.clear();
|
||||
services_.clear();
|
||||
clients_.clear();
|
||||
}
|
||||
|
||||
void clear_handles()
|
||||
{
|
||||
subscriber_handles_.clear();
|
||||
subscription_handles_.clear();
|
||||
service_handles_.clear();
|
||||
client_handles_.clear();
|
||||
timer_handles_.clear();
|
||||
}
|
||||
|
||||
void revalidate_handles()
|
||||
virtual void remove_null_handles(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
{
|
||||
VectorRebind<void *> temp;
|
||||
for (auto & subscriber_handle : subscriber_handles_) {
|
||||
if (subscriber_handle) {
|
||||
temp.push_back(subscriber_handle);
|
||||
}
|
||||
for (size_t i = 0; i < wait_set->size_of_subscriptions; ++i) {
|
||||
if (!wait_set->subscriptions[i]) {
|
||||
subscription_handles_[i] = nullptr;
|
||||
}
|
||||
subscriber_handles_.swap(temp);
|
||||
}
|
||||
{
|
||||
VectorRebind<void *> temp;
|
||||
for (auto & service_handle : service_handles_) {
|
||||
if (service_handle) {
|
||||
temp.push_back(service_handle);
|
||||
}
|
||||
for (size_t i = 0; i < wait_set->size_of_services; ++i) {
|
||||
if (!wait_set->services[i]) {
|
||||
service_handles_[i] = nullptr;
|
||||
}
|
||||
service_handles_.swap(temp);
|
||||
}
|
||||
{
|
||||
VectorRebind<void *> temp;
|
||||
for (auto & client_handle : client_handles_) {
|
||||
if (client_handle) {
|
||||
temp.push_back(client_handle);
|
||||
}
|
||||
for (size_t i = 0; i < wait_set->size_of_clients; ++i) {
|
||||
if (!wait_set->clients[i]) {
|
||||
client_handles_[i] = nullptr;
|
||||
}
|
||||
client_handles_.swap(temp);
|
||||
}
|
||||
for (size_t i = 0; i < wait_set->size_of_timers; ++i) {
|
||||
if (!wait_set->timers[i]) {
|
||||
timer_handles_[i] = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
subscription_handles_.erase(
|
||||
std::remove(subscription_handles_.begin(), subscription_handles_.end(), nullptr),
|
||||
subscription_handles_.end()
|
||||
);
|
||||
|
||||
service_handles_.erase(
|
||||
std::remove(service_handles_.begin(), service_handles_.end(), nullptr),
|
||||
service_handles_.end()
|
||||
);
|
||||
|
||||
client_handles_.erase(
|
||||
std::remove(client_handles_.begin(), client_handles_.end(), nullptr),
|
||||
client_handles_.end()
|
||||
);
|
||||
|
||||
timer_handles_.erase(
|
||||
std::remove(timer_handles_.begin(), timer_handles_.end(), nullptr),
|
||||
timer_handles_.end()
|
||||
);
|
||||
}
|
||||
|
||||
bool collect_entities(const WeakNodeVector & weak_nodes)
|
||||
@@ -146,7 +142,7 @@ public:
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
has_invalid_weak_nodes = false;
|
||||
has_invalid_weak_nodes = true;
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
@@ -157,17 +153,29 @@ public:
|
||||
for (auto & weak_subscription : group->get_subscription_ptrs()) {
|
||||
auto subscription = weak_subscription.lock();
|
||||
if (subscription) {
|
||||
subscriptions_.push_back(subscription);
|
||||
subscription_handles_.push_back(subscription->get_subscription_handle());
|
||||
if (subscription->get_intra_process_subscription_handle()) {
|
||||
subscription_handles_.push_back(
|
||||
subscription->get_intra_process_subscription_handle());
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto & service : group->get_service_ptrs()) {
|
||||
for (auto & weak_service : group->get_service_ptrs()) {
|
||||
auto service = weak_service.lock();
|
||||
if (service) {
|
||||
services_.push_back(service);
|
||||
service_handles_.push_back(service->get_service_handle());
|
||||
}
|
||||
}
|
||||
for (auto & client : group->get_client_ptrs()) {
|
||||
for (auto & weak_client : group->get_client_ptrs()) {
|
||||
auto client = weak_client.lock();
|
||||
if (client) {
|
||||
clients_.push_back(client);
|
||||
client_handles_.push_back(client->get_client_handle());
|
||||
}
|
||||
}
|
||||
for (auto & weak_timer : group->get_timer_ptrs()) {
|
||||
auto timer = weak_timer.lock();
|
||||
if (timer) {
|
||||
timer_handles_.push_back(timer->get_timer_handle());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -175,6 +183,45 @@ public:
|
||||
return has_invalid_weak_nodes;
|
||||
}
|
||||
|
||||
bool add_handles_to_waitset(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
for (auto subscription : subscription_handles_) {
|
||||
if (rcl_wait_set_add_subscription(wait_set, subscription) != RCL_RET_OK) {
|
||||
fprintf(stderr, "Couldn't add subscription to waitset: %s\n", rcl_get_error_string_safe());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto client : client_handles_) {
|
||||
if (rcl_wait_set_add_client(wait_set, client) != RCL_RET_OK) {
|
||||
fprintf(stderr, "Couldn't add client to waitset: %s\n", rcl_get_error_string_safe());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto service : service_handles_) {
|
||||
if (rcl_wait_set_add_service(wait_set, service) != RCL_RET_OK) {
|
||||
fprintf(stderr, "Couldn't add service to waitset: %s\n", rcl_get_error_string_safe());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto timer : timer_handles_) {
|
||||
if (rcl_wait_set_add_timer(wait_set, timer) != RCL_RET_OK) {
|
||||
fprintf(stderr, "Couldn't add timer to waitset: %s\n", rcl_get_error_string_safe());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto guard_condition : guard_conditions_) {
|
||||
if (rcl_wait_set_add_guard_condition(wait_set, guard_condition) != RCL_RET_OK) {
|
||||
fprintf(stderr, "Couldn't add guard_condition to waitset: %s\n",
|
||||
rcl_get_error_string_safe());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/// Provide a newly initialized AnyExecutable object.
|
||||
// \return Shared pointer to the fresh executable.
|
||||
@@ -187,21 +234,21 @@ public:
|
||||
get_next_subscription(executor::AnyExecutable::SharedPtr any_exec,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
auto it = subscriber_handles_.begin();
|
||||
while (it != subscriber_handles_.end()) {
|
||||
auto it = subscription_handles_.begin();
|
||||
while (it != subscription_handles_.end()) {
|
||||
auto subscription = get_subscription_by_handle(*it, weak_nodes);
|
||||
if (subscription) {
|
||||
// Figure out if this is for intra-process or not.
|
||||
bool is_intra_process = false;
|
||||
if (subscription->get_intra_process_subscription_handle()) {
|
||||
is_intra_process = subscription->get_intra_process_subscription_handle()->data == *it;
|
||||
is_intra_process = subscription->get_intra_process_subscription_handle() == *it;
|
||||
}
|
||||
// Find the group for this handle and see if it can be serviced
|
||||
auto group = get_group_by_subscription(subscription, weak_nodes);
|
||||
if (!group) {
|
||||
// Group was not found, meaning the subscription is not valid...
|
||||
// Remove it from the ready list and continue looking
|
||||
subscriber_handles_.erase(it);
|
||||
subscription_handles_.erase(it);
|
||||
continue;
|
||||
}
|
||||
if (!group->can_be_taken_from().load()) {
|
||||
@@ -217,12 +264,12 @@ public:
|
||||
any_exec->subscription = subscription;
|
||||
}
|
||||
any_exec->callback_group = group;
|
||||
any_exec->node = get_node_by_group(group, weak_nodes);
|
||||
subscriber_handles_.erase(it);
|
||||
any_exec->node_base = get_node_by_group(group, weak_nodes);
|
||||
subscription_handles_.erase(it);
|
||||
return;
|
||||
}
|
||||
// Else, the subscription is no longer valid, remove it and continue
|
||||
subscriber_handles_.erase(it);
|
||||
subscription_handles_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -251,7 +298,7 @@ public:
|
||||
// Otherwise it is safe to set and return the any_exec
|
||||
any_exec->service = service;
|
||||
any_exec->callback_group = group;
|
||||
any_exec->node = get_node_by_group(group, weak_nodes);
|
||||
any_exec->node_base = get_node_by_group(group, weak_nodes);
|
||||
service_handles_.erase(it);
|
||||
return;
|
||||
}
|
||||
@@ -284,7 +331,7 @@ public:
|
||||
// Otherwise it is safe to set and return the any_exec
|
||||
any_exec->client = client;
|
||||
any_exec->callback_group = group;
|
||||
any_exec->node = get_node_by_group(group, weak_nodes);
|
||||
any_exec->node_base = get_node_by_group(group, weak_nodes);
|
||||
client_handles_.erase(it);
|
||||
return;
|
||||
}
|
||||
@@ -293,18 +340,47 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual rcl_allocator_t get_allocator()
|
||||
{
|
||||
return rclcpp::allocator::get_rcl_allocator<void *, VoidAlloc>(*allocator_.get());
|
||||
}
|
||||
|
||||
size_t number_of_ready_subscriptions() const
|
||||
{
|
||||
return subscription_handles_.size();
|
||||
}
|
||||
|
||||
size_t number_of_ready_services() const
|
||||
{
|
||||
return service_handles_.size();
|
||||
}
|
||||
|
||||
size_t number_of_ready_clients() const
|
||||
{
|
||||
return client_handles_.size();
|
||||
}
|
||||
|
||||
size_t number_of_guard_conditions() const
|
||||
{
|
||||
return guard_conditions_.size();
|
||||
}
|
||||
|
||||
size_t number_of_ready_timers() const
|
||||
{
|
||||
return timer_handles_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
template<typename T>
|
||||
using VectorRebind =
|
||||
std::vector<T, typename std::allocator_traits<Alloc>::template rebind_alloc<T>>;
|
||||
|
||||
VectorRebind<rclcpp::subscription::SubscriptionBase::SharedPtr> subscriptions_;
|
||||
VectorRebind<rclcpp::service::ServiceBase::SharedPtr> services_;
|
||||
VectorRebind<rclcpp::client::ClientBase::SharedPtr> clients_;
|
||||
VectorRebind<const rcl_guard_condition_t *> guard_conditions_;
|
||||
|
||||
VectorRebind<void *> subscriber_handles_;
|
||||
VectorRebind<void *> service_handles_;
|
||||
VectorRebind<void *> client_handles_;
|
||||
VectorRebind<const rcl_subscription_t *> subscription_handles_;
|
||||
VectorRebind<const rcl_service_t *> service_handles_;
|
||||
VectorRebind<const rcl_client_t *> client_handles_;
|
||||
VectorRebind<const rcl_timer_t *> timer_handles_;
|
||||
|
||||
std::shared_ptr<ExecAlloc> executable_allocator_;
|
||||
std::shared_ptr<VoidAlloc> allocator_;
|
||||
@@ -312,7 +388,6 @@ private:
|
||||
|
||||
} // namespace allocator_memory_strategy
|
||||
} // namespace memory_strategies
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__STRATEGIES__ALLOCATOR_MEMORY_STRATEGY_HPP_
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
#ifndef RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_
|
||||
#define RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_
|
||||
|
||||
#include <rclcpp/macros.hpp>
|
||||
#include <rclcpp/message_memory_strategy.hpp>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/message_memory_strategy.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -32,14 +35,18 @@ namespace message_pool_memory_strategy
|
||||
* The size of the message pool should be at least the largest number of concurrent accesses to
|
||||
* the subscription (usually the number of threads).
|
||||
*/
|
||||
template<typename MessageT, size_t Size,
|
||||
typename std::enable_if<rosidl_generator_traits::has_fixed_size<MessageT>::value>::type * =
|
||||
nullptr>
|
||||
template<
|
||||
typename MessageT,
|
||||
size_t Size,
|
||||
typename std::enable_if<
|
||||
rosidl_generator_traits::has_fixed_size<MessageT>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
class MessagePoolMemoryStrategy
|
||||
: public message_memory_strategy::MessageMemoryStrategy<MessageT>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(MessagePoolMemoryStrategy);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(MessagePoolMemoryStrategy)
|
||||
|
||||
/// Default constructor
|
||||
MessagePoolMemoryStrategy()
|
||||
@@ -101,4 +108,5 @@ protected:
|
||||
} // namespace message_pool_memory_strategy
|
||||
} // namespace strategies
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__STRATEGIES__MESSAGE_POOL_MEMORY_STRATEGY_HPP_
|
||||
|
||||
@@ -24,19 +24,26 @@
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/subscription.h"
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/message_memory_strategy.hpp"
|
||||
#include "rclcpp/any_subscription_callback.hpp"
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace node
|
||||
namespace node_interfaces
|
||||
{
|
||||
class Node;
|
||||
} // namespace node
|
||||
class NodeTopicsInterface;
|
||||
} // namespace node_interfaces
|
||||
|
||||
namespace subscription
|
||||
{
|
||||
@@ -46,107 +53,77 @@ namespace subscription
|
||||
class SubscriptionBase
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(SubscriptionBase)
|
||||
|
||||
/// Default constructor.
|
||||
/**
|
||||
* \param[in] node_handle The rmw representation of the node that owns this subscription.
|
||||
* \param[in] node_handle The rcl representation of the node that owns this subscription.
|
||||
* \param[in] type_support_handle rosidl type support struct, for the Message type of the topic.
|
||||
* \param[in] topic_name Name of the topic to subscribe to.
|
||||
* \param[in] ignore_local_publications True to ignore local publications (unused).
|
||||
* \param[in] subscription_options options for the subscription.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
SubscriptionBase(
|
||||
std::shared_ptr<rmw_node_t> node_handle,
|
||||
rmw_subscription_t * subscription_handle,
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const rosidl_message_type_support_t & type_support_handle,
|
||||
const std::string & topic_name,
|
||||
bool ignore_local_publications)
|
||||
: intra_process_subscription_handle_(nullptr),
|
||||
node_handle_(node_handle),
|
||||
subscription_handle_(subscription_handle),
|
||||
topic_name_(topic_name),
|
||||
ignore_local_publications_(ignore_local_publications)
|
||||
{
|
||||
// To avoid unused private member warnings.
|
||||
(void)ignore_local_publications_;
|
||||
}
|
||||
const rcl_subscription_options_t & subscription_options);
|
||||
|
||||
/// Default destructor.
|
||||
virtual ~SubscriptionBase()
|
||||
{
|
||||
if (subscription_handle_) {
|
||||
if (rmw_destroy_subscription(node_handle_.get(), subscription_handle_) != RMW_RET_OK) {
|
||||
std::stringstream ss;
|
||||
ss << "Error in destruction of rmw subscription handle: " <<
|
||||
rmw_get_error_string_safe() << '\n';
|
||||
(std::cerr << ss.str()).flush();
|
||||
}
|
||||
}
|
||||
if (intra_process_subscription_handle_) {
|
||||
auto ret = rmw_destroy_subscription(node_handle_.get(), intra_process_subscription_handle_);
|
||||
if (ret != RMW_RET_OK) {
|
||||
std::stringstream ss;
|
||||
ss << "Error in destruction of rmw intra process subscription handle: " <<
|
||||
rmw_get_error_string_safe() << '\n';
|
||||
(std::cerr << ss.str()).flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~SubscriptionBase();
|
||||
|
||||
/// Get the topic that this subscription is subscribed on.
|
||||
const std::string & get_topic_name() const
|
||||
{
|
||||
return this->topic_name_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
get_topic_name() const;
|
||||
|
||||
const rmw_subscription_t * get_subscription_handle() const
|
||||
{
|
||||
return subscription_handle_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_subscription_t *
|
||||
get_subscription_handle() const;
|
||||
|
||||
const rmw_subscription_t * get_intra_process_subscription_handle() const
|
||||
{
|
||||
return intra_process_subscription_handle_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
virtual const rcl_subscription_t *
|
||||
get_intra_process_subscription_handle() const;
|
||||
|
||||
/// Borrow a new message.
|
||||
// \return Shared pointer to the fresh message.
|
||||
virtual std::shared_ptr<void> create_message() = 0;
|
||||
/** \return Shared pointer to the fresh message. */
|
||||
virtual std::shared_ptr<void>
|
||||
create_message() = 0;
|
||||
/// Check if we need to handle the message, and execute the callback if we do.
|
||||
/**
|
||||
* \param[in] message Shared pointer to the message to handle.
|
||||
* \param[in] message_info Metadata associated with this message.
|
||||
*/
|
||||
virtual void handle_message(
|
||||
std::shared_ptr<void> & message,
|
||||
const rmw_message_info_t & message_info) = 0;
|
||||
virtual void
|
||||
handle_message(std::shared_ptr<void> & message, const rmw_message_info_t & message_info) = 0;
|
||||
|
||||
/// Return the message borrowed in create_message.
|
||||
// \param[in] Shared pointer to the returned message.
|
||||
virtual void return_message(std::shared_ptr<void> & message) = 0;
|
||||
virtual void handle_intra_process_message(
|
||||
/** \param[in] message Shared pointer to the returned message. */
|
||||
virtual void
|
||||
return_message(std::shared_ptr<void> & message) = 0;
|
||||
|
||||
virtual void
|
||||
handle_intra_process_message(
|
||||
rcl_interfaces::msg::IntraProcessMessage & ipm,
|
||||
const rmw_message_info_t & message_info) = 0;
|
||||
|
||||
protected:
|
||||
rmw_subscription_t * intra_process_subscription_handle_;
|
||||
rcl_subscription_t intra_process_subscription_handle_ = rcl_get_zero_initialized_subscription();
|
||||
rcl_subscription_t subscription_handle_ = rcl_get_zero_initialized_subscription();
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(SubscriptionBase);
|
||||
|
||||
std::shared_ptr<rmw_node_t> node_handle_;
|
||||
|
||||
rmw_subscription_t * subscription_handle_;
|
||||
|
||||
std::string topic_name_;
|
||||
bool ignore_local_publications_;
|
||||
RCLCPP_DISABLE_COPY(SubscriptionBase)
|
||||
};
|
||||
|
||||
using namespace any_subscription_callback;
|
||||
using any_subscription_callback::AnySubscriptionCallback;
|
||||
|
||||
/// Subscription implementation, templated on the type of message this subscription receives.
|
||||
template<typename MessageT, typename Alloc = std::allocator<void>>
|
||||
class Subscription : public SubscriptionBase
|
||||
{
|
||||
friend class rclcpp::node::Node;
|
||||
friend class rclcpp::node_interfaces::NodeTopicsInterface;
|
||||
|
||||
public:
|
||||
using MessageAllocTraits = allocator::AllocRebind<MessageT, Alloc>;
|
||||
@@ -154,34 +131,36 @@ public:
|
||||
using MessageDeleter = allocator::Deleter<MessageAlloc, MessageT>;
|
||||
using MessageUniquePtr = std::unique_ptr<MessageT, MessageDeleter>;
|
||||
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Subscription);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Subscription)
|
||||
|
||||
/// Default constructor.
|
||||
/**
|
||||
* The constructor for a subscription is almost never called directly. Instead, subscriptions
|
||||
* should be instantiated through Node::create_subscription.
|
||||
* \param[in] node_handle rmw representation of the node that owns this subscription.
|
||||
* \param[in] node_handle rcl representation of the node that owns this subscription.
|
||||
* \param[in] topic_name Name of the topic to subscribe to.
|
||||
* \param[in] ignore_local_publications True to ignore local publications (unused).
|
||||
* \param[in] callback User-defined callback to call when a message is received.
|
||||
* \param[in] subscription_options options for the subscription.
|
||||
* \param[in] callback User defined callback to call when a message is received.
|
||||
* \param[in] memory_strategy The memory strategy to be used for managing message memory.
|
||||
*/
|
||||
Subscription(
|
||||
std::shared_ptr<rmw_node_t> node_handle,
|
||||
rmw_subscription_t * subscription_handle,
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const std::string & topic_name,
|
||||
bool ignore_local_publications,
|
||||
const rcl_subscription_options_t & subscription_options,
|
||||
AnySubscriptionCallback<MessageT, Alloc> callback,
|
||||
typename message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr memory_strategy =
|
||||
message_memory_strategy::MessageMemoryStrategy<MessageT,
|
||||
typename message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
|
||||
memory_strategy = message_memory_strategy::MessageMemoryStrategy<MessageT,
|
||||
Alloc>::create_default())
|
||||
: SubscriptionBase(node_handle, subscription_handle, topic_name, ignore_local_publications),
|
||||
: SubscriptionBase(
|
||||
node_handle,
|
||||
*rosidl_typesupport_cpp::get_message_type_support_handle<MessageT>(),
|
||||
topic_name,
|
||||
subscription_options),
|
||||
any_callback_(callback),
|
||||
message_memory_strategy_(memory_strategy),
|
||||
get_intra_process_message_callback_(nullptr),
|
||||
matches_any_intra_process_publishers_(nullptr)
|
||||
{
|
||||
}
|
||||
{}
|
||||
|
||||
/// Support dynamically setting the message memory strategy.
|
||||
/**
|
||||
@@ -216,6 +195,8 @@ public:
|
||||
any_callback_.dispatch(typed_message, message_info);
|
||||
}
|
||||
|
||||
/// Return the loaned message.
|
||||
/** \param message message to be returned */
|
||||
void return_message(std::shared_ptr<void> & message)
|
||||
{
|
||||
auto typed_message = std::static_pointer_cast<MessageT>(message);
|
||||
@@ -248,26 +229,55 @@ public:
|
||||
any_callback_.dispatch_intra_process(msg, message_info);
|
||||
}
|
||||
|
||||
private:
|
||||
typedef
|
||||
std::function<
|
||||
void (uint64_t, uint64_t, uint64_t, MessageUniquePtr &)
|
||||
> GetMessageCallbackType;
|
||||
typedef std::function<bool (const rmw_gid_t *)> MatchesAnyPublishersCallbackType;
|
||||
using GetMessageCallbackType =
|
||||
std::function<void(uint64_t, uint64_t, uint64_t, MessageUniquePtr &)>;
|
||||
using MatchesAnyPublishersCallbackType = std::function<bool(const rmw_gid_t *)>;
|
||||
|
||||
/// Implemenation detail.
|
||||
void setup_intra_process(
|
||||
uint64_t intra_process_subscription_id,
|
||||
rmw_subscription_t * intra_process_subscription,
|
||||
GetMessageCallbackType get_message_callback,
|
||||
MatchesAnyPublishersCallbackType matches_any_publisher_callback)
|
||||
MatchesAnyPublishersCallbackType matches_any_publisher_callback,
|
||||
const rcl_subscription_options_t & intra_process_options)
|
||||
{
|
||||
std::string intra_process_topic_name = std::string(get_topic_name()) + "/_intra";
|
||||
rcl_ret_t ret = rcl_subscription_init(
|
||||
&intra_process_subscription_handle_,
|
||||
node_handle_.get(),
|
||||
rclcpp::type_support::get_intra_process_message_msg_type_support(),
|
||||
intra_process_topic_name.c_str(),
|
||||
&intra_process_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
|
||||
auto rcl_node_handle = node_handle_.get();
|
||||
// this will throw on any validation problem
|
||||
rcl_reset_error();
|
||||
expand_topic_or_service_name(
|
||||
intra_process_topic_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle));
|
||||
}
|
||||
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create intra process subscription");
|
||||
}
|
||||
|
||||
intra_process_subscription_id_ = intra_process_subscription_id;
|
||||
intra_process_subscription_handle_ = intra_process_subscription;
|
||||
get_intra_process_message_callback_ = get_message_callback;
|
||||
matches_any_intra_process_publishers_ = matches_any_publisher_callback;
|
||||
}
|
||||
|
||||
RCLCPP_DISABLE_COPY(Subscription);
|
||||
/// Implemenation detail.
|
||||
const rcl_subscription_t *
|
||||
get_intra_process_subscription_handle() const
|
||||
{
|
||||
if (!get_intra_process_message_callback_) {
|
||||
return nullptr;
|
||||
}
|
||||
return &intra_process_subscription_handle_;
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Subscription)
|
||||
|
||||
AnySubscriptionCallback<MessageT, Alloc> any_callback_;
|
||||
typename message_memory_strategy::MessageMemoryStrategy<MessageT, Alloc>::SharedPtr
|
||||
|
||||
171
rclcpp/include/rclcpp/subscription_factory.hpp
Normal file
171
rclcpp/include/rclcpp/subscription_factory.hpp
Normal 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_
|
||||
72
rclcpp/include/rclcpp/time.hpp
Normal file
72
rclcpp/include/rclcpp/time.hpp
Normal 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_
|
||||
@@ -12,107 +12,111 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_TIMER_HPP_
|
||||
#define RCLCPP_RCLCPP_TIMER_HPP_
|
||||
#ifndef RCLCPP__TIMER_HPP_
|
||||
#define RCLCPP__TIMER_HPP_
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/rate.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include <rclcpp/macros.hpp>
|
||||
#include <rclcpp/rate.hpp>
|
||||
#include <rclcpp/utilities.hpp>
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/timer.h"
|
||||
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace timer
|
||||
{
|
||||
|
||||
using CallbackType = std::function<void()>;
|
||||
|
||||
class TimerBase
|
||||
{
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimerBase);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimerBase)
|
||||
|
||||
TimerBase(std::chrono::nanoseconds period, CallbackType callback)
|
||||
: period_(period),
|
||||
callback_(callback),
|
||||
canceled_(false)
|
||||
{
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
explicit TimerBase(std::chrono::nanoseconds period);
|
||||
|
||||
virtual ~TimerBase()
|
||||
{
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
~TimerBase();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
cancel()
|
||||
{
|
||||
this->canceled_ = true;
|
||||
}
|
||||
cancel();
|
||||
|
||||
void execute_callback() const
|
||||
{
|
||||
callback_();
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
reset();
|
||||
|
||||
const CallbackType & get_callback() const
|
||||
{
|
||||
return callback_;
|
||||
}
|
||||
RCLCPP_PUBLIC
|
||||
virtual void
|
||||
execute_callback() = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_timer_t *
|
||||
get_timer_handle();
|
||||
|
||||
/// Check how long the timer has until its next scheduled callback.
|
||||
// \return A std::chrono::duration representing the relative time until the next callback.
|
||||
virtual std::chrono::nanoseconds
|
||||
time_until_trigger() = 0;
|
||||
/** \return A std::chrono::duration representing the relative time until the next callback. */
|
||||
RCLCPP_PUBLIC
|
||||
std::chrono::nanoseconds
|
||||
time_until_trigger();
|
||||
|
||||
/// Is the clock steady (i.e. is the time between ticks constant?)
|
||||
// \return True if the clock used by this timer is steady.
|
||||
/** \return True if the clock used by this timer is steady. */
|
||||
virtual bool is_steady() = 0;
|
||||
|
||||
/// Check if the timer needs to trigger the callback.
|
||||
/// Check if the timer is ready to trigger the callback.
|
||||
/**
|
||||
* This function expects its caller to immediately trigger the callback after this function,
|
||||
* since it maintains the last time the callback was triggered.
|
||||
* \return True if the timer needs to trigger.
|
||||
*/
|
||||
virtual bool check_and_trigger() = 0;
|
||||
RCLCPP_PUBLIC
|
||||
bool is_ready();
|
||||
|
||||
protected:
|
||||
std::chrono::nanoseconds period_;
|
||||
CallbackType callback_;
|
||||
|
||||
bool canceled_;
|
||||
|
||||
rcl_timer_t timer_handle_ = rcl_get_zero_initialized_timer();
|
||||
};
|
||||
|
||||
|
||||
using VoidCallbackType = std::function<void()>;
|
||||
using TimerCallbackType = std::function<void(TimerBase &)>;
|
||||
|
||||
/// Generic timer templated on the clock type. Periodically executes a user-specified callback.
|
||||
template<class Clock = std::chrono::high_resolution_clock>
|
||||
template<
|
||||
typename FunctorT,
|
||||
class Clock,
|
||||
typename std::enable_if<
|
||||
(rclcpp::function_traits::same_arguments<FunctorT, VoidCallbackType>::value ||
|
||||
rclcpp::function_traits::same_arguments<FunctorT, TimerCallbackType>::value) &&
|
||||
Clock::is_steady
|
||||
>::type * = nullptr
|
||||
>
|
||||
class GenericTimer : public TimerBase
|
||||
{
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericTimer);
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(GenericTimer)
|
||||
|
||||
/// Default constructor.
|
||||
/**
|
||||
* \param[in] period The interval at which the timer fires.
|
||||
* \param[in] callback User-specified callback function.
|
||||
*/
|
||||
GenericTimer(std::chrono::nanoseconds period, CallbackType callback)
|
||||
: TimerBase(period, callback), loop_rate_(period)
|
||||
GenericTimer(std::chrono::nanoseconds period, FunctorT && callback)
|
||||
: TimerBase(period), callback_(std::forward<FunctorT>(callback))
|
||||
{
|
||||
/* Subtracting the loop rate period ensures that the callback gets triggered
|
||||
on the first call to check_and_trigger. */
|
||||
last_triggered_time_ = Clock::now() - period;
|
||||
}
|
||||
|
||||
/// Default destructor.
|
||||
@@ -120,39 +124,47 @@ public:
|
||||
{
|
||||
// Stop the timer from running.
|
||||
cancel();
|
||||
if (rcl_timer_fini(&timer_handle_) != RCL_RET_OK) {
|
||||
fprintf(stderr, "Failed to clean up rcl timer handle: %s\n", rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
check_and_trigger()
|
||||
void
|
||||
execute_callback()
|
||||
{
|
||||
if (canceled_) {
|
||||
return false;
|
||||
rcl_ret_t ret = rcl_timer_call(&timer_handle_);
|
||||
if (ret == RCL_RET_TIMER_CANCELED) {
|
||||
return;
|
||||
}
|
||||
if (Clock::now() < last_triggered_time_) {
|
||||
return false;
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw std::runtime_error("Failed to notify timer that callback occurred");
|
||||
}
|
||||
if (std::chrono::duration_cast<std::chrono::nanoseconds>(Clock::now() - last_triggered_time_) >=
|
||||
loop_rate_.period())
|
||||
{
|
||||
last_triggered_time_ = Clock::now();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
execute_callback_delegate<>();
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds
|
||||
time_until_trigger()
|
||||
// void specialization
|
||||
template<
|
||||
typename CallbackT = FunctorT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<CallbackT, VoidCallbackType>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void
|
||||
execute_callback_delegate()
|
||||
{
|
||||
std::chrono::nanoseconds time_until_trigger;
|
||||
// Calculate the time between the next trigger and the current time
|
||||
if (last_triggered_time_ + loop_rate_.period() < Clock::now()) {
|
||||
// time is overdue, need to trigger immediately
|
||||
time_until_trigger = std::chrono::nanoseconds::zero();
|
||||
} else {
|
||||
time_until_trigger = std::chrono::duration_cast<std::chrono::nanoseconds>(
|
||||
last_triggered_time_ - Clock::now()) + loop_rate_.period();
|
||||
}
|
||||
return time_until_trigger;
|
||||
callback_();
|
||||
}
|
||||
|
||||
template<
|
||||
typename CallbackT = FunctorT,
|
||||
typename std::enable_if<
|
||||
rclcpp::function_traits::same_arguments<CallbackT, TimerCallbackType>::value
|
||||
>::type * = nullptr
|
||||
>
|
||||
void
|
||||
execute_callback_delegate()
|
||||
{
|
||||
callback_(*this);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
@@ -161,17 +173,16 @@ public:
|
||||
return Clock::is_steady;
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(GenericTimer);
|
||||
|
||||
rclcpp::rate::GenericRate<Clock> loop_rate_;
|
||||
std::chrono::time_point<Clock> last_triggered_time_;
|
||||
protected:
|
||||
RCLCPP_DISABLE_COPY(GenericTimer)
|
||||
|
||||
FunctorT callback_;
|
||||
};
|
||||
|
||||
using WallTimer = GenericTimer<std::chrono::steady_clock>;
|
||||
template<typename CallbackType>
|
||||
using WallTimer = GenericTimer<CallbackType, std::chrono::steady_clock>;
|
||||
|
||||
} /* namespace timer */
|
||||
} /* namespace rclcpp */
|
||||
} // namespace timer
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_TIMER_HPP_ */
|
||||
#endif // RCLCPP__TIMER_HPP_
|
||||
|
||||
78
rclcpp/include/rclcpp/type_support_decl.hpp
Normal file
78
rclcpp/include/rclcpp/type_support_decl.hpp
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2014 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__TYPE_SUPPORT_DECL_HPP_
|
||||
#define RCLCPP__TYPE_SUPPORT_DECL_HPP_
|
||||
|
||||
#include "rosidl_generator_cpp/message_type_support_decl.hpp"
|
||||
#include "rosidl_generator_cpp/service_type_support_decl.hpp"
|
||||
|
||||
#include "rosidl_typesupport_cpp/message_type_support.hpp"
|
||||
#include "rosidl_typesupport_cpp/service_type_support.hpp"
|
||||
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace type_support
|
||||
{
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_message_type_support_t *
|
||||
get_intra_process_message_msg_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_message_type_support_t *
|
||||
get_parameter_event_msg_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_message_type_support_t *
|
||||
get_set_parameters_result_msg_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_message_type_support_t *
|
||||
get_parameter_descriptor_msg_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_message_type_support_t *
|
||||
get_list_parameters_result_msg_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_service_type_support_t *
|
||||
get_get_parameters_srv_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_service_type_support_t *
|
||||
get_get_parameter_types_srv_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_service_type_support_t *
|
||||
get_set_parameters_srv_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_service_type_support_t *
|
||||
get_list_parameters_srv_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_service_type_support_t *
|
||||
get_describe_parameters_srv_type_support();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const rosidl_service_type_support_t *
|
||||
get_set_parameters_atomically_srv_type_support();
|
||||
|
||||
} // namespace type_support
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__TYPE_SUPPORT_DECL_HPP_
|
||||
@@ -12,100 +12,38 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP_RCLCPP_UTILITIES_HPP_
|
||||
#define RCLCPP_RCLCPP_UTILITIES_HPP_
|
||||
#ifndef RCLCPP__UTILITIES_HPP_
|
||||
#define RCLCPP__UTILITIES_HPP_
|
||||
|
||||
// TODO(wjwwood): remove
|
||||
#include <iostream>
|
||||
|
||||
#include <cerrno>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <csignal>
|
||||
#include <cstring>
|
||||
#include <mutex>
|
||||
#include <string.h>
|
||||
#include <thread>
|
||||
#include <functional>
|
||||
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/macros.h>
|
||||
#include <rmw/rmw.h>
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
// Determine if sigaction is available
|
||||
#if __APPLE__ || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
|
||||
#define HAS_SIGACTION
|
||||
#endif
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
|
||||
namespace
|
||||
#include "rmw/macros.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <string>
|
||||
#include <sstream>
|
||||
|
||||
namespace std
|
||||
{
|
||||
/// Represent the status of the global interrupt signal.
|
||||
volatile sig_atomic_t g_signal_status = 0;
|
||||
/// Guard condition for interrupting the rmw implementation when the global interrupt signal fired.
|
||||
rmw_guard_condition_t * g_sigint_guard_cond_handle = \
|
||||
rmw_create_guard_condition();
|
||||
/// Condition variable for timed sleep (see sleep_for).
|
||||
std::condition_variable g_interrupt_condition_variable;
|
||||
std::atomic<bool> g_is_interrupted(false);
|
||||
/// Mutex for protecting the global condition variable.
|
||||
std::mutex g_interrupt_mutex;
|
||||
|
||||
#ifdef HAS_SIGACTION
|
||||
struct sigaction old_action;
|
||||
#else
|
||||
void (* old_signal_handler)(int) = 0;
|
||||
#endif
|
||||
|
||||
/// Handle the interrupt signal.
|
||||
/** When the interrupt signal fires, the signal handler notifies the condition variable to wake up
|
||||
* and triggers the interrupt guard condition, so that all global threads managed by rclcpp
|
||||
* are interrupted.
|
||||
*/
|
||||
void
|
||||
#ifdef HAS_SIGACTION
|
||||
signal_handler(int signal_value, siginfo_t * siginfo, void * context)
|
||||
#else
|
||||
signal_handler(int signal_value)
|
||||
#endif
|
||||
template<typename T>
|
||||
std::string to_string(T value)
|
||||
{
|
||||
// TODO(wjwwood): remove
|
||||
std::cout << "signal_handler(" << signal_value << ")" << std::endl;
|
||||
#ifdef HAS_SIGACTION
|
||||
if (old_action.sa_flags & SA_SIGINFO) {
|
||||
if (old_action.sa_sigaction != NULL) {
|
||||
old_action.sa_sigaction(signal_value, siginfo, context);
|
||||
}
|
||||
} else {
|
||||
// *INDENT-OFF*
|
||||
if (
|
||||
old_action.sa_handler != NULL && // Is set
|
||||
old_action.sa_handler != SIG_DFL && // Is not default
|
||||
old_action.sa_handler != SIG_IGN) // Is not ignored
|
||||
// *INDENT-ON*
|
||||
{
|
||||
old_action.sa_handler(signal_value);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (old_signal_handler) {
|
||||
old_signal_handler(signal_value);
|
||||
}
|
||||
#endif
|
||||
g_signal_status = signal_value;
|
||||
rmw_ret_t status = rmw_trigger_guard_condition(g_sigint_guard_cond_handle);
|
||||
if (status != RMW_RET_OK) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] failed to trigger guard condition: %s\n", rmw_get_error_string_safe());
|
||||
}
|
||||
g_is_interrupted.store(true);
|
||||
g_interrupt_condition_variable.notify_all();
|
||||
std::ostringstream os;
|
||||
os << value;
|
||||
return os.str();
|
||||
}
|
||||
} // namespace
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
RMW_THREAD_LOCAL size_t thread_id = 0;
|
||||
|
||||
namespace utilities
|
||||
{
|
||||
|
||||
@@ -114,113 +52,66 @@ namespace utilities
|
||||
* \param[in] argc Number of arguments.
|
||||
* \param[in] argv Argument vector. Will eventually be used for passing options to rclcpp.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init(int argc, char * argv[])
|
||||
{
|
||||
(void)argc;
|
||||
(void)argv;
|
||||
g_is_interrupted.store(false);
|
||||
rmw_ret_t status = rmw_init();
|
||||
if (status != RMW_RET_OK) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("failed to initialize rmw implementation: ") + rmw_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
#ifdef HAS_SIGACTION
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_sigaction = ::signal_handler;
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
ssize_t ret = sigaction(SIGINT, &action, &old_action);
|
||||
if (ret == -1)
|
||||
#else
|
||||
::old_signal_handler = std::signal(SIGINT, ::signal_handler);
|
||||
if (::old_signal_handler == SIG_ERR)
|
||||
#endif
|
||||
{
|
||||
const size_t error_length = 1024;
|
||||
char error_string[error_length];
|
||||
#ifndef _WIN32
|
||||
auto rc = strerror_r(errno, error_string, error_length);
|
||||
if (rc) {
|
||||
// *INDENT-OFF*
|
||||
throw std::runtime_error(
|
||||
"Failed to set SIGINT signal handler: (" + std::to_string(errno) +
|
||||
") unable to retrieve error string");
|
||||
// *INDENT-ON*
|
||||
}
|
||||
|
||||
#else
|
||||
strerror_s(error_string, error_length, errno);
|
||||
#endif
|
||||
// *INDENT-OFF*
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to set SIGINT signal handler: (" + std::to_string(errno) + ")") +
|
||||
error_string);
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
init(int argc, char * argv[]);
|
||||
|
||||
/// Check rclcpp's status.
|
||||
// \return True if SIGINT hasn't fired yet, false otherwise.
|
||||
/** \return True if SIGINT hasn't fired yet, false otherwise. */
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
ok()
|
||||
{
|
||||
return ::g_signal_status == 0;
|
||||
}
|
||||
ok();
|
||||
|
||||
/// Notify the signal handler and rmw that rclcpp is shutting down.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
shutdown()
|
||||
{
|
||||
g_signal_status = SIGINT;
|
||||
rmw_ret_t status = rmw_trigger_guard_condition(g_sigint_guard_cond_handle);
|
||||
if (status != RMW_RET_OK) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] failed to trigger guard condition: %s\n", rmw_get_error_string_safe());
|
||||
}
|
||||
g_is_interrupted.store(true);
|
||||
g_interrupt_condition_variable.notify_all();
|
||||
}
|
||||
shutdown();
|
||||
|
||||
/// Register a function to be called when shutdown is called.
|
||||
/** Calling the callbacks is the last thing shutdown() does. */
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
on_shutdown(std::function<void(void)> callback);
|
||||
|
||||
/// Get a handle to the rmw guard condition that manages the signal handler.
|
||||
rmw_guard_condition_t *
|
||||
get_global_sigint_guard_condition()
|
||||
{
|
||||
return ::g_sigint_guard_cond_handle;
|
||||
}
|
||||
/**
|
||||
* The first time that this function is called for a given waitset a new guard
|
||||
* condition will be created and returned; thereafter the same guard condition
|
||||
* will be returned for the same waitset. This mechanism is designed to ensure
|
||||
* that the same guard condition is not reused across waitsets (e.g., when
|
||||
* using multiple executors in the same process). Will throw an exception if
|
||||
* initialization of the guard condition fails.
|
||||
* \param waitset Pointer to the rcl_wait_set_t that will be using the
|
||||
* resulting guard condition.
|
||||
* \return Pointer to the guard condition.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rcl_guard_condition_t *
|
||||
get_sigint_guard_condition(rcl_wait_set_t * waitset);
|
||||
|
||||
/// Release the previously allocated guard condition that manages the signal handler.
|
||||
/**
|
||||
* If you previously called get_sigint_guard_condition() for a given waitset
|
||||
* to get a sigint guard condition, then you should call release_sigint_guard_condition()
|
||||
* when you're done, to free that condition. Will throw an exception if
|
||||
* get_sigint_guard_condition() wasn't previously called for the given waitset.
|
||||
* \param waitset Pointer to the rcl_wait_set_t that was using the
|
||||
* resulting guard condition.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
release_sigint_guard_condition(rcl_wait_set_t * waitset);
|
||||
|
||||
/// Use the global condition variable to block for the specified amount of time.
|
||||
/**
|
||||
* \param[in] nanoseconds A std::chrono::duration representing how long to sleep for.
|
||||
* \return True if the condition variable did not timeout.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
sleep_for(const std::chrono::nanoseconds & nanoseconds)
|
||||
{
|
||||
// TODO: determine if posix's nanosleep(2) is more efficient here
|
||||
std::chrono::nanoseconds time_left = nanoseconds;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(::g_interrupt_mutex);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
::g_interrupt_condition_variable.wait_for(lock, nanoseconds);
|
||||
time_left -= std::chrono::steady_clock::now() - start;
|
||||
}
|
||||
if (time_left > std::chrono::nanoseconds::zero() && !g_is_interrupted) {
|
||||
return sleep_for(time_left);
|
||||
}
|
||||
// Return true if the timeout elapsed successfully, otherwise false.
|
||||
return !g_is_interrupted;
|
||||
}
|
||||
sleep_for(const std::chrono::nanoseconds & nanoseconds);
|
||||
|
||||
} /* namespace utilities */
|
||||
} /* namespace rclcpp */
|
||||
} // namespace utilities
|
||||
} // namespace rclcpp
|
||||
|
||||
#ifdef HAS_SIGACTION
|
||||
#undef HAS_SIGACTION
|
||||
#endif
|
||||
|
||||
#endif /* RCLCPP_RCLCPP_UTILITIES_HPP_ */
|
||||
#endif // RCLCPP__UTILITIES_HPP_
|
||||
|
||||
56
rclcpp/include/rclcpp/visibility_control.hpp
Normal file
56
rclcpp/include/rclcpp/visibility_control.hpp
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
/* This header must be included by all rclcpp headers which declare symbols
|
||||
* which are defined in the rclcpp library. When not building the rclcpp
|
||||
* library, i.e. when using the headers in other package's code, the contents
|
||||
* of this header change the visibility of certain symbols which the rclcpp
|
||||
* library cannot have, but the consuming code must have inorder to link.
|
||||
*/
|
||||
|
||||
#ifndef RCLCPP__VISIBILITY_CONTROL_HPP_
|
||||
#define RCLCPP__VISIBILITY_CONTROL_HPP_
|
||||
|
||||
// This logic was borrowed (then namespaced) from the examples on the gcc wiki:
|
||||
// https://gcc.gnu.org/wiki/Visibility
|
||||
|
||||
#if defined _WIN32 || defined __CYGWIN__
|
||||
#ifdef __GNUC__
|
||||
#define RCLCPP_EXPORT __attribute__ ((dllexport))
|
||||
#define RCLCPP_IMPORT __attribute__ ((dllimport))
|
||||
#else
|
||||
#define RCLCPP_EXPORT __declspec(dllexport)
|
||||
#define RCLCPP_IMPORT __declspec(dllimport)
|
||||
#endif
|
||||
#ifdef RCLCPP_BUILDING_LIBRARY
|
||||
#define RCLCPP_PUBLIC RCLCPP_EXPORT
|
||||
#else
|
||||
#define RCLCPP_PUBLIC RCLCPP_IMPORT
|
||||
#endif
|
||||
#define RCLCPP_PUBLIC_TYPE RCLCPP_PUBLIC
|
||||
#define RCLCPP_LOCAL
|
||||
#else
|
||||
#define RCLCPP_EXPORT __attribute__ ((visibility("default")))
|
||||
#define RCLCPP_IMPORT
|
||||
#if __GNUC__ >= 4
|
||||
#define RCLCPP_PUBLIC __attribute__ ((visibility("default")))
|
||||
#define RCLCPP_LOCAL __attribute__ ((visibility("hidden")))
|
||||
#else
|
||||
#define RCLCPP_PUBLIC
|
||||
#define RCLCPP_LOCAL
|
||||
#endif
|
||||
#define RCLCPP_PUBLIC_TYPE
|
||||
#endif
|
||||
|
||||
#endif // RCLCPP__VISIBILITY_CONTROL_HPP_
|
||||
@@ -1,7 +1,8 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp</name>
|
||||
<version>0.0.0</version>
|
||||
<version>0.0.2</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
|
||||
<license>Apache License 2.0</license>
|
||||
@@ -10,13 +11,27 @@
|
||||
|
||||
<build_export_depend>rmw</build_export_depend>
|
||||
|
||||
<build_depend>builtin_interfaces</build_depend>
|
||||
<build_depend>rcl_interfaces</build_depend>
|
||||
<build_depend>rosidl_generator_cpp</build_depend>
|
||||
<build_depend>rosidl_typesupport_c</build_depend>
|
||||
<build_depend>rosidl_typesupport_cpp</build_depend>
|
||||
<build_export_depend>builtin_interfaces</build_export_depend>
|
||||
<build_export_depend>rcl_interfaces</build_export_depend>
|
||||
<build_export_depend>rosidl_generator_cpp</build_export_depend>
|
||||
<build_export_depend>rosidl_typesupport_c</build_export_depend>
|
||||
<build_export_depend>rosidl_typesupport_cpp</build_export_depend>
|
||||
|
||||
<depend>rcl</depend>
|
||||
<depend>rmw_implementation</depend>
|
||||
|
||||
<exec_depend>ament_cmake</exec_depend>
|
||||
|
||||
<test_depend>ament_cmake_gtest</test_depend>
|
||||
<test_depend>ament_lint_auto</test_depend>
|
||||
<test_depend>ament_lint_common</test_depend>
|
||||
<test_depend>rmw</test_depend>
|
||||
<test_depend>rmw_implementation_cmake</test_depend>
|
||||
|
||||
<export>
|
||||
<build_type>ament_cmake</build_type>
|
||||
|
||||
@@ -14,14 +14,16 @@
|
||||
|
||||
# copied from rclcpp/rclcpp-extras.cmake
|
||||
|
||||
set(rclcpp_node_main_SRC "${rclcpp_DIR}/../../../src/rclcpp/node_main.cpp")
|
||||
# register ament_package() hook for node plugins once
|
||||
macro(_rclcpp_register_package_hook)
|
||||
if(NOT DEFINED _RCLCPP_PACKAGE_HOOK_REGISTERED)
|
||||
set(_RCLCPP_PACKAGE_HOOK_REGISTERED TRUE)
|
||||
|
||||
function(rclcpp_create_node_main node_library_target)
|
||||
if(NOT TARGET ${node_library_target})
|
||||
message(FATAL_ERROR "rclcpp_create_node_main() the first argument must be a valid target name")
|
||||
find_package(ament_cmake_core QUIET REQUIRED)
|
||||
ament_register_extension("ament_package" "rclcpp"
|
||||
"rclcpp_package_hook.cmake")
|
||||
endif()
|
||||
set(executable_name_ ${node_library_target}_node)
|
||||
add_executable(${executable_name_} ${rclcpp_node_main_SRC})
|
||||
target_link_libraries(${executable_name_} ${node_library_target})
|
||||
install(TARGETS ${executable_name_} DESTINATION bin)
|
||||
endfunction()
|
||||
endmacro()
|
||||
|
||||
include("${rclcpp_DIR}/rclcpp_create_node_main.cmake")
|
||||
include("${rclcpp_DIR}/rclcpp_register_node_plugins.cmake")
|
||||
|
||||
37
rclcpp/src/rclcpp/any_executable.cpp
Normal file
37
rclcpp/src/rclcpp/any_executable.cpp
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/any_executable.hpp"
|
||||
|
||||
using rclcpp::executor::AnyExecutable;
|
||||
|
||||
AnyExecutable::AnyExecutable()
|
||||
: subscription(nullptr),
|
||||
subscription_intra_process(nullptr),
|
||||
timer(nullptr),
|
||||
service(nullptr),
|
||||
client(nullptr),
|
||||
callback_group(nullptr),
|
||||
node_base(nullptr)
|
||||
{}
|
||||
|
||||
AnyExecutable::~AnyExecutable()
|
||||
{
|
||||
// Make sure that discarded (taken but not executed) AnyExecutable's have
|
||||
// their callback groups reset. This can happen when an executor is canceled
|
||||
// between taking an AnyExecutable and executing it.
|
||||
if (callback_group) {
|
||||
callback_group->can_be_taken_from().store(true);
|
||||
}
|
||||
}
|
||||
93
rclcpp/src/rclcpp/callback_group.cpp
Normal file
93
rclcpp/src/rclcpp/callback_group.cpp
Normal file
@@ -0,0 +1,93 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
|
||||
#include <vector>
|
||||
|
||||
using rclcpp::callback_group::CallbackGroup;
|
||||
using rclcpp::callback_group::CallbackGroupType;
|
||||
|
||||
CallbackGroup::CallbackGroup(CallbackGroupType group_type)
|
||||
: type_(group_type), can_be_taken_from_(true)
|
||||
{}
|
||||
|
||||
const std::vector<rclcpp::subscription::SubscriptionBase::WeakPtr> &
|
||||
CallbackGroup::get_subscription_ptrs() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return subscription_ptrs_;
|
||||
}
|
||||
|
||||
const std::vector<rclcpp::timer::TimerBase::WeakPtr> &
|
||||
CallbackGroup::get_timer_ptrs() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return timer_ptrs_;
|
||||
}
|
||||
|
||||
const std::vector<rclcpp::service::ServiceBase::WeakPtr> &
|
||||
CallbackGroup::get_service_ptrs() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return service_ptrs_;
|
||||
}
|
||||
|
||||
const std::vector<rclcpp::client::ClientBase::WeakPtr> &
|
||||
CallbackGroup::get_client_ptrs() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return client_ptrs_;
|
||||
}
|
||||
|
||||
std::atomic_bool &
|
||||
CallbackGroup::can_be_taken_from()
|
||||
{
|
||||
return can_be_taken_from_;
|
||||
}
|
||||
|
||||
const CallbackGroupType &
|
||||
CallbackGroup::type() const
|
||||
{
|
||||
return type_;
|
||||
}
|
||||
|
||||
void
|
||||
CallbackGroup::add_subscription(
|
||||
const rclcpp::subscription::SubscriptionBase::SharedPtr subscription_ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
subscription_ptrs_.push_back(subscription_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
CallbackGroup::add_timer(const rclcpp::timer::TimerBase::SharedPtr timer_ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
timer_ptrs_.push_back(timer_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
CallbackGroup::add_service(const rclcpp::service::ServiceBase::SharedPtr service_ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
service_ptrs_.push_back(service_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
CallbackGroup::add_client(const rclcpp::client::ClientBase::SharedPtr client_ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
client_ptrs_.push_back(client_ptr);
|
||||
}
|
||||
121
rclcpp/src/rclcpp/client.cpp
Normal file
121
rclcpp/src/rclcpp/client.cpp
Normal file
@@ -0,0 +1,121 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/client.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rcl/graph.h"
|
||||
#include "rcl/node.h"
|
||||
#include "rcl/wait.h"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
using rclcpp::client::ClientBase;
|
||||
using rclcpp::exceptions::InvalidNodeError;
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
|
||||
ClientBase::ClientBase(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
|
||||
const std::string & service_name)
|
||||
: node_graph_(node_graph),
|
||||
node_handle_(node_base->get_shared_rcl_node_handle()),
|
||||
service_name_(service_name)
|
||||
{}
|
||||
|
||||
ClientBase::~ClientBase() {}
|
||||
|
||||
const std::string &
|
||||
ClientBase::get_service_name() const
|
||||
{
|
||||
return this->service_name_;
|
||||
}
|
||||
|
||||
const rcl_client_t *
|
||||
ClientBase::get_client_handle() const
|
||||
{
|
||||
return &client_handle_;
|
||||
}
|
||||
|
||||
bool
|
||||
ClientBase::service_is_ready() const
|
||||
{
|
||||
bool is_ready;
|
||||
rcl_ret_t ret =
|
||||
rcl_service_server_is_available(this->get_rcl_node_handle(), &client_handle_, &is_ready);
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "rcl_service_server_is_available failed");
|
||||
}
|
||||
return is_ready;
|
||||
}
|
||||
|
||||
bool
|
||||
ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
// make an event to reuse, rather than create a new one each time
|
||||
auto node_ptr = node_graph_.lock();
|
||||
if (!node_ptr) {
|
||||
throw InvalidNodeError();
|
||||
}
|
||||
auto event = node_ptr->get_graph_event();
|
||||
// check to see if the server is ready immediately
|
||||
if (this->service_is_ready()) {
|
||||
return true;
|
||||
}
|
||||
if (timeout == std::chrono::nanoseconds(0)) {
|
||||
// check was non-blocking, return immediately
|
||||
return false;
|
||||
}
|
||||
// update the time even on the first loop to account for time spent in the first call
|
||||
// to this->server_is_ready()
|
||||
std::chrono::nanoseconds time_to_wait = timeout - (std::chrono::steady_clock::now() - start);
|
||||
if (timeout > std::chrono::nanoseconds(0) && time_to_wait < std::chrono::nanoseconds(0)) {
|
||||
// Do not allow the time_to_wait to become negative when timeout was originally positive.
|
||||
// Setting time_to_wait to 0 will allow one non-blocking wait because of the do-while.
|
||||
time_to_wait = std::chrono::nanoseconds(0);
|
||||
}
|
||||
// continue forever if timeout is negative, otherwise continue until out of time_to_wait
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
do {
|
||||
if (!rclcpp::utilities::ok()) {
|
||||
return false;
|
||||
}
|
||||
node_ptr->wait_for_graph_change(event, time_to_wait);
|
||||
event->check_and_clear(); // reset the event
|
||||
|
||||
// always check if the service is ready, even if the graph event wasn't triggered
|
||||
// this is needed to avoid a race condition that is specific to the Connext RMW implementation
|
||||
// (see https://github.com/ros2/rmw_connext/issues/201)
|
||||
if (this->service_is_ready()) {
|
||||
return true;
|
||||
}
|
||||
// server is not ready, loop if there is time left
|
||||
time_to_wait = timeout - (std::chrono::steady_clock::now() - start);
|
||||
} while (timeout < std::chrono::nanoseconds(0) || time_to_wait > std::chrono::nanoseconds(0));
|
||||
// *INDENT-ON*
|
||||
return false; // timeout exceeded while waiting for the server to be ready
|
||||
}
|
||||
|
||||
rcl_node_t *
|
||||
ClientBase::get_rcl_node_handle() const
|
||||
{
|
||||
return node_handle_.get();
|
||||
}
|
||||
19
rclcpp/src/rclcpp/context.cpp
Normal file
19
rclcpp/src/rclcpp/context.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/context.hpp"
|
||||
|
||||
using rclcpp::context::Context;
|
||||
|
||||
Context::Context() {}
|
||||
@@ -1,4 +1,4 @@
|
||||
// Copyright 2014 Open Source Robotics Foundation, Inc.
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
@@ -12,17 +12,16 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <rclcpp/rclcpp.hpp>
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
|
||||
// This forward declaration is implemented by the RCLCPP_REGISTER_NODE macro
|
||||
RMW_IMPORT rclcpp::Node::SharedPtr create_node();
|
||||
using rclcpp::contexts::default_context::DefaultContext;
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
DefaultContext::DefaultContext()
|
||||
{}
|
||||
|
||||
DefaultContext::SharedPtr
|
||||
rclcpp::contexts::default_context::get_global_default_context()
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
rclcpp::Node::SharedPtr node = create_node();
|
||||
executor.add_node(node);
|
||||
executor.spin();
|
||||
return 0;
|
||||
static DefaultContext::SharedPtr default_context = DefaultContext::make_shared();
|
||||
return default_context;
|
||||
}
|
||||
44
rclcpp/src/rclcpp/event.cpp
Normal file
44
rclcpp/src/rclcpp/event.cpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2016 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/event.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace event
|
||||
{
|
||||
|
||||
Event::Event()
|
||||
: state_(false) {}
|
||||
|
||||
bool
|
||||
Event::set()
|
||||
{
|
||||
return state_.exchange(true);
|
||||
}
|
||||
|
||||
bool
|
||||
Event::check()
|
||||
{
|
||||
return state_.load();
|
||||
}
|
||||
|
||||
bool
|
||||
Event::check_and_clear()
|
||||
{
|
||||
return state_.exchange(false);
|
||||
}
|
||||
|
||||
} // namespace event
|
||||
} // namespace rclcpp
|
||||
116
rclcpp/src/rclcpp/exceptions.cpp
Normal file
116
rclcpp/src/rclcpp/exceptions.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2016 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace exceptions
|
||||
{
|
||||
|
||||
std::string
|
||||
NameValidationError::format_error(
|
||||
const char * name_type,
|
||||
const char * name,
|
||||
const char * error_msg,
|
||||
size_t invalid_index)
|
||||
{
|
||||
std::string msg = "";
|
||||
msg += "Invalid "s + name_type + ": " + error_msg + ":\n";
|
||||
msg += " '"s + name + "'\n";
|
||||
msg += " "s + std::string(invalid_index, ' ') + "^\n";
|
||||
return msg;
|
||||
}
|
||||
|
||||
void
|
||||
throw_from_rcl_error(
|
||||
rcl_ret_t ret,
|
||||
const std::string & prefix,
|
||||
const rcl_error_state_t * error_state,
|
||||
void (* reset_error)())
|
||||
{
|
||||
if (RCL_RET_OK == ret) {
|
||||
throw std::invalid_argument("ret is RCL_RET_OK");
|
||||
}
|
||||
if (!error_state) {
|
||||
error_state = rcl_get_error_state();
|
||||
}
|
||||
if (!error_state) {
|
||||
throw std::runtime_error("rcl error state is not set");
|
||||
}
|
||||
std::string formated_prefix = prefix;
|
||||
if (!prefix.empty()) {
|
||||
formated_prefix += ": ";
|
||||
}
|
||||
RCLErrorBase base_exc(ret, error_state);
|
||||
if (reset_error) {
|
||||
reset_error();
|
||||
}
|
||||
switch (ret) {
|
||||
case RCL_RET_BAD_ALLOC:
|
||||
throw RCLBadAlloc(base_exc);
|
||||
case RCL_RET_INVALID_ARGUMENT:
|
||||
throw RCLInvalidArgument(base_exc, formated_prefix);
|
||||
default:
|
||||
throw RCLError(base_exc, formated_prefix);
|
||||
}
|
||||
}
|
||||
|
||||
RCLErrorBase::RCLErrorBase(rcl_ret_t ret, const rcl_error_state_t * error_state)
|
||||
: ret(ret), message(error_state->message), file(error_state->file), line(error_state->line_number),
|
||||
formatted_message(rcl_get_error_string_safe())
|
||||
{}
|
||||
|
||||
RCLError::RCLError(
|
||||
rcl_ret_t ret,
|
||||
const rcl_error_state_t * error_state,
|
||||
const std::string & prefix)
|
||||
: RCLError(RCLErrorBase(ret, error_state), prefix)
|
||||
{}
|
||||
|
||||
RCLError::RCLError(
|
||||
const RCLErrorBase & base_exc,
|
||||
const std::string & prefix)
|
||||
: RCLErrorBase(base_exc), std::runtime_error(prefix + base_exc.formatted_message)
|
||||
{}
|
||||
|
||||
RCLBadAlloc::RCLBadAlloc(rcl_ret_t ret, const rcl_error_state_t * error_state)
|
||||
: RCLBadAlloc(RCLErrorBase(ret, error_state))
|
||||
{}
|
||||
|
||||
RCLBadAlloc::RCLBadAlloc(const RCLErrorBase & base_exc)
|
||||
: RCLErrorBase(base_exc), std::bad_alloc()
|
||||
{}
|
||||
|
||||
RCLInvalidArgument::RCLInvalidArgument(
|
||||
rcl_ret_t ret,
|
||||
const rcl_error_state_t * error_state,
|
||||
const std::string & prefix)
|
||||
: RCLInvalidArgument(RCLErrorBase(ret, error_state), prefix)
|
||||
{}
|
||||
|
||||
RCLInvalidArgument::RCLInvalidArgument(
|
||||
const RCLErrorBase & base_exc,
|
||||
const std::string & prefix)
|
||||
: RCLErrorBase(base_exc), std::invalid_argument(prefix + base_exc.formatted_message)
|
||||
{}
|
||||
|
||||
} // namespace exceptions
|
||||
} // namespace rclcpp
|
||||
594
rclcpp/src/rclcpp/executor.cpp
Normal file
594
rclcpp/src/rclcpp/executor.cpp
Normal file
@@ -0,0 +1,594 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "rcl/allocator.h"
|
||||
#include "rcl/error_handling.h"
|
||||
|
||||
#include "rclcpp/executor.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/scope_exit.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
|
||||
using rclcpp::executor::AnyExecutable;
|
||||
using rclcpp::executor::Executor;
|
||||
using rclcpp::executor::ExecutorArgs;
|
||||
using rclcpp::executor::FutureReturnCode;
|
||||
|
||||
Executor::Executor(const ExecutorArgs & args)
|
||||
: spinning(false),
|
||||
memory_strategy_(args.memory_strategy)
|
||||
{
|
||||
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
|
||||
if (rcl_guard_condition_init(
|
||||
&interrupt_guard_condition_, guard_condition_options) != RCL_RET_OK)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to create interrupt guard condition in Executor constructor: ") +
|
||||
rcl_get_error_string_safe());
|
||||
}
|
||||
|
||||
// The number of guard conditions is always at least 2: 1 for the ctrl-c guard cond,
|
||||
// and one for the executor's guard cond (interrupt_guard_condition_)
|
||||
|
||||
// Put the global ctrl-c guard condition in
|
||||
memory_strategy_->add_guard_condition(rclcpp::utilities::get_sigint_guard_condition(&waitset_));
|
||||
|
||||
// Put the executor's guard condition in
|
||||
memory_strategy_->add_guard_condition(&interrupt_guard_condition_);
|
||||
rcl_allocator_t allocator = memory_strategy_->get_allocator();
|
||||
|
||||
if (rcl_wait_set_init(
|
||||
&waitset_, 0, 2, 0, 0, 0, allocator) != RCL_RET_OK)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] failed to create waitset: %s\n", rcl_get_error_string_safe());
|
||||
if (rcl_guard_condition_fini(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
|
||||
}
|
||||
throw std::runtime_error("Failed to create waitset in Executor constructor");
|
||||
}
|
||||
}
|
||||
|
||||
Executor::~Executor()
|
||||
{
|
||||
// Finalize the waitset.
|
||||
if (rcl_wait_set_fini(&waitset_) != RCL_RET_OK) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] failed to destroy waitset: %s\n", rcl_get_error_string_safe());
|
||||
}
|
||||
// Finalize the interrupt guard condition.
|
||||
if (rcl_guard_condition_fini(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] failed to destroy guard condition: %s\n", rcl_get_error_string_safe());
|
||||
}
|
||||
// Remove and release the sigint guard condition
|
||||
memory_strategy_->remove_guard_condition(
|
||||
rclcpp::utilities::get_sigint_guard_condition(&waitset_));
|
||||
rclcpp::utilities::release_sigint_guard_condition(&waitset_);
|
||||
}
|
||||
|
||||
void
|
||||
Executor::add_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
||||
{
|
||||
// If the node already has an executor
|
||||
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
||||
if (has_executor.exchange(true)) {
|
||||
throw std::runtime_error("Node has already been added to an executor.");
|
||||
}
|
||||
// Check to ensure node not already added
|
||||
for (auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (node == node_ptr) {
|
||||
// TODO(jacquelinekay): Use a different error here?
|
||||
throw std::runtime_error("Cannot add node to executor, node already added.");
|
||||
}
|
||||
}
|
||||
weak_nodes_.push_back(node_ptr);
|
||||
if (notify) {
|
||||
// Interrupt waiting to handle new node
|
||||
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
throw std::runtime_error(rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
// Add the node's notify condition to the guard condition handles
|
||||
memory_strategy_->add_guard_condition(node_ptr->get_notify_guard_condition());
|
||||
}
|
||||
|
||||
void
|
||||
Executor::add_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify)
|
||||
{
|
||||
this->add_node(node_ptr->get_node_base_interface(), notify);
|
||||
}
|
||||
|
||||
void
|
||||
Executor::remove_node(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr, bool notify)
|
||||
{
|
||||
bool node_removed = false;
|
||||
weak_nodes_.erase(
|
||||
std::remove_if(
|
||||
weak_nodes_.begin(), weak_nodes_.end(),
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
[&](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr & i)
|
||||
{
|
||||
bool matched = (i.lock() == node_ptr);
|
||||
node_removed |= matched;
|
||||
return matched;
|
||||
}
|
||||
// *INDENT-ON*
|
||||
)
|
||||
);
|
||||
std::atomic_bool & has_executor = node_ptr->get_associated_with_executor_atomic();
|
||||
has_executor.store(false);
|
||||
if (notify) {
|
||||
// If the node was matched and removed, interrupt waiting
|
||||
if (node_removed) {
|
||||
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
throw std::runtime_error(rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
}
|
||||
memory_strategy_->remove_guard_condition(node_ptr->get_notify_guard_condition());
|
||||
}
|
||||
|
||||
void
|
||||
Executor::remove_node(std::shared_ptr<rclcpp::node::Node> node_ptr, bool notify)
|
||||
{
|
||||
this->remove_node(node_ptr->get_node_base_interface(), notify);
|
||||
}
|
||||
|
||||
void
|
||||
Executor::spin_node_once_nanoseconds(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
|
||||
std::chrono::nanoseconds timeout)
|
||||
{
|
||||
this->add_node(node, false);
|
||||
// non-blocking = true
|
||||
spin_once(timeout);
|
||||
this->remove_node(node, false);
|
||||
}
|
||||
|
||||
void
|
||||
Executor::spin_node_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node)
|
||||
{
|
||||
this->add_node(node, false);
|
||||
spin_some();
|
||||
this->remove_node(node, false);
|
||||
}
|
||||
|
||||
void
|
||||
Executor::spin_node_some(std::shared_ptr<rclcpp::node::Node> node)
|
||||
{
|
||||
this->spin_node_some(node->get_node_base_interface());
|
||||
}
|
||||
|
||||
void
|
||||
Executor::spin_some()
|
||||
{
|
||||
if (spinning.exchange(true)) {
|
||||
throw std::runtime_error("spin_some() called while already spinning");
|
||||
}
|
||||
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
|
||||
AnyExecutable::SharedPtr any_exec;
|
||||
while ((any_exec = get_next_executable(std::chrono::milliseconds::zero())) && spinning.load()) {
|
||||
execute_any_executable(any_exec);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Executor::spin_once(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
if (spinning.exchange(true)) {
|
||||
throw std::runtime_error("spin_once() called while already spinning");
|
||||
}
|
||||
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
|
||||
auto any_exec = get_next_executable(timeout);
|
||||
if (any_exec) {
|
||||
execute_any_executable(any_exec);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Executor::cancel()
|
||||
{
|
||||
spinning.store(false);
|
||||
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
throw std::runtime_error(rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Executor::set_memory_strategy(rclcpp::memory_strategy::MemoryStrategy::SharedPtr memory_strategy)
|
||||
{
|
||||
if (memory_strategy == nullptr) {
|
||||
throw std::runtime_error("Received NULL memory strategy in executor.");
|
||||
}
|
||||
memory_strategy_ = memory_strategy;
|
||||
}
|
||||
|
||||
void
|
||||
Executor::execute_any_executable(AnyExecutable::SharedPtr any_exec)
|
||||
{
|
||||
if (!any_exec || !spinning.load()) {
|
||||
return;
|
||||
}
|
||||
if (any_exec->timer) {
|
||||
execute_timer(any_exec->timer);
|
||||
}
|
||||
if (any_exec->subscription) {
|
||||
execute_subscription(any_exec->subscription);
|
||||
}
|
||||
if (any_exec->subscription_intra_process) {
|
||||
execute_intra_process_subscription(any_exec->subscription_intra_process);
|
||||
}
|
||||
if (any_exec->service) {
|
||||
execute_service(any_exec->service);
|
||||
}
|
||||
if (any_exec->client) {
|
||||
execute_client(any_exec->client);
|
||||
}
|
||||
// Reset the callback_group, regardless of type
|
||||
any_exec->callback_group->can_be_taken_from().store(true);
|
||||
// Wake the wait, because it may need to be recalculated or work that
|
||||
// was previously blocked is now available.
|
||||
if (rcl_trigger_guard_condition(&interrupt_guard_condition_) != RCL_RET_OK) {
|
||||
throw std::runtime_error(rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Executor::execute_subscription(
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr subscription)
|
||||
{
|
||||
std::shared_ptr<void> message = subscription->create_message();
|
||||
rmw_message_info_t message_info;
|
||||
|
||||
auto ret = rcl_take(subscription->get_subscription_handle(),
|
||||
message.get(), &message_info);
|
||||
if (ret == RCL_RET_OK) {
|
||||
message_info.from_intra_process = false;
|
||||
subscription->handle_message(message, message_info);
|
||||
} else if (ret != RCL_RET_SUBSCRIPTION_TAKE_FAILED) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] take failed for subscription on topic '%s': %s\n",
|
||||
subscription->get_topic_name(), rcl_get_error_string_safe());
|
||||
}
|
||||
subscription->return_message(message);
|
||||
}
|
||||
|
||||
void
|
||||
Executor::execute_intra_process_subscription(
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr subscription)
|
||||
{
|
||||
rcl_interfaces::msg::IntraProcessMessage ipm;
|
||||
rmw_message_info_t message_info;
|
||||
rcl_ret_t status = rcl_take(
|
||||
subscription->get_intra_process_subscription_handle(),
|
||||
&ipm,
|
||||
&message_info);
|
||||
|
||||
if (status == RCL_RET_OK) {
|
||||
message_info.from_intra_process = true;
|
||||
subscription->handle_intra_process_message(ipm, message_info);
|
||||
} else if (status != RCL_RET_SUBSCRIPTION_TAKE_FAILED) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] take failed for intra process subscription on topic '%s': %s\n",
|
||||
subscription->get_topic_name(), rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Executor::execute_timer(
|
||||
rclcpp::timer::TimerBase::SharedPtr timer)
|
||||
{
|
||||
timer->execute_callback();
|
||||
}
|
||||
|
||||
void
|
||||
Executor::execute_service(
|
||||
rclcpp::service::ServiceBase::SharedPtr service)
|
||||
{
|
||||
auto request_header = service->create_request_header();
|
||||
std::shared_ptr<void> request = service->create_request();
|
||||
rcl_ret_t status = rcl_take_request(
|
||||
service->get_service_handle(),
|
||||
request_header.get(),
|
||||
request.get());
|
||||
if (status == RCL_RET_OK) {
|
||||
service->handle_request(request_header, request);
|
||||
} else if (status != RCL_RET_SERVICE_TAKE_FAILED) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] take request failed for server of service '%s': %s\n",
|
||||
service->get_service_name().c_str(), rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Executor::execute_client(
|
||||
rclcpp::client::ClientBase::SharedPtr client)
|
||||
{
|
||||
auto request_header = client->create_request_header();
|
||||
std::shared_ptr<void> response = client->create_response();
|
||||
rcl_ret_t status = rcl_take_response(
|
||||
client->get_client_handle(),
|
||||
request_header.get(),
|
||||
response.get());
|
||||
if (status == RCL_RET_OK) {
|
||||
client->handle_response(request_header, response);
|
||||
} else if (status != RCL_RET_SERVICE_TAKE_FAILED) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] take response failed for client of service '%s': %s\n",
|
||||
client->get_service_name().c_str(), rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Executor::wait_for_work(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
// Collect the subscriptions and timers to be waited on
|
||||
memory_strategy_->clear_handles();
|
||||
bool has_invalid_weak_nodes = memory_strategy_->collect_entities(weak_nodes_);
|
||||
|
||||
// Clean up any invalid nodes, if they were detected
|
||||
if (has_invalid_weak_nodes) {
|
||||
weak_nodes_.erase(
|
||||
remove_if(
|
||||
weak_nodes_.begin(), weak_nodes_.end(),
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
[](rclcpp::node_interfaces::NodeBaseInterface::WeakPtr i)
|
||||
{
|
||||
return i.expired();
|
||||
}
|
||||
// *INDENT-ON*
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
if (rcl_wait_set_resize_subscriptions(
|
||||
&waitset_, memory_strategy_->number_of_ready_subscriptions()) != RCL_RET_OK)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string("Couldn't resize the number of subscriptions in waitset : ") +
|
||||
rcl_get_error_string_safe());
|
||||
}
|
||||
|
||||
if (rcl_wait_set_resize_services(
|
||||
&waitset_, memory_strategy_->number_of_ready_services()) != RCL_RET_OK)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string("Couldn't resize the number of services in waitset : ") +
|
||||
rcl_get_error_string_safe());
|
||||
}
|
||||
|
||||
if (rcl_wait_set_resize_clients(
|
||||
&waitset_, memory_strategy_->number_of_ready_clients()) != RCL_RET_OK)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string("Couldn't resize the number of clients in waitset : ") +
|
||||
rcl_get_error_string_safe());
|
||||
}
|
||||
|
||||
if (rcl_wait_set_resize_guard_conditions(
|
||||
&waitset_, memory_strategy_->number_of_guard_conditions()) != RCL_RET_OK)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string("Couldn't resize the number of guard_conditions in waitset : ") +
|
||||
rcl_get_error_string_safe());
|
||||
}
|
||||
|
||||
if (rcl_wait_set_resize_timers(
|
||||
&waitset_, memory_strategy_->number_of_ready_timers()) != RCL_RET_OK)
|
||||
{
|
||||
throw std::runtime_error(
|
||||
std::string("Couldn't resize the number of timers in waitset : ") +
|
||||
rcl_get_error_string_safe());
|
||||
}
|
||||
|
||||
if (!memory_strategy_->add_handles_to_waitset(&waitset_)) {
|
||||
throw std::runtime_error("Couldn't fill waitset");
|
||||
}
|
||||
rcl_ret_t status =
|
||||
rcl_wait(&waitset_, std::chrono::duration_cast<std::chrono::nanoseconds>(timeout).count());
|
||||
if (status == RCL_RET_WAIT_SET_EMPTY) {
|
||||
fprintf(stderr, "Warning: empty waitset received in rcl_wait(). This should never happen.\n");
|
||||
} else if (status != RCL_RET_OK && status != RCL_RET_TIMEOUT) {
|
||||
throw std::runtime_error(std::string("rcl_wait() failed: ") + rcl_get_error_string_safe());
|
||||
}
|
||||
|
||||
// check the null handles in the waitset and remove them from the handles in memory strategy
|
||||
// for callback-based entities
|
||||
memory_strategy_->remove_null_handles(&waitset_);
|
||||
if (rcl_wait_set_clear_subscriptions(&waitset_) != RCL_RET_OK) {
|
||||
throw std::runtime_error("Couldn't clear subscriptions from waitset");
|
||||
}
|
||||
if (rcl_wait_set_clear_services(&waitset_) != RCL_RET_OK) {
|
||||
throw std::runtime_error("Couldn't clear servicess from waitset");
|
||||
}
|
||||
if (rcl_wait_set_clear_clients(&waitset_) != RCL_RET_OK) {
|
||||
throw std::runtime_error("Couldn't clear clients from waitset");
|
||||
}
|
||||
if (rcl_wait_set_clear_guard_conditions(&waitset_) != RCL_RET_OK) {
|
||||
throw std::runtime_error("Couldn't clear guard conditions from waitset");
|
||||
}
|
||||
if (rcl_wait_set_clear_timers(&waitset_) != RCL_RET_OK) {
|
||||
throw std::runtime_error("Couldn't clear timers from waitset");
|
||||
}
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
Executor::get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
if (!group) {
|
||||
return nullptr;
|
||||
}
|
||||
for (auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto callback_group = weak_group.lock();
|
||||
if (callback_group == group) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
Executor::get_group_by_timer(rclcpp::timer::TimerBase::SharedPtr timer)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_timer : group->get_timer_ptrs()) {
|
||||
auto t = weak_timer.lock();
|
||||
if (t == timer) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rclcpp::callback_group::CallbackGroup::SharedPtr();
|
||||
}
|
||||
|
||||
void
|
||||
Executor::get_next_timer(AnyExecutable::SharedPtr any_exec)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes_) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group || !group->can_be_taken_from().load()) {
|
||||
continue;
|
||||
}
|
||||
for (auto & timer_ref : group->get_timer_ptrs()) {
|
||||
auto timer = timer_ref.lock();
|
||||
if (timer && timer->is_ready()) {
|
||||
any_exec->timer = timer;
|
||||
any_exec->callback_group = group;
|
||||
node = get_node_by_group(group);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
AnyExecutable::SharedPtr
|
||||
Executor::get_next_ready_executable()
|
||||
{
|
||||
auto any_exec = memory_strategy_->instantiate_next_executable();
|
||||
// Check the timers to see if there are any that are ready, if so return
|
||||
get_next_timer(any_exec);
|
||||
if (any_exec->timer) {
|
||||
return any_exec;
|
||||
}
|
||||
// Check the subscriptions to see if there are any that are ready
|
||||
memory_strategy_->get_next_subscription(any_exec, weak_nodes_);
|
||||
if (any_exec->subscription || any_exec->subscription_intra_process) {
|
||||
return any_exec;
|
||||
}
|
||||
// Check the services to see if there are any that are ready
|
||||
memory_strategy_->get_next_service(any_exec, weak_nodes_);
|
||||
if (any_exec->service) {
|
||||
return any_exec;
|
||||
}
|
||||
// Check the clients to see if there are any that are ready
|
||||
memory_strategy_->get_next_client(any_exec, weak_nodes_);
|
||||
if (any_exec->client) {
|
||||
return any_exec;
|
||||
}
|
||||
// If there is no ready executable, return a null ptr
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
AnyExecutable::SharedPtr
|
||||
Executor::get_next_executable(std::chrono::nanoseconds timeout)
|
||||
{
|
||||
// Check to see if there are any subscriptions or timers needing service
|
||||
// TODO(wjwwood): improve run to run efficiency of this function
|
||||
auto any_exec = get_next_ready_executable();
|
||||
// If there are none
|
||||
if (!any_exec) {
|
||||
// Wait for subscriptions or timers to work on
|
||||
wait_for_work(timeout);
|
||||
if (!spinning.load()) {
|
||||
return nullptr;
|
||||
}
|
||||
// Try again
|
||||
any_exec = get_next_ready_executable();
|
||||
}
|
||||
// At this point any_exec should be valid with either a valid subscription
|
||||
// or a valid timer, or it should be a null shared_ptr
|
||||
if (any_exec) {
|
||||
// If it is valid, check to see if the group is mutually exclusive or
|
||||
// not, then mark it accordingly
|
||||
if (any_exec->callback_group && any_exec->callback_group->type() == \
|
||||
callback_group::CallbackGroupType::MutuallyExclusive)
|
||||
{
|
||||
// It should not have been taken otherwise
|
||||
assert(any_exec->callback_group->can_be_taken_from().load());
|
||||
// Set to false to indicate something is being run from this group
|
||||
// This is reset to true either when the any_exec is executed or when the
|
||||
// any_exec is destructued
|
||||
any_exec->callback_group->can_be_taken_from().store(false);
|
||||
}
|
||||
}
|
||||
return any_exec;
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
rclcpp::executor::operator<<(std::ostream & os, const FutureReturnCode & future_return_code)
|
||||
{
|
||||
return os << to_string(future_return_code);
|
||||
}
|
||||
|
||||
std::string
|
||||
rclcpp::executor::to_string(const FutureReturnCode & future_return_code)
|
||||
{
|
||||
using enum_type = std::underlying_type<FutureReturnCode>::type;
|
||||
std::string prefix = "Unknown enum value (";
|
||||
std::string ret_as_string = std::to_string(static_cast<enum_type>(future_return_code));
|
||||
switch (future_return_code) {
|
||||
case FutureReturnCode::SUCCESS:
|
||||
prefix = "SUCCESS (";
|
||||
break;
|
||||
case FutureReturnCode::INTERRUPTED:
|
||||
prefix = "INTERRUPTED (";
|
||||
break;
|
||||
case FutureReturnCode::TIMEOUT:
|
||||
prefix = "TIMEOUT (";
|
||||
break;
|
||||
}
|
||||
return prefix + ret_as_string + ")";
|
||||
}
|
||||
43
rclcpp/src/rclcpp/executors.cpp
Normal file
43
rclcpp/src/rclcpp/executors.cpp
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/executors.hpp"
|
||||
|
||||
void
|
||||
rclcpp::spin_some(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
exec.spin_node_some(node_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::spin_some(rclcpp::node::Node::SharedPtr node_ptr)
|
||||
{
|
||||
rclcpp::spin_some(node_ptr->get_node_base_interface());
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::spin(rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_ptr)
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor exec;
|
||||
exec.add_node(node_ptr);
|
||||
exec.spin();
|
||||
exec.remove_node(node_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::spin(rclcpp::node::Node::SharedPtr node_ptr)
|
||||
{
|
||||
rclcpp::spin(node_ptr->get_node_base_interface());
|
||||
}
|
||||
80
rclcpp/src/rclcpp/executors/multi_threaded_executor.cpp
Normal file
80
rclcpp/src/rclcpp/executors/multi_threaded_executor.cpp
Normal file
@@ -0,0 +1,80 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/executors/multi_threaded_executor.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/scope_exit.hpp"
|
||||
|
||||
using rclcpp::executors::multi_threaded_executor::MultiThreadedExecutor;
|
||||
|
||||
MultiThreadedExecutor::MultiThreadedExecutor(const rclcpp::executor::ExecutorArgs & args)
|
||||
: executor::Executor(args)
|
||||
{
|
||||
number_of_threads_ = std::thread::hardware_concurrency();
|
||||
if (number_of_threads_ == 0) {
|
||||
number_of_threads_ = 1;
|
||||
}
|
||||
}
|
||||
|
||||
MultiThreadedExecutor::~MultiThreadedExecutor() {}
|
||||
|
||||
void
|
||||
MultiThreadedExecutor::spin()
|
||||
{
|
||||
if (spinning.exchange(true)) {
|
||||
throw std::runtime_error("spin() called while already spinning");
|
||||
}
|
||||
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
|
||||
std::vector<std::thread> threads;
|
||||
size_t thread_id = 0;
|
||||
{
|
||||
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
|
||||
for (; thread_id < number_of_threads_ - 1; ++thread_id) {
|
||||
auto func = std::bind(&MultiThreadedExecutor::run, this, thread_id);
|
||||
threads.emplace_back(func);
|
||||
}
|
||||
}
|
||||
|
||||
run(thread_id);
|
||||
for (auto & thread : threads) {
|
||||
thread.join();
|
||||
}
|
||||
}
|
||||
|
||||
size_t
|
||||
MultiThreadedExecutor::get_number_of_threads()
|
||||
{
|
||||
return number_of_threads_;
|
||||
}
|
||||
|
||||
void
|
||||
MultiThreadedExecutor::run(size_t)
|
||||
{
|
||||
while (rclcpp::utilities::ok() && spinning.load()) {
|
||||
executor::AnyExecutable::SharedPtr any_exec;
|
||||
{
|
||||
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
|
||||
if (!rclcpp::utilities::ok() || !spinning.load()) {
|
||||
return;
|
||||
}
|
||||
any_exec = get_next_executable();
|
||||
}
|
||||
execute_any_executable(any_exec);
|
||||
}
|
||||
}
|
||||
36
rclcpp/src/rclcpp/executors/single_threaded_executor.cpp
Normal file
36
rclcpp/src/rclcpp/executors/single_threaded_executor.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/executors/single_threaded_executor.hpp"
|
||||
#include "rclcpp/scope_exit.hpp"
|
||||
|
||||
using rclcpp::executors::single_threaded_executor::SingleThreadedExecutor;
|
||||
|
||||
SingleThreadedExecutor::SingleThreadedExecutor(const rclcpp::executor::ExecutorArgs & args)
|
||||
: executor::Executor(args) {}
|
||||
|
||||
SingleThreadedExecutor::~SingleThreadedExecutor() {}
|
||||
|
||||
void
|
||||
SingleThreadedExecutor::spin()
|
||||
{
|
||||
if (spinning.exchange(true)) {
|
||||
throw std::runtime_error("spin() called while already spinning");
|
||||
}
|
||||
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
|
||||
while (rclcpp::utilities::ok() && spinning.load()) {
|
||||
auto any_exec = get_next_executable();
|
||||
execute_any_executable(any_exec);
|
||||
}
|
||||
}
|
||||
196
rclcpp/src/rclcpp/expand_topic_or_service_name.cpp
Normal file
196
rclcpp/src/rclcpp/expand_topic_or_service_name.cpp
Normal 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;
|
||||
}
|
||||
356
rclcpp/src/rclcpp/graph_listener.cpp
Normal file
356
rclcpp/src/rclcpp/graph_listener.cpp
Normal file
@@ -0,0 +1,356 @@
|
||||
// Copyright 2016 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/graph_listener.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <exception>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/types.h"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace graph_listener
|
||||
{
|
||||
|
||||
GraphListener::GraphListener()
|
||||
: is_started_(false), is_shutdown_(false), shutdown_guard_condition_(nullptr)
|
||||
{
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&interrupt_guard_condition_,
|
||||
rcl_guard_condition_get_default_options());
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
|
||||
}
|
||||
|
||||
shutdown_guard_condition_ = rclcpp::utilities::get_sigint_guard_condition(&wait_set_);
|
||||
}
|
||||
|
||||
GraphListener::~GraphListener()
|
||||
{
|
||||
this->shutdown();
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::start_if_not_started()
|
||||
{
|
||||
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
|
||||
if (is_shutdown_.load()) {
|
||||
throw GraphListenerShutdownError();
|
||||
}
|
||||
if (!is_started_) {
|
||||
// Initialize the wait set before starting.
|
||||
rcl_ret_t ret = rcl_wait_set_init(
|
||||
&wait_set_,
|
||||
0, // number_of_subscriptions
|
||||
2, // number_of_guard_conditions
|
||||
0, // number_of_timers
|
||||
0, // number_of_clients
|
||||
0, // number_of_services
|
||||
rcl_get_default_allocator());
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to initialize wait set");
|
||||
}
|
||||
// Register an on_shutdown hook to shtudown the graph listener.
|
||||
// This is important to ensure that the wait set is finalized before
|
||||
// destruction of static objects occurs.
|
||||
std::weak_ptr<GraphListener> weak_this = shared_from_this();
|
||||
rclcpp::utilities::on_shutdown([weak_this]() {
|
||||
auto shared_this = weak_this.lock();
|
||||
if (shared_this) {
|
||||
shared_this->shutdown();
|
||||
}
|
||||
});
|
||||
// Start the listener thread.
|
||||
listener_thread_ = std::thread(&GraphListener::run, this);
|
||||
is_started_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::run()
|
||||
{
|
||||
try {
|
||||
run_loop();
|
||||
} catch (const std::exception & exc) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp] caught %s exception in GraphListener thread: %s\n",
|
||||
rmw::impl::cpp::demangle(exc).c_str(),
|
||||
exc.what());
|
||||
std::rethrow_exception(std::current_exception());
|
||||
} catch (...) {
|
||||
fprintf(stderr, "[rclcpp] unknown error in GraphListener thread\n");
|
||||
std::rethrow_exception(std::current_exception());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::run_loop()
|
||||
{
|
||||
while (true) {
|
||||
// If shutdown() was called, exit.
|
||||
if (is_shutdown_.load()) {
|
||||
return;
|
||||
}
|
||||
rcl_ret_t ret;
|
||||
{
|
||||
// This "barrier" lock ensures that other functions can acquire the
|
||||
// node_graph_interfaces_mutex_ after waking up rcl_wait.
|
||||
std::lock_guard<std::mutex> nodes_barrier_lock(node_graph_interfaces_barrier_mutex_);
|
||||
// This is ownership is passed to nodes_lock in the next line.
|
||||
node_graph_interfaces_mutex_.lock();
|
||||
}
|
||||
// This lock is released when the loop continues or exits.
|
||||
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
|
||||
|
||||
// Resize the wait set if necessary.
|
||||
if (wait_set_.size_of_guard_conditions < (node_graph_interfaces_.size() + 2)) {
|
||||
ret = rcl_wait_set_resize_guard_conditions(&wait_set_, node_graph_interfaces_.size() + 2);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to resize wait set");
|
||||
}
|
||||
}
|
||||
// Clear the wait set's guard conditions.
|
||||
ret = rcl_wait_set_clear_guard_conditions(&wait_set_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to clear wait set");
|
||||
}
|
||||
// Put the interrupt guard condition in the wait set.
|
||||
ret = rcl_wait_set_add_guard_condition(&wait_set_, &interrupt_guard_condition_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to add interrupt guard condition to wait set");
|
||||
}
|
||||
// Put the shutdown guard condition in the wait set.
|
||||
ret = rcl_wait_set_add_guard_condition(&wait_set_, shutdown_guard_condition_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to add shutdown guard condition to wait set");
|
||||
}
|
||||
// Put graph guard conditions for each node into the wait set.
|
||||
for (const auto node_ptr : node_graph_interfaces_) {
|
||||
// Only wait on graph changes if some user of the node is watching.
|
||||
if (node_ptr->count_graph_users() == 0) {
|
||||
continue;
|
||||
}
|
||||
// Add the graph guard condition for the node to the wait set.
|
||||
auto graph_gc = node_ptr->get_graph_guard_condition();
|
||||
if (!graph_gc) {
|
||||
throw_from_rcl_error(RCL_RET_ERROR, "failed to get graph guard condition");
|
||||
}
|
||||
ret = rcl_wait_set_add_guard_condition(&wait_set_, graph_gc);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to add graph guard condition to wait set");
|
||||
}
|
||||
}
|
||||
|
||||
// Wait for: graph changes, interrupt, or shutdown/SIGINT
|
||||
ret = rcl_wait(&wait_set_, -1); // block for ever until a guard condition is triggered
|
||||
if (RCL_RET_TIMEOUT == ret) {
|
||||
throw std::runtime_error("rcl_wait unexpectedly timed out");
|
||||
}
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to wait on wait set");
|
||||
}
|
||||
|
||||
bool shutdown_guard_condition_triggered = false;
|
||||
// Check to see if the shutdown guard condition has been triggered.
|
||||
for (size_t i = 0; i < wait_set_.size_of_guard_conditions; ++i) {
|
||||
if (shutdown_guard_condition_ == wait_set_.guard_conditions[i]) {
|
||||
shutdown_guard_condition_triggered = true;
|
||||
}
|
||||
}
|
||||
// Notify nodes who's guard conditions are set (triggered).
|
||||
for (const auto node_ptr : node_graph_interfaces_) {
|
||||
auto graph_gc = node_ptr->get_graph_guard_condition();
|
||||
if (!graph_gc) {
|
||||
throw_from_rcl_error(RCL_RET_ERROR, "failed to get graph guard condition");
|
||||
}
|
||||
for (size_t i = 0; i < wait_set_.size_of_guard_conditions; ++i) {
|
||||
if (graph_gc == wait_set_.guard_conditions[i]) {
|
||||
node_ptr->notify_graph_change();
|
||||
}
|
||||
}
|
||||
if (shutdown_guard_condition_triggered) {
|
||||
// If shutdown, then notify the node of this as well.
|
||||
node_ptr->notify_shutdown();
|
||||
}
|
||||
}
|
||||
} // while (true)
|
||||
}
|
||||
|
||||
static void
|
||||
interrupt_(rcl_guard_condition_t * interrupt_guard_condition)
|
||||
{
|
||||
rcl_ret_t ret = rcl_trigger_guard_condition(interrupt_guard_condition);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to trigger the interrupt guard condition");
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
acquire_nodes_lock_(
|
||||
std::mutex * node_graph_interfaces_barrier_mutex,
|
||||
std::mutex * node_graph_interfaces_mutex,
|
||||
rcl_guard_condition_t * interrupt_guard_condition)
|
||||
{
|
||||
{
|
||||
// Acquire this lock to prevent the run loop from re-locking the
|
||||
// nodes_mutext_ after being woken up.
|
||||
std::lock_guard<std::mutex> nodes_barrier_lock(*node_graph_interfaces_barrier_mutex);
|
||||
// Trigger the interrupt guard condition to wake up rcl_wait.
|
||||
interrupt_(interrupt_guard_condition);
|
||||
node_graph_interfaces_mutex->lock();
|
||||
}
|
||||
}
|
||||
|
||||
static bool
|
||||
has_node_(
|
||||
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> * node_graph_interfaces,
|
||||
rclcpp::node_interfaces::NodeGraphInterface * node_graph)
|
||||
{
|
||||
for (const auto node_ptr : (*node_graph_interfaces)) {
|
||||
if (node_graph == node_ptr) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
GraphListener::has_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
|
||||
{
|
||||
if (!node_graph) {
|
||||
return false;
|
||||
}
|
||||
// Acquire the nodes mutex using the barrier to prevent the run loop from
|
||||
// re-locking the nodes mutex after being interrupted.
|
||||
acquire_nodes_lock_(
|
||||
&node_graph_interfaces_barrier_mutex_,
|
||||
&node_graph_interfaces_mutex_,
|
||||
&interrupt_guard_condition_);
|
||||
// Store the now acquired node_graph_interfaces_mutex_ in the scoped lock using adopt_lock.
|
||||
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
|
||||
return has_node_(&node_graph_interfaces_, node_graph);
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::add_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
|
||||
{
|
||||
if (!node_graph) {
|
||||
throw std::invalid_argument("node is nullptr");
|
||||
}
|
||||
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
|
||||
if (is_shutdown_.load()) {
|
||||
throw GraphListenerShutdownError();
|
||||
}
|
||||
|
||||
// Acquire the nodes mutex using the barrier to prevent the run loop from
|
||||
// re-locking the nodes mutex after being interrupted.
|
||||
acquire_nodes_lock_(
|
||||
&node_graph_interfaces_barrier_mutex_,
|
||||
&node_graph_interfaces_mutex_,
|
||||
&interrupt_guard_condition_);
|
||||
// Store the now acquired node_graph_interfaces_mutex_ in the scoped lock using adopt_lock.
|
||||
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
|
||||
if (has_node_(&node_graph_interfaces_, node_graph)) {
|
||||
throw NodeAlreadyAddedError();
|
||||
}
|
||||
node_graph_interfaces_.push_back(node_graph);
|
||||
// The run loop has already been interrupted by acquire_nodes_lock_() and
|
||||
// will evaluate the new node when nodes_lock releases the node_graph_interfaces_mutex_.
|
||||
}
|
||||
|
||||
static void
|
||||
remove_node_(
|
||||
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> * node_graph_interfaces,
|
||||
rclcpp::node_interfaces::NodeGraphInterface * node_graph)
|
||||
{
|
||||
// Remove the node if it is found.
|
||||
for (auto it = node_graph_interfaces->begin(); it != node_graph_interfaces->end(); ++it) {
|
||||
if (node_graph == *it) {
|
||||
// Found the node, remove it.
|
||||
node_graph_interfaces->erase(it);
|
||||
// Now trigger the interrupt guard condition to make sure
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Not found in the loop.
|
||||
throw NodeNotFoundError();
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_graph)
|
||||
{
|
||||
if (!node_graph) {
|
||||
throw std::invalid_argument("node is nullptr");
|
||||
}
|
||||
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
|
||||
if (is_shutdown()) {
|
||||
// If shutdown, then the run loop has been joined, so we can remove them directly.
|
||||
return remove_node_(&node_graph_interfaces_, node_graph);
|
||||
}
|
||||
// Otherwise, first interrupt and lock against the run loop to safely remove the node.
|
||||
// Acquire the nodes mutex using the barrier to prevent the run loop from
|
||||
// re-locking the nodes mutex after being interrupted.
|
||||
acquire_nodes_lock_(
|
||||
&node_graph_interfaces_barrier_mutex_,
|
||||
&node_graph_interfaces_mutex_,
|
||||
&interrupt_guard_condition_);
|
||||
// Store the now acquired node_graph_interfaces_mutex_ in the scoped lock using adopt_lock.
|
||||
std::lock_guard<std::mutex> nodes_lock(node_graph_interfaces_mutex_, std::adopt_lock);
|
||||
remove_node_(&node_graph_interfaces_, node_graph);
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::shutdown()
|
||||
{
|
||||
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
|
||||
if (!is_shutdown_.exchange(true)) {
|
||||
if (is_started_) {
|
||||
interrupt_(&interrupt_guard_condition_);
|
||||
listener_thread_.join();
|
||||
}
|
||||
rcl_ret_t ret = rcl_guard_condition_fini(&interrupt_guard_condition_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to finalize interrupt guard condition");
|
||||
}
|
||||
if (shutdown_guard_condition_) {
|
||||
rclcpp::utilities::release_sigint_guard_condition(&wait_set_);
|
||||
shutdown_guard_condition_ = nullptr;
|
||||
}
|
||||
if (is_started_) {
|
||||
ret = rcl_wait_set_fini(&wait_set_);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to finalize wait set");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
GraphListener::is_shutdown()
|
||||
{
|
||||
return is_shutdown_.load();
|
||||
}
|
||||
|
||||
} // namespace graph_listener
|
||||
} // namespace rclcpp
|
||||
82
rclcpp/src/rclcpp/intra_process_manager.cpp
Normal file
82
rclcpp/src/rclcpp/intra_process_manager.cpp
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/intra_process_manager.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace intra_process_manager
|
||||
{
|
||||
|
||||
static std::atomic<uint64_t> _next_unique_id {1};
|
||||
|
||||
IntraProcessManager::IntraProcessManager(
|
||||
rclcpp::intra_process_manager::IntraProcessManagerImplBase::SharedPtr impl)
|
||||
: impl_(impl)
|
||||
{}
|
||||
|
||||
IntraProcessManager::~IntraProcessManager()
|
||||
{}
|
||||
|
||||
uint64_t
|
||||
IntraProcessManager::add_subscription(
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr subscription)
|
||||
{
|
||||
auto id = IntraProcessManager::get_next_unique_id();
|
||||
impl_->add_subscription(id, subscription);
|
||||
return id;
|
||||
}
|
||||
|
||||
void
|
||||
IntraProcessManager::remove_subscription(uint64_t intra_process_subscription_id)
|
||||
{
|
||||
impl_->remove_subscription(intra_process_subscription_id);
|
||||
}
|
||||
|
||||
void
|
||||
IntraProcessManager::remove_publisher(uint64_t intra_process_publisher_id)
|
||||
{
|
||||
impl_->remove_publisher(intra_process_publisher_id);
|
||||
}
|
||||
|
||||
bool
|
||||
IntraProcessManager::matches_any_publishers(const rmw_gid_t * id) const
|
||||
{
|
||||
return impl_->matches_any_publishers(id);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
IntraProcessManager::get_next_unique_id()
|
||||
{
|
||||
auto next_id = _next_unique_id.fetch_add(1, std::memory_order_relaxed);
|
||||
// Check for rollover (we started at 1).
|
||||
if (0 == next_id) {
|
||||
// This puts a technical limit on the number of times you can add a publisher or subscriber.
|
||||
// But even if you could add (and remove) them at 1 kHz (very optimistic rate)
|
||||
// it would still be a very long time before you could exhaust the pool of id's:
|
||||
// 2^64 / 1000 times per sec / 60 sec / 60 min / 24 hours / 365 days = 584,942,417 years
|
||||
// So around 585 million years. Even at 1 GHz, it would take 585 years.
|
||||
// I think it's safe to avoid trying to handle overflow.
|
||||
// If we roll over then it's most likely a bug.
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::overflow_error(
|
||||
"exhausted the unique id's for publishers and subscribers in this process "
|
||||
"(congratulations your computer is either extremely fast or extremely old)");
|
||||
// *INDENT-ON*
|
||||
}
|
||||
return next_id;
|
||||
}
|
||||
|
||||
} // namespace intra_process_manager
|
||||
} // namespace rclcpp
|
||||
23
rclcpp/src/rclcpp/intra_process_manager_impl.cpp
Normal file
23
rclcpp/src/rclcpp/intra_process_manager_impl.cpp
Normal file
@@ -0,0 +1,23 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/intra_process_manager_impl.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
rclcpp::intra_process_manager::IntraProcessManagerImplBase::SharedPtr
|
||||
rclcpp::intra_process_manager::create_default_impl()
|
||||
{
|
||||
return std::make_shared<IntraProcessManagerImpl<>>();
|
||||
}
|
||||
27
rclcpp/src/rclcpp/memory_strategies.cpp
Normal file
27
rclcpp/src/rclcpp/memory_strategies.cpp
Normal file
@@ -0,0 +1,27 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/memory_strategies.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/strategies/allocator_memory_strategy.hpp"
|
||||
|
||||
using rclcpp::memory_strategies::allocator_memory_strategy::AllocatorMemoryStrategy;
|
||||
|
||||
rclcpp::memory_strategy::MemoryStrategy::SharedPtr
|
||||
rclcpp::memory_strategies::create_default_strategy()
|
||||
{
|
||||
return std::make_shared<AllocatorMemoryStrategy<>>();
|
||||
}
|
||||
197
rclcpp/src/rclcpp/memory_strategy.cpp
Normal file
197
rclcpp/src/rclcpp/memory_strategy.cpp
Normal file
@@ -0,0 +1,197 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
|
||||
using rclcpp::memory_strategy::MemoryStrategy;
|
||||
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr
|
||||
MemoryStrategy::get_subscription_by_handle(
|
||||
const rcl_subscription_t * subscriber_handle, const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_subscription : group->get_subscription_ptrs()) {
|
||||
auto subscription = weak_subscription.lock();
|
||||
if (subscription) {
|
||||
if (subscription->get_subscription_handle() == subscriber_handle) {
|
||||
return subscription;
|
||||
}
|
||||
if (subscription->get_intra_process_subscription_handle() == subscriber_handle) {
|
||||
return subscription;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rclcpp::service::ServiceBase::SharedPtr
|
||||
MemoryStrategy::get_service_by_handle(const rcl_service_t * service_handle,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_service : group->get_service_ptrs()) {
|
||||
auto service = weak_service.lock();
|
||||
if (service && service->get_service_handle() == service_handle) {
|
||||
return service;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rclcpp::client::ClientBase::SharedPtr
|
||||
MemoryStrategy::get_client_by_handle(const rcl_client_t * client_handle,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_client : group->get_client_ptrs()) {
|
||||
auto client = weak_client.lock();
|
||||
if (client && client->get_client_handle() == client_handle) {
|
||||
return client;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
MemoryStrategy::get_node_by_group(rclcpp::callback_group::CallbackGroup::SharedPtr group,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
if (!group) {
|
||||
return nullptr;
|
||||
}
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto callback_group = weak_group.lock();
|
||||
if (callback_group == group) {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
MemoryStrategy::get_group_by_subscription(
|
||||
rclcpp::subscription::SubscriptionBase::SharedPtr subscription,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_sub : group->get_subscription_ptrs()) {
|
||||
auto sub = weak_sub.lock();
|
||||
if (sub == subscription) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
MemoryStrategy::get_group_by_service(
|
||||
rclcpp::service::ServiceBase::SharedPtr service,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_serv : group->get_service_ptrs()) {
|
||||
auto serv = weak_serv.lock();
|
||||
if (serv && serv == service) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
MemoryStrategy::get_group_by_client(
|
||||
rclcpp::client::ClientBase::SharedPtr client,
|
||||
const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
for (auto & weak_node : weak_nodes) {
|
||||
auto node = weak_node.lock();
|
||||
if (!node) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_group : node->get_callback_groups()) {
|
||||
auto group = weak_group.lock();
|
||||
if (!group) {
|
||||
continue;
|
||||
}
|
||||
for (auto & weak_client : group->get_client_ptrs()) {
|
||||
auto cli = weak_client.lock();
|
||||
if (cli && cli == client) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
225
rclcpp/src/rclcpp/node.cpp
Normal file
225
rclcpp/src/rclcpp/node.cpp
Normal file
@@ -0,0 +1,225 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/graph_listener.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph.hpp"
|
||||
#include "rclcpp/node_interfaces/node_parameters.hpp"
|
||||
#include "rclcpp/node_interfaces/node_services.hpp"
|
||||
#include "rclcpp/node_interfaces/node_timers.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics.hpp"
|
||||
|
||||
using rclcpp::node::Node;
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
|
||||
Node::Node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
bool use_intra_process_comms)
|
||||
: Node(
|
||||
node_name,
|
||||
namespace_,
|
||||
rclcpp::contexts::default_context::get_global_default_context(),
|
||||
use_intra_process_comms)
|
||||
{}
|
||||
|
||||
Node::Node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
rclcpp::context::Context::SharedPtr context,
|
||||
bool use_intra_process_comms)
|
||||
: node_base_(new rclcpp::node_interfaces::NodeBase(node_name, namespace_, context)),
|
||||
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
|
||||
node_timers_(new rclcpp::node_interfaces::NodeTimers(node_base_.get())),
|
||||
node_topics_(new rclcpp::node_interfaces::NodeTopics(node_base_.get())),
|
||||
node_services_(new rclcpp::node_interfaces::NodeServices(node_base_.get())),
|
||||
node_parameters_(new rclcpp::node_interfaces::NodeParameters(
|
||||
node_topics_.get(),
|
||||
use_intra_process_comms
|
||||
)),
|
||||
use_intra_process_comms_(use_intra_process_comms)
|
||||
{
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{}
|
||||
|
||||
const char *
|
||||
Node::get_name() const
|
||||
{
|
||||
return node_base_->get_name();
|
||||
}
|
||||
|
||||
const char *
|
||||
Node::get_namespace() const
|
||||
{
|
||||
return node_base_->get_namespace();
|
||||
}
|
||||
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
Node::create_callback_group(
|
||||
rclcpp::callback_group::CallbackGroupType group_type)
|
||||
{
|
||||
return node_base_->create_callback_group(group_type);
|
||||
}
|
||||
|
||||
bool
|
||||
Node::group_in_node(rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
return node_base_->callback_group_in_node(group);
|
||||
}
|
||||
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
Node::set_parameters(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
|
||||
{
|
||||
return node_parameters_->set_parameters(parameters);
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
Node::set_parameters_atomically(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
|
||||
{
|
||||
return node_parameters_->set_parameters_atomically(parameters);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::parameter::ParameterVariant>
|
||||
Node::get_parameters(
|
||||
const std::vector<std::string> & names) const
|
||||
{
|
||||
return node_parameters_->get_parameters(names);
|
||||
}
|
||||
|
||||
rclcpp::parameter::ParameterVariant
|
||||
Node::get_parameter(const std::string & name) const
|
||||
{
|
||||
return node_parameters_->get_parameter(name);
|
||||
}
|
||||
|
||||
bool Node::get_parameter(const std::string & name,
|
||||
rclcpp::parameter::ParameterVariant & parameter) const
|
||||
{
|
||||
return node_parameters_->get_parameter(name, parameter);
|
||||
}
|
||||
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor>
|
||||
Node::describe_parameters(
|
||||
const std::vector<std::string> & names) const
|
||||
{
|
||||
return node_parameters_->describe_parameters(names);
|
||||
}
|
||||
|
||||
std::vector<uint8_t>
|
||||
Node::get_parameter_types(
|
||||
const std::vector<std::string> & names) const
|
||||
{
|
||||
return node_parameters_->get_parameter_types(names);
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
Node::list_parameters(
|
||||
const std::vector<std::string> & prefixes, uint64_t depth) const
|
||||
{
|
||||
return node_parameters_->list_parameters(prefixes, depth);
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
Node::get_topic_names_and_types() const
|
||||
{
|
||||
return node_graph_->get_topic_names_and_types();
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>>
|
||||
Node::get_service_names_and_types() const
|
||||
{
|
||||
return node_graph_->get_service_names_and_types();
|
||||
}
|
||||
|
||||
size_t
|
||||
Node::count_publishers(const std::string & topic_name) const
|
||||
{
|
||||
return node_graph_->count_publishers(topic_name);
|
||||
}
|
||||
|
||||
size_t
|
||||
Node::count_subscribers(const std::string & topic_name) const
|
||||
{
|
||||
return node_graph_->count_subscribers(topic_name);
|
||||
}
|
||||
|
||||
const std::vector<rclcpp::callback_group::CallbackGroup::WeakPtr> &
|
||||
Node::get_callback_groups() const
|
||||
{
|
||||
return node_base_->get_callback_groups();
|
||||
}
|
||||
|
||||
rclcpp::event::Event::SharedPtr
|
||||
Node::get_graph_event()
|
||||
{
|
||||
return node_graph_->get_graph_event();
|
||||
}
|
||||
|
||||
void
|
||||
Node::wait_for_graph_change(
|
||||
rclcpp::event::Event::SharedPtr event,
|
||||
std::chrono::nanoseconds timeout)
|
||||
{
|
||||
node_graph_->wait_for_graph_change(event, timeout);
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr
|
||||
Node::get_node_base_interface()
|
||||
{
|
||||
return node_base_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
|
||||
Node::get_node_graph_interface()
|
||||
{
|
||||
return node_graph_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
|
||||
Node::get_node_timers_interface()
|
||||
{
|
||||
return node_timers_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr
|
||||
Node::get_node_topics_interface()
|
||||
{
|
||||
return node_topics_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
|
||||
Node::get_node_services_interface()
|
||||
{
|
||||
return node_services_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
|
||||
Node::get_node_parameters_interface()
|
||||
{
|
||||
return node_parameters_;
|
||||
}
|
||||
260
rclcpp/src/rclcpp/node_interfaces/node_base.cpp
Normal file
260
rclcpp/src/rclcpp/node_interfaces/node_base.cpp
Normal 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(¬ify_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(¬ify_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(¬ify_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 ¬ify_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_);
|
||||
}
|
||||
312
rclcpp/src/rclcpp/node_interfaces/node_graph.cpp
Normal file
312
rclcpp/src/rclcpp/node_interfaces/node_graph.cpp
Normal 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();
|
||||
}
|
||||
241
rclcpp/src/rclcpp/node_interfaces/node_parameters.cpp
Normal file
241
rclcpp/src/rclcpp/node_interfaces/node_parameters.cpp
Normal 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;
|
||||
}
|
||||
78
rclcpp/src/rclcpp/node_interfaces/node_services.cpp
Normal file
78
rclcpp/src/rclcpp/node_interfaces/node_services.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
47
rclcpp/src/rclcpp/node_interfaces/node_timers.cpp
Normal file
47
rclcpp/src/rclcpp/node_interfaces/node_timers.cpp
Normal 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());
|
||||
}
|
||||
}
|
||||
131
rclcpp/src/rclcpp/node_interfaces/node_topics.cpp
Normal file
131
rclcpp/src/rclcpp/node_interfaces/node_topics.cpp
Normal 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()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
279
rclcpp/src/rclcpp/parameter.cpp
Normal file
279
rclcpp/src/rclcpp/parameter.cpp
Normal file
@@ -0,0 +1,279 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <ostream>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
using rclcpp::parameter::ParameterType;
|
||||
using rclcpp::parameter::ParameterVariant;
|
||||
|
||||
ParameterVariant::ParameterVariant()
|
||||
: name_("")
|
||||
{
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_NOT_SET;
|
||||
}
|
||||
|
||||
ParameterVariant::ParameterVariant(const std::string & name, const bool bool_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.bool_value = bool_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BOOL;
|
||||
}
|
||||
|
||||
ParameterVariant::ParameterVariant(const std::string & name, const int int_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.integer_value = int_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
|
||||
}
|
||||
|
||||
ParameterVariant::ParameterVariant(const std::string & name, const int64_t int_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.integer_value = int_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_INTEGER;
|
||||
}
|
||||
|
||||
ParameterVariant::ParameterVariant(const std::string & name, const float double_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.double_value = double_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
|
||||
}
|
||||
|
||||
ParameterVariant::ParameterVariant(const std::string & name, const double double_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.double_value = double_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_DOUBLE;
|
||||
}
|
||||
|
||||
ParameterVariant::ParameterVariant(const std::string & name, const std::string & string_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.string_value = string_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_STRING;
|
||||
}
|
||||
|
||||
ParameterVariant::ParameterVariant(const std::string & name, const char * string_value)
|
||||
: ParameterVariant(name, std::string(string_value))
|
||||
{}
|
||||
|
||||
ParameterVariant::ParameterVariant(
|
||||
const std::string & name, const std::vector<uint8_t> & bytes_value)
|
||||
: name_(name)
|
||||
{
|
||||
value_.bytes_value = bytes_value;
|
||||
value_.type = rcl_interfaces::msg::ParameterType::PARAMETER_BYTES;
|
||||
}
|
||||
|
||||
ParameterType
|
||||
ParameterVariant::get_type() const
|
||||
{
|
||||
return static_cast<ParameterType>(value_.type);
|
||||
}
|
||||
|
||||
std::string
|
||||
ParameterVariant::get_type_name() const
|
||||
{
|
||||
switch (get_type()) {
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_BOOL:
|
||||
return "bool";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_INTEGER:
|
||||
return "integer";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_DOUBLE:
|
||||
return "double";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_STRING:
|
||||
return "string";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_BYTES:
|
||||
return "bytes";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_NOT_SET:
|
||||
return "not set";
|
||||
default:
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(
|
||||
"Unexpected type from ParameterVariant: " + std::to_string(get_type()));
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
|
||||
const std::string &
|
||||
ParameterVariant::get_name() const
|
||||
{
|
||||
return name_;
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ParameterValue
|
||||
ParameterVariant::get_parameter_value() const
|
||||
{
|
||||
return value_;
|
||||
}
|
||||
|
||||
int64_t
|
||||
ParameterVariant::as_int() const
|
||||
{
|
||||
return get_value<ParameterType::PARAMETER_INTEGER>();
|
||||
}
|
||||
|
||||
double
|
||||
ParameterVariant::as_double() const
|
||||
{
|
||||
return get_value<ParameterType::PARAMETER_DOUBLE>();
|
||||
}
|
||||
|
||||
const std::string &
|
||||
ParameterVariant::as_string() const
|
||||
{
|
||||
return get_value<ParameterType::PARAMETER_STRING>();
|
||||
}
|
||||
|
||||
bool
|
||||
ParameterVariant::as_bool() const
|
||||
{
|
||||
return get_value<ParameterType::PARAMETER_BOOL>();
|
||||
}
|
||||
|
||||
const std::vector<uint8_t> &
|
||||
ParameterVariant::as_bytes() const
|
||||
{
|
||||
return get_value<ParameterType::PARAMETER_BYTES>();
|
||||
}
|
||||
|
||||
ParameterVariant
|
||||
ParameterVariant::from_parameter(const rcl_interfaces::msg::Parameter & parameter)
|
||||
{
|
||||
switch (parameter.value.type) {
|
||||
case PARAMETER_BOOL:
|
||||
return ParameterVariant(parameter.name, parameter.value.bool_value);
|
||||
case PARAMETER_INTEGER:
|
||||
return ParameterVariant(parameter.name, parameter.value.integer_value);
|
||||
case PARAMETER_DOUBLE:
|
||||
return ParameterVariant(parameter.name, parameter.value.double_value);
|
||||
case PARAMETER_STRING:
|
||||
return ParameterVariant(parameter.name, parameter.value.string_value);
|
||||
case PARAMETER_BYTES:
|
||||
return ParameterVariant(parameter.name, parameter.value.bytes_value);
|
||||
case PARAMETER_NOT_SET:
|
||||
throw std::runtime_error("Type from ParameterValue is not set");
|
||||
default:
|
||||
// TODO(wjwwood): use custom exception
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(
|
||||
"Unexpected type from ParameterVariant: " + std::to_string(parameter.value.type));
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::Parameter
|
||||
ParameterVariant::to_parameter()
|
||||
{
|
||||
rcl_interfaces::msg::Parameter parameter;
|
||||
parameter.name = name_;
|
||||
parameter.value = value_;
|
||||
return parameter;
|
||||
}
|
||||
|
||||
std::string
|
||||
ParameterVariant::value_to_string() const
|
||||
{
|
||||
switch (get_type()) {
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_BOOL:
|
||||
return as_bool() ? "true" : "false";
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_INTEGER:
|
||||
return std::to_string(as_int());
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_DOUBLE:
|
||||
return std::to_string(as_double());
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_STRING:
|
||||
return as_string();
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_BYTES:
|
||||
{
|
||||
std::stringstream bytes;
|
||||
bool first_byte = true;
|
||||
bytes << "[" << std::hex;
|
||||
for (auto & byte : as_bytes()) {
|
||||
bytes << "0x" << byte;
|
||||
if (!first_byte) {
|
||||
bytes << ", ";
|
||||
} else {
|
||||
first_byte = false;
|
||||
}
|
||||
}
|
||||
return bytes.str();
|
||||
}
|
||||
case rclcpp::parameter::ParameterType::PARAMETER_NOT_SET:
|
||||
return "not set";
|
||||
default:
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(
|
||||
"Unexpected type from ParameterVariant: " + std::to_string(get_type()));
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
|
||||
std::string
|
||||
rclcpp::parameter::_to_json_dict_entry(const ParameterVariant & param)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "\"" << param.get_name() << "\": ";
|
||||
ss << "{\"type\": \"" << param.get_type_name() << "\", ";
|
||||
ss << "\"value\": \"" << param.value_to_string() << "\"}";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
rclcpp::parameter::operator<<(std::ostream & os, const rclcpp::parameter::ParameterVariant & pv)
|
||||
{
|
||||
os << std::to_string(pv);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream &
|
||||
rclcpp::parameter::operator<<(std::ostream & os, const std::vector<ParameterVariant> & parameters)
|
||||
{
|
||||
os << std::to_string(parameters);
|
||||
return os;
|
||||
}
|
||||
|
||||
std::string
|
||||
std::to_string(const rclcpp::parameter::ParameterVariant & param)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "{\"name\": \"" << param.get_name() << "\", ";
|
||||
ss << "\"type\": \"" << param.get_type_name() << "\", ";
|
||||
ss << "\"value\": \"" << param.value_to_string() << "\"}";
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string
|
||||
std::to_string(const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "{";
|
||||
bool first = true;
|
||||
for (const auto & pv : parameters) {
|
||||
if (first == false) {
|
||||
ss << ", ";
|
||||
} else {
|
||||
first = false;
|
||||
}
|
||||
ss << rclcpp::parameter::_to_json_dict_entry(pv);
|
||||
}
|
||||
ss << "}";
|
||||
return ss.str();
|
||||
}
|
||||
333
rclcpp/src/rclcpp/parameter_client.cpp
Normal file
333
rclcpp/src/rclcpp/parameter_client.cpp
Normal file
@@ -0,0 +1,333 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/parameter_client.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using rclcpp::parameter_client::AsyncParametersClient;
|
||||
using rclcpp::parameter_client::SyncParametersClient;
|
||||
|
||||
AsyncParametersClient::AsyncParametersClient(
|
||||
const rclcpp::node::Node::SharedPtr node,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: node_(node)
|
||||
{
|
||||
if (remote_node_name != "") {
|
||||
remote_node_name_ = remote_node_name;
|
||||
} else {
|
||||
remote_node_name_ = node_->get_name();
|
||||
}
|
||||
get_parameters_client_ = node_->create_client<rcl_interfaces::srv::GetParameters>(
|
||||
remote_node_name_ + "__get_parameters", qos_profile);
|
||||
get_parameter_types_client_ = node_->create_client<rcl_interfaces::srv::GetParameterTypes>(
|
||||
remote_node_name_ + "__get_parameter_types", qos_profile);
|
||||
set_parameters_client_ = node_->create_client<rcl_interfaces::srv::SetParameters>(
|
||||
remote_node_name_ + "__set_parameters", qos_profile);
|
||||
list_parameters_client_ = node_->create_client<rcl_interfaces::srv::ListParameters>(
|
||||
remote_node_name_ + "__list_parameters", qos_profile);
|
||||
describe_parameters_client_ = node_->create_client<rcl_interfaces::srv::DescribeParameters>(
|
||||
remote_node_name_ + "__describe_parameters", qos_profile);
|
||||
}
|
||||
|
||||
std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>
|
||||
AsyncParametersClient::get_parameters(
|
||||
const std::vector<std::string> & names,
|
||||
std::function<
|
||||
void(std::shared_future<std::vector<rclcpp::parameter::ParameterVariant>>)
|
||||
> callback)
|
||||
{
|
||||
auto promise_result =
|
||||
std::make_shared<std::promise<std::vector<rclcpp::parameter::ParameterVariant>>>();
|
||||
auto future_result = promise_result->get_future().share();
|
||||
|
||||
auto request = std::make_shared<rcl_interfaces::srv::GetParameters::Request>();
|
||||
request->names = names;
|
||||
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
get_parameters_client_->async_send_request(
|
||||
request,
|
||||
[request, promise_result, future_result, &callback](
|
||||
rclcpp::client::Client<rcl_interfaces::srv::GetParameters>::SharedFuture cb_f)
|
||||
{
|
||||
std::vector<rclcpp::parameter::ParameterVariant> parameter_variants;
|
||||
auto & pvalues = cb_f.get()->values;
|
||||
|
||||
for (auto & pvalue : pvalues) {
|
||||
auto i = &pvalue - &pvalues[0];
|
||||
rcl_interfaces::msg::Parameter parameter;
|
||||
parameter.name = request->names[i];
|
||||
parameter.value = pvalue;
|
||||
parameter_variants.push_back(rclcpp::parameter::ParameterVariant::from_parameter(
|
||||
parameter));
|
||||
}
|
||||
|
||||
promise_result->set_value(parameter_variants);
|
||||
if (callback != nullptr) {
|
||||
callback(future_result);
|
||||
}
|
||||
}
|
||||
);
|
||||
// *INDENT-ON*
|
||||
|
||||
return future_result;
|
||||
}
|
||||
|
||||
std::shared_future<std::vector<rclcpp::parameter::ParameterType>>
|
||||
AsyncParametersClient::get_parameter_types(
|
||||
const std::vector<std::string> & names,
|
||||
std::function<
|
||||
void(std::shared_future<std::vector<rclcpp::parameter::ParameterType>>)
|
||||
> callback)
|
||||
{
|
||||
auto promise_result =
|
||||
std::make_shared<std::promise<std::vector<rclcpp::parameter::ParameterType>>>();
|
||||
auto future_result = promise_result->get_future().share();
|
||||
|
||||
auto request = std::make_shared<rcl_interfaces::srv::GetParameterTypes::Request>();
|
||||
request->names = names;
|
||||
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
get_parameter_types_client_->async_send_request(
|
||||
request,
|
||||
[promise_result, future_result, &callback](
|
||||
rclcpp::client::Client<rcl_interfaces::srv::GetParameterTypes>::SharedFuture cb_f)
|
||||
{
|
||||
std::vector<rclcpp::parameter::ParameterType> types;
|
||||
auto & pts = cb_f.get()->types;
|
||||
for (auto & pt : pts) {
|
||||
pts.push_back(static_cast<rclcpp::parameter::ParameterType>(pt));
|
||||
}
|
||||
promise_result->set_value(types);
|
||||
if (callback != nullptr) {
|
||||
callback(future_result);
|
||||
}
|
||||
}
|
||||
);
|
||||
// *INDENT-ON*
|
||||
|
||||
return future_result;
|
||||
}
|
||||
|
||||
std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>
|
||||
AsyncParametersClient::set_parameters(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
|
||||
std::function<
|
||||
void(std::shared_future<std::vector<rcl_interfaces::msg::SetParametersResult>>)
|
||||
> callback)
|
||||
{
|
||||
auto promise_result =
|
||||
std::make_shared<std::promise<std::vector<rcl_interfaces::msg::SetParametersResult>>>();
|
||||
auto future_result = promise_result->get_future().share();
|
||||
|
||||
auto request = std::make_shared<rcl_interfaces::srv::SetParameters::Request>();
|
||||
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
std::transform(parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
|
||||
[](rclcpp::parameter::ParameterVariant p) {
|
||||
return p.to_parameter();
|
||||
}
|
||||
);
|
||||
|
||||
set_parameters_client_->async_send_request(
|
||||
request,
|
||||
[promise_result, future_result, &callback](
|
||||
rclcpp::client::Client<rcl_interfaces::srv::SetParameters>::SharedFuture cb_f)
|
||||
{
|
||||
promise_result->set_value(cb_f.get()->results);
|
||||
if (callback != nullptr) {
|
||||
callback(future_result);
|
||||
}
|
||||
}
|
||||
);
|
||||
// *INDENT-ON*
|
||||
|
||||
return future_result;
|
||||
}
|
||||
|
||||
std::shared_future<rcl_interfaces::msg::SetParametersResult>
|
||||
AsyncParametersClient::set_parameters_atomically(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters,
|
||||
std::function<
|
||||
void(std::shared_future<rcl_interfaces::msg::SetParametersResult>)
|
||||
> callback)
|
||||
{
|
||||
auto promise_result =
|
||||
std::make_shared<std::promise<rcl_interfaces::msg::SetParametersResult>>();
|
||||
auto future_result = promise_result->get_future().share();
|
||||
|
||||
auto request = std::make_shared<rcl_interfaces::srv::SetParametersAtomically::Request>();
|
||||
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
std::transform(parameters.begin(), parameters.end(), std::back_inserter(request->parameters),
|
||||
[](rclcpp::parameter::ParameterVariant p) {
|
||||
return p.to_parameter();
|
||||
}
|
||||
);
|
||||
|
||||
set_parameters_atomically_client_->async_send_request(
|
||||
request,
|
||||
[promise_result, future_result, &callback](
|
||||
rclcpp::client::Client<rcl_interfaces::srv::SetParametersAtomically>::SharedFuture cb_f)
|
||||
{
|
||||
promise_result->set_value(cb_f.get()->result);
|
||||
if (callback != nullptr) {
|
||||
callback(future_result);
|
||||
}
|
||||
}
|
||||
);
|
||||
// *INDENT-ON*
|
||||
|
||||
return future_result;
|
||||
}
|
||||
|
||||
std::shared_future<rcl_interfaces::msg::ListParametersResult>
|
||||
AsyncParametersClient::list_parameters(
|
||||
const std::vector<std::string> & prefixes,
|
||||
uint64_t depth,
|
||||
std::function<
|
||||
void(std::shared_future<rcl_interfaces::msg::ListParametersResult>)
|
||||
> callback)
|
||||
{
|
||||
auto promise_result =
|
||||
std::make_shared<std::promise<rcl_interfaces::msg::ListParametersResult>>();
|
||||
auto future_result = promise_result->get_future().share();
|
||||
|
||||
auto request = std::make_shared<rcl_interfaces::srv::ListParameters::Request>();
|
||||
request->prefixes = prefixes;
|
||||
request->depth = depth;
|
||||
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
list_parameters_client_->async_send_request(
|
||||
request,
|
||||
[promise_result, future_result, &callback](
|
||||
rclcpp::client::Client<rcl_interfaces::srv::ListParameters>::SharedFuture cb_f)
|
||||
{
|
||||
promise_result->set_value(cb_f.get()->result);
|
||||
if (callback != nullptr) {
|
||||
callback(future_result);
|
||||
}
|
||||
}
|
||||
);
|
||||
// *INDENT-ON*
|
||||
|
||||
return future_result;
|
||||
}
|
||||
|
||||
SyncParametersClient::SyncParametersClient(
|
||||
rclcpp::node::Node::SharedPtr node,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: node_(node)
|
||||
{
|
||||
executor_ = std::make_shared<rclcpp::executors::SingleThreadedExecutor>();
|
||||
async_parameters_client_ = std::make_shared<AsyncParametersClient>(node, "", qos_profile);
|
||||
}
|
||||
|
||||
SyncParametersClient::SyncParametersClient(
|
||||
rclcpp::executor::Executor::SharedPtr executor,
|
||||
rclcpp::node::Node::SharedPtr node,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: executor_(executor), node_(node)
|
||||
{
|
||||
async_parameters_client_ = std::make_shared<AsyncParametersClient>(node, "", qos_profile);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::parameter::ParameterVariant>
|
||||
SyncParametersClient::get_parameters(const std::vector<std::string> & parameter_names)
|
||||
{
|
||||
auto f = async_parameters_client_->get_parameters(parameter_names);
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
|
||||
rclcpp::executor::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
}
|
||||
// Return an empty vector if unsuccessful
|
||||
return std::vector<rclcpp::parameter::ParameterVariant>();
|
||||
}
|
||||
|
||||
bool
|
||||
SyncParametersClient::has_parameter(const std::string & parameter_name)
|
||||
{
|
||||
std::vector<std::string> names;
|
||||
names.push_back(parameter_name);
|
||||
auto vars = list_parameters(names, 1);
|
||||
return vars.names.size() > 0;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::parameter::ParameterType>
|
||||
SyncParametersClient::get_parameter_types(const std::vector<std::string> & parameter_names)
|
||||
{
|
||||
auto f = async_parameters_client_->get_parameter_types(parameter_names);
|
||||
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
|
||||
rclcpp::executor::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
}
|
||||
return std::vector<rclcpp::parameter::ParameterType>();
|
||||
}
|
||||
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
SyncParametersClient::set_parameters(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
|
||||
{
|
||||
auto f = async_parameters_client_->set_parameters(parameters);
|
||||
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
|
||||
rclcpp::executor::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
}
|
||||
return std::vector<rcl_interfaces::msg::SetParametersResult>();
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
SyncParametersClient::set_parameters_atomically(
|
||||
const std::vector<rclcpp::parameter::ParameterVariant> & parameters)
|
||||
{
|
||||
auto f = async_parameters_client_->set_parameters_atomically(parameters);
|
||||
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
|
||||
rclcpp::executor::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unable to get result of set parameters service call.");
|
||||
}
|
||||
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
SyncParametersClient::list_parameters(
|
||||
const std::vector<std::string> & parameter_prefixes,
|
||||
uint64_t depth)
|
||||
{
|
||||
auto f = async_parameters_client_->list_parameters(parameter_prefixes, depth);
|
||||
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
|
||||
rclcpp::executor::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
}
|
||||
|
||||
throw std::runtime_error("Unable to get result of list parameters service call.");
|
||||
}
|
||||
150
rclcpp/src/rclcpp/parameter_service.cpp
Normal file
150
rclcpp/src/rclcpp/parameter_service.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/parameter_service.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using rclcpp::parameter_service::ParameterService;
|
||||
|
||||
ParameterService::ParameterService(
|
||||
const rclcpp::node::Node::SharedPtr node,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: node_(node)
|
||||
{
|
||||
std::weak_ptr<rclcpp::node::Node> captured_node = node_;
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
get_parameters_service_ = node_->create_service<rcl_interfaces::srv::GetParameters>(
|
||||
std::string(node_->get_name()) + "__get_parameters",
|
||||
[captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::GetParameters::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::GetParameters::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
auto values = node->get_parameters(request->names);
|
||||
for (auto & pvariant : values) {
|
||||
response->values.push_back(pvariant.get_parameter_value());
|
||||
}
|
||||
},
|
||||
qos_profile
|
||||
);
|
||||
|
||||
get_parameter_types_service_ = node_->create_service<rcl_interfaces::srv::GetParameterTypes>(
|
||||
std::string(node_->get_name()) + "__get_parameter_types",
|
||||
[captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::GetParameterTypes::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
auto types = node->get_parameter_types(request->names);
|
||||
std::transform(types.cbegin(), types.cend(),
|
||||
std::back_inserter(response->types), [](const uint8_t & type) {
|
||||
return static_cast<rclcpp::parameter::ParameterType>(type);
|
||||
});
|
||||
},
|
||||
qos_profile
|
||||
);
|
||||
|
||||
set_parameters_service_ = node_->create_service<rcl_interfaces::srv::SetParameters>(
|
||||
std::string(node_->get_name()) + "__set_parameters",
|
||||
[captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::SetParameters::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::SetParameters::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
std::vector<rclcpp::parameter::ParameterVariant> pvariants;
|
||||
for (auto & p : request->parameters) {
|
||||
pvariants.push_back(rclcpp::parameter::ParameterVariant::from_parameter(p));
|
||||
}
|
||||
auto results = node->set_parameters(pvariants);
|
||||
response->results = results;
|
||||
},
|
||||
qos_profile
|
||||
);
|
||||
|
||||
set_parameters_atomically_service_ =
|
||||
node_->create_service<rcl_interfaces::srv::SetParametersAtomically>(
|
||||
std::string(node_->get_name()) + "__set_parameters_atomically",
|
||||
[captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::SetParametersAtomically::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
std::vector<rclcpp::parameter::ParameterVariant> pvariants;
|
||||
std::transform(request->parameters.cbegin(), request->parameters.cend(),
|
||||
std::back_inserter(pvariants),
|
||||
[](const rcl_interfaces::msg::Parameter & p) {
|
||||
return rclcpp::parameter::ParameterVariant::
|
||||
from_parameter(p);
|
||||
});
|
||||
auto result = node->set_parameters_atomically(pvariants);
|
||||
response->result = result;
|
||||
},
|
||||
qos_profile
|
||||
);
|
||||
|
||||
describe_parameters_service_ = node_->create_service<rcl_interfaces::srv::DescribeParameters>(
|
||||
std::string(node_->get_name()) + "__describe_parameters",
|
||||
[captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::DescribeParameters::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
auto descriptors = node->describe_parameters(request->names);
|
||||
response->descriptors = descriptors;
|
||||
},
|
||||
qos_profile
|
||||
);
|
||||
|
||||
list_parameters_service_ = node_->create_service<rcl_interfaces::srv::ListParameters>(
|
||||
std::string(node_->get_name()) + "__list_parameters",
|
||||
[captured_node](
|
||||
const std::shared_ptr<rmw_request_id_t>,
|
||||
const std::shared_ptr<rcl_interfaces::srv::ListParameters::Request> request,
|
||||
std::shared_ptr<rcl_interfaces::srv::ListParameters::Response> response)
|
||||
{
|
||||
auto node = captured_node.lock();
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
auto result = node->list_parameters(request->prefixes, request->depth);
|
||||
response->result = result;
|
||||
},
|
||||
qos_profile
|
||||
);
|
||||
// *INDENT-ON*
|
||||
}
|
||||
200
rclcpp/src/rclcpp/publisher.cpp
Normal file
200
rclcpp/src/rclcpp/publisher.cpp
Normal file
@@ -0,0 +1,200 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/publisher.hpp"
|
||||
|
||||
#include <rmw/error_handling.h>
|
||||
#include <rmw/rmw.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
|
||||
using rclcpp::publisher::PublisherBase;
|
||||
|
||||
PublisherBase::PublisherBase(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
const std::string & topic,
|
||||
const rosidl_message_type_support_t & type_support,
|
||||
const rcl_publisher_options_t & publisher_options)
|
||||
: rcl_node_handle_(node_base->get_shared_rcl_node_handle()),
|
||||
intra_process_publisher_id_(0), store_intra_process_message_(nullptr)
|
||||
{
|
||||
rcl_ret_t ret = rcl_publisher_init(
|
||||
&publisher_handle_,
|
||||
rcl_node_handle_.get(),
|
||||
&type_support,
|
||||
topic.c_str(),
|
||||
&publisher_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
|
||||
auto rcl_node_handle = rcl_node_handle_.get();
|
||||
// this will throw on any validation problem
|
||||
rcl_reset_error();
|
||||
expand_topic_or_service_name(
|
||||
topic,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle));
|
||||
}
|
||||
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create publisher");
|
||||
}
|
||||
// Life time of this object is tied to the publisher handle.
|
||||
rmw_publisher_t * publisher_rmw_handle = rcl_publisher_get_rmw_handle(&publisher_handle_);
|
||||
if (!publisher_rmw_handle) {
|
||||
auto msg = std::string("failed to get rmw handle: ") + rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
if (rmw_get_gid_for_publisher(publisher_rmw_handle, &rmw_gid_) != RMW_RET_OK) {
|
||||
auto msg = std::string("failed to get publisher gid: ") + rmw_get_error_string_safe();
|
||||
rmw_reset_error();
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
}
|
||||
|
||||
PublisherBase::~PublisherBase()
|
||||
{
|
||||
if (rcl_publisher_fini(&intra_process_publisher_handle_, rcl_node_handle_.get()) != RCL_RET_OK) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Error in destruction of intra process rcl publisher handle: %s\n",
|
||||
rcl_get_error_string_safe());
|
||||
rcl_reset_error();
|
||||
}
|
||||
|
||||
if (rcl_publisher_fini(&publisher_handle_, rcl_node_handle_.get()) != RCL_RET_OK) {
|
||||
fprintf(
|
||||
stderr,
|
||||
"Error in destruction of rcl publisher handle: %s\n",
|
||||
rcl_get_error_string_safe());
|
||||
rcl_reset_error();
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
PublisherBase::get_topic_name() const
|
||||
{
|
||||
return rcl_publisher_get_topic_name(&publisher_handle_);
|
||||
}
|
||||
|
||||
size_t
|
||||
PublisherBase::get_queue_size() const
|
||||
{
|
||||
const rcl_publisher_options_t * publisher_options = rcl_publisher_get_options(&publisher_handle_);
|
||||
if (!publisher_options) {
|
||||
auto msg = std::string("failed to get publisher options: ") + rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
return publisher_options->qos.depth;
|
||||
}
|
||||
|
||||
const rmw_gid_t &
|
||||
PublisherBase::get_gid() const
|
||||
{
|
||||
return rmw_gid_;
|
||||
}
|
||||
|
||||
const rmw_gid_t &
|
||||
PublisherBase::get_intra_process_gid() const
|
||||
{
|
||||
return intra_process_rmw_gid_;
|
||||
}
|
||||
|
||||
bool
|
||||
PublisherBase::operator==(const rmw_gid_t & gid) const
|
||||
{
|
||||
return *this == &gid;
|
||||
}
|
||||
|
||||
bool
|
||||
PublisherBase::operator==(const rmw_gid_t * gid) const
|
||||
{
|
||||
bool result = false;
|
||||
auto ret = rmw_compare_gids_equal(gid, &this->get_gid(), &result);
|
||||
if (ret != RMW_RET_OK) {
|
||||
auto msg = std::string("failed to compare gids: ") + rmw_get_error_string_safe();
|
||||
rmw_reset_error();
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
if (!result) {
|
||||
ret = rmw_compare_gids_equal(gid, &this->get_intra_process_gid(), &result);
|
||||
if (ret != RMW_RET_OK) {
|
||||
auto msg = std::string("failed to compare gids: ") + rmw_get_error_string_safe();
|
||||
rmw_reset_error();
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void
|
||||
PublisherBase::setup_intra_process(
|
||||
uint64_t intra_process_publisher_id,
|
||||
StoreMessageCallbackT callback,
|
||||
const rcl_publisher_options_t & intra_process_options)
|
||||
{
|
||||
auto intra_process_topic_name = std::string(this->get_topic_name()) + "/_intra";
|
||||
rcl_ret_t ret = rcl_publisher_init(
|
||||
&intra_process_publisher_handle_,
|
||||
rcl_node_handle_.get(),
|
||||
rclcpp::type_support::get_intra_process_message_msg_type_support(),
|
||||
intra_process_topic_name.c_str(),
|
||||
&intra_process_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
|
||||
auto rcl_node_handle = rcl_node_handle_.get();
|
||||
// this will throw on any validation problem
|
||||
rcl_reset_error();
|
||||
expand_topic_or_service_name(
|
||||
intra_process_topic_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle));
|
||||
}
|
||||
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create intra process publisher");
|
||||
}
|
||||
|
||||
intra_process_publisher_id_ = intra_process_publisher_id;
|
||||
store_intra_process_message_ = callback;
|
||||
// Life time of this object is tied to the publisher handle.
|
||||
rmw_publisher_t * publisher_rmw_handle = rcl_publisher_get_rmw_handle(
|
||||
&intra_process_publisher_handle_);
|
||||
if (publisher_rmw_handle == nullptr) {
|
||||
auto msg = std::string("Failed to get rmw publisher handle") + rcl_get_error_string_safe();
|
||||
rcl_reset_error();
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
auto rmw_ret = rmw_get_gid_for_publisher(
|
||||
publisher_rmw_handle, &intra_process_rmw_gid_);
|
||||
if (rmw_ret != RMW_RET_OK) {
|
||||
auto msg =
|
||||
std::string("failed to create intra process publisher gid: ") + rmw_get_error_string_safe();
|
||||
rmw_reset_error();
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
}
|
||||
59
rclcpp/src/rclcpp/service.cpp
Normal file
59
rclcpp/src/rclcpp/service.cpp
Normal file
@@ -0,0 +1,59 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/service.hpp"
|
||||
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/any_service_callback.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
using rclcpp::service::ServiceBase;
|
||||
|
||||
ServiceBase::ServiceBase(
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const std::string & service_name)
|
||||
: node_handle_(node_handle), service_name_(service_name)
|
||||
{}
|
||||
|
||||
ServiceBase::ServiceBase(std::shared_ptr<rcl_node_t> node_handle)
|
||||
: node_handle_(node_handle)
|
||||
{}
|
||||
|
||||
ServiceBase::~ServiceBase()
|
||||
{}
|
||||
|
||||
std::string
|
||||
ServiceBase::get_service_name()
|
||||
{
|
||||
return this->service_name_;
|
||||
}
|
||||
|
||||
const rcl_service_t *
|
||||
ServiceBase::get_service_handle()
|
||||
{
|
||||
return service_handle_;
|
||||
}
|
||||
|
||||
rcl_node_t *
|
||||
ServiceBase::get_rcl_node_handle() const
|
||||
{
|
||||
return node_handle_.get();
|
||||
}
|
||||
92
rclcpp/src/rclcpp/subscription.cpp
Normal file
92
rclcpp/src/rclcpp/subscription.cpp
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/subscription.hpp"
|
||||
|
||||
#include <cstdio>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
|
||||
using rclcpp::subscription::SubscriptionBase;
|
||||
|
||||
SubscriptionBase::SubscriptionBase(
|
||||
std::shared_ptr<rcl_node_t> node_handle,
|
||||
const rosidl_message_type_support_t & type_support_handle,
|
||||
const std::string & topic_name,
|
||||
const rcl_subscription_options_t & subscription_options)
|
||||
: node_handle_(node_handle)
|
||||
{
|
||||
rcl_ret_t ret = rcl_subscription_init(
|
||||
&subscription_handle_,
|
||||
node_handle_.get(),
|
||||
&type_support_handle,
|
||||
topic_name.c_str(),
|
||||
&subscription_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
if (ret == RCL_RET_TOPIC_NAME_INVALID) {
|
||||
auto rcl_node_handle = node_handle_.get();
|
||||
// this will throw on any validation problem
|
||||
rcl_reset_error();
|
||||
expand_topic_or_service_name(
|
||||
topic_name,
|
||||
rcl_node_get_name(rcl_node_handle),
|
||||
rcl_node_get_namespace(rcl_node_handle));
|
||||
}
|
||||
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "could not create subscription");
|
||||
}
|
||||
}
|
||||
|
||||
SubscriptionBase::~SubscriptionBase()
|
||||
{
|
||||
if (rcl_subscription_fini(&subscription_handle_, node_handle_.get()) != RCL_RET_OK) {
|
||||
std::stringstream ss;
|
||||
ss << "Error in destruction of rcl subscription handle: " <<
|
||||
rcl_get_error_string_safe() << '\n';
|
||||
(std::cerr << ss.str()).flush();
|
||||
}
|
||||
if (rcl_subscription_fini(
|
||||
&intra_process_subscription_handle_, node_handle_.get()) != RCL_RET_OK)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "Error in destruction of rmw intra process subscription handle: " <<
|
||||
rcl_get_error_string_safe() << '\n';
|
||||
(std::cerr << ss.str()).flush();
|
||||
}
|
||||
}
|
||||
|
||||
const char *
|
||||
SubscriptionBase::get_topic_name() const
|
||||
{
|
||||
return rcl_subscription_get_topic_name(&subscription_handle_);
|
||||
}
|
||||
|
||||
const rcl_subscription_t *
|
||||
SubscriptionBase::get_subscription_handle() const
|
||||
{
|
||||
return &subscription_handle_;
|
||||
}
|
||||
|
||||
const rcl_subscription_t *
|
||||
SubscriptionBase::get_intra_process_subscription_handle() const
|
||||
{
|
||||
return &intra_process_subscription_handle_;
|
||||
}
|
||||
77
rclcpp/src/rclcpp/timer.cpp
Normal file
77
rclcpp/src/rclcpp/timer.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/timer.hpp"
|
||||
|
||||
#include <chrono>
|
||||
#include <string>
|
||||
|
||||
using rclcpp::timer::TimerBase;
|
||||
|
||||
TimerBase::TimerBase(std::chrono::nanoseconds period)
|
||||
{
|
||||
if (rcl_timer_init(
|
||||
&timer_handle_, period.count(), nullptr,
|
||||
rcl_get_default_allocator()) != RCL_RET_OK)
|
||||
{
|
||||
fprintf(stderr, "Couldn't initialize rcl timer handle: %s\n", rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
|
||||
TimerBase::~TimerBase()
|
||||
{}
|
||||
|
||||
void
|
||||
TimerBase::cancel()
|
||||
{
|
||||
if (rcl_timer_cancel(&timer_handle_) != RCL_RET_OK) {
|
||||
throw std::runtime_error(std::string("Couldn't cancel timer: ") + rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
TimerBase::reset()
|
||||
{
|
||||
if (rcl_timer_reset(&timer_handle_) != RCL_RET_OK) {
|
||||
throw std::runtime_error(std::string("Couldn't reset timer: ") + rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
TimerBase::is_ready()
|
||||
{
|
||||
bool ready = false;
|
||||
if (rcl_timer_is_ready(&timer_handle_, &ready) != RCL_RET_OK) {
|
||||
throw std::runtime_error(std::string("Failed to check timer: ") + rcl_get_error_string_safe());
|
||||
}
|
||||
return ready;
|
||||
}
|
||||
|
||||
std::chrono::nanoseconds
|
||||
TimerBase::time_until_trigger()
|
||||
{
|
||||
int64_t time_until_next_call = 0;
|
||||
if (rcl_timer_get_time_until_next_call(&timer_handle_, &time_until_next_call) != RCL_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("Timer could not get time until next call: ") +
|
||||
rcl_get_error_string_safe());
|
||||
}
|
||||
return std::chrono::nanoseconds(time_until_next_call);
|
||||
}
|
||||
|
||||
const rcl_timer_t *
|
||||
TimerBase::get_timer_handle()
|
||||
{
|
||||
return &timer_handle_;
|
||||
}
|
||||
116
rclcpp/src/rclcpp/type_support.cpp
Normal file
116
rclcpp/src/rclcpp/type_support.cpp
Normal file
@@ -0,0 +1,116 @@
|
||||
// Copyright 2014 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/type_support_decl.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
#include "rcl_interfaces/msg/list_parameters_result.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_descriptor.hpp"
|
||||
#include "rcl_interfaces/msg/parameter_event.hpp"
|
||||
#include "rcl_interfaces/msg/set_parameters_result.hpp"
|
||||
#include "rcl_interfaces/srv/describe_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/get_parameter_types.hpp"
|
||||
#include "rcl_interfaces/srv/get_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/list_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/set_parameters.hpp"
|
||||
#include "rcl_interfaces/srv/set_parameters_atomically.hpp"
|
||||
|
||||
const rosidl_message_type_support_t *
|
||||
rclcpp::type_support::get_intra_process_message_msg_type_support()
|
||||
{
|
||||
return rosidl_typesupport_cpp::get_message_type_support_handle<
|
||||
rcl_interfaces::msg::IntraProcessMessage
|
||||
>();
|
||||
}
|
||||
|
||||
const rosidl_message_type_support_t *
|
||||
rclcpp::type_support::get_parameter_event_msg_type_support()
|
||||
{
|
||||
return rosidl_typesupport_cpp::get_message_type_support_handle<
|
||||
rcl_interfaces::msg::ParameterEvent
|
||||
>();
|
||||
}
|
||||
|
||||
const rosidl_message_type_support_t *
|
||||
rclcpp::type_support::get_set_parameters_result_msg_type_support()
|
||||
{
|
||||
return rosidl_typesupport_cpp::get_message_type_support_handle<
|
||||
rcl_interfaces::msg::SetParametersResult
|
||||
>();
|
||||
}
|
||||
|
||||
const rosidl_message_type_support_t *
|
||||
rclcpp::type_support::get_parameter_descriptor_msg_type_support()
|
||||
{
|
||||
return rosidl_typesupport_cpp::get_message_type_support_handle<
|
||||
rcl_interfaces::msg::ParameterDescriptor
|
||||
>();
|
||||
}
|
||||
|
||||
const rosidl_message_type_support_t *
|
||||
rclcpp::type_support::get_list_parameters_result_msg_type_support()
|
||||
{
|
||||
return rosidl_typesupport_cpp::get_message_type_support_handle<
|
||||
rcl_interfaces::msg::ListParametersResult
|
||||
>();
|
||||
}
|
||||
|
||||
const rosidl_service_type_support_t *
|
||||
rclcpp::type_support::get_get_parameters_srv_type_support()
|
||||
{
|
||||
return rosidl_typesupport_cpp::get_service_type_support_handle<
|
||||
rcl_interfaces::srv::GetParameters
|
||||
>();
|
||||
}
|
||||
|
||||
const rosidl_service_type_support_t *
|
||||
rclcpp::type_support::get_get_parameter_types_srv_type_support()
|
||||
{
|
||||
return rosidl_typesupport_cpp::get_service_type_support_handle<
|
||||
rcl_interfaces::srv::GetParameterTypes
|
||||
>();
|
||||
}
|
||||
|
||||
const rosidl_service_type_support_t *
|
||||
rclcpp::type_support::get_set_parameters_srv_type_support()
|
||||
{
|
||||
return rosidl_typesupport_cpp::get_service_type_support_handle<
|
||||
rcl_interfaces::srv::SetParameters
|
||||
>();
|
||||
}
|
||||
|
||||
const rosidl_service_type_support_t *
|
||||
rclcpp::type_support::get_list_parameters_srv_type_support()
|
||||
{
|
||||
return rosidl_typesupport_cpp::get_service_type_support_handle<
|
||||
rcl_interfaces::srv::ListParameters
|
||||
>();
|
||||
}
|
||||
|
||||
const rosidl_service_type_support_t *
|
||||
rclcpp::type_support::get_describe_parameters_srv_type_support()
|
||||
{
|
||||
return rosidl_typesupport_cpp::get_service_type_support_handle<
|
||||
rcl_interfaces::srv::DescribeParameters
|
||||
>();
|
||||
}
|
||||
|
||||
const rosidl_service_type_support_t *
|
||||
rclcpp::type_support::get_set_parameters_atomically_srv_type_support()
|
||||
{
|
||||
return rosidl_typesupport_cpp::get_service_type_support_handle<
|
||||
rcl_interfaces::srv::SetParametersAtomically
|
||||
>();
|
||||
}
|
||||
251
rclcpp/src/rclcpp/utilities.cpp
Normal file
251
rclcpp/src/rclcpp/utilities.cpp
Normal file
@@ -0,0 +1,251 @@
|
||||
// Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/rcl.h"
|
||||
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
// Determine if sigaction is available
|
||||
#if __APPLE__ || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
|
||||
#define HAS_SIGACTION
|
||||
#endif
|
||||
|
||||
/// Represent the status of the global interrupt signal.
|
||||
static volatile sig_atomic_t g_signal_status = 0;
|
||||
/// Guard conditions for interrupting the rmw implementation when the global interrupt signal fired.
|
||||
static std::map<rcl_wait_set_t *, rcl_guard_condition_t> g_sigint_guard_cond_handles;
|
||||
/// Mutex to protect g_sigint_guard_cond_handles
|
||||
static std::mutex g_sigint_guard_cond_handles_mutex;
|
||||
/// Condition variable for timed sleep (see sleep_for).
|
||||
static std::condition_variable g_interrupt_condition_variable;
|
||||
static std::atomic<bool> g_is_interrupted(false);
|
||||
/// Mutex for protecting the global condition variable.
|
||||
static std::mutex g_interrupt_mutex;
|
||||
|
||||
#ifdef HAS_SIGACTION
|
||||
static struct sigaction old_action;
|
||||
#else
|
||||
static void (* old_signal_handler)(int) = 0;
|
||||
#endif
|
||||
|
||||
void
|
||||
#ifdef HAS_SIGACTION
|
||||
signal_handler(int signal_value, siginfo_t * siginfo, void * context)
|
||||
#else
|
||||
signal_handler(int signal_value)
|
||||
#endif
|
||||
{
|
||||
// TODO(wjwwood): remove? move to console logging at some point?
|
||||
printf("signal_handler(%d)\n", signal_value);
|
||||
#ifdef HAS_SIGACTION
|
||||
if (old_action.sa_flags & SA_SIGINFO) {
|
||||
if (old_action.sa_sigaction != NULL) {
|
||||
old_action.sa_sigaction(signal_value, siginfo, context);
|
||||
}
|
||||
} else {
|
||||
// *INDENT-OFF*
|
||||
if (
|
||||
old_action.sa_handler != NULL && // Is set
|
||||
old_action.sa_handler != SIG_DFL && // Is not default
|
||||
old_action.sa_handler != SIG_IGN) // Is not ignored
|
||||
// *INDENT-ON*
|
||||
{
|
||||
old_action.sa_handler(signal_value);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (old_signal_handler) {
|
||||
old_signal_handler(signal_value);
|
||||
}
|
||||
#endif
|
||||
g_signal_status = signal_value;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
|
||||
for (auto & kv : g_sigint_guard_cond_handles) {
|
||||
rcl_ret_t status = rcl_trigger_guard_condition(&(kv.second));
|
||||
if (status != RCL_RET_OK) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] failed to trigger guard condition: %s\n", rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
}
|
||||
g_is_interrupted.store(true);
|
||||
g_interrupt_condition_variable.notify_all();
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::utilities::init(int argc, char * argv[])
|
||||
{
|
||||
g_is_interrupted.store(false);
|
||||
if (rcl_init(argc, argv, rcl_get_default_allocator()) != RCL_RET_OK) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("failed to initialize rmw implementation: ") + rcl_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
#ifdef HAS_SIGACTION
|
||||
struct sigaction action;
|
||||
memset(&action, 0, sizeof(action));
|
||||
sigemptyset(&action.sa_mask);
|
||||
action.sa_sigaction = ::signal_handler;
|
||||
action.sa_flags = SA_SIGINFO;
|
||||
ssize_t ret = sigaction(SIGINT, &action, &old_action);
|
||||
if (ret == -1)
|
||||
#else
|
||||
::old_signal_handler = std::signal(SIGINT, ::signal_handler);
|
||||
// NOLINTNEXTLINE(readability/braces)
|
||||
if (::old_signal_handler == SIG_ERR)
|
||||
#endif
|
||||
{
|
||||
const size_t error_length = 1024;
|
||||
// NOLINTNEXTLINE(runtime/arrays)
|
||||
char error_string[error_length];
|
||||
#ifndef _WIN32
|
||||
#if (defined(_GNU_SOURCE) && !defined(ANDROID))
|
||||
char * msg = strerror_r(errno, error_string, error_length);
|
||||
if (msg != error_string) {
|
||||
strncpy(error_string, msg, error_length);
|
||||
msg[error_length - 1] = '\0';
|
||||
}
|
||||
#else
|
||||
int error_status = strerror_r(errno, error_string, error_length);
|
||||
if (error_status != 0) {
|
||||
throw std::runtime_error("Failed to get error string for errno: " + std::to_string(errno));
|
||||
}
|
||||
#endif
|
||||
#else
|
||||
strerror_s(error_string, error_length, errno);
|
||||
#endif
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to set SIGINT signal handler: (" + std::to_string(errno) + ")") +
|
||||
error_string);
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::utilities::ok()
|
||||
{
|
||||
return ::g_signal_status == 0;
|
||||
}
|
||||
|
||||
static std::mutex on_shutdown_mutex_;
|
||||
static std::vector<std::function<void(void)>> on_shutdown_callbacks_;
|
||||
|
||||
void
|
||||
rclcpp::utilities::shutdown()
|
||||
{
|
||||
g_signal_status = SIGINT;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
|
||||
for (auto & kv : g_sigint_guard_cond_handles) {
|
||||
if (rcl_trigger_guard_condition(&(kv.second)) != RCL_RET_OK) {
|
||||
fprintf(stderr,
|
||||
"[rclcpp::error] failed to trigger sigint guard condition: %s\n",
|
||||
rcl_get_error_string_safe());
|
||||
}
|
||||
}
|
||||
}
|
||||
g_is_interrupted.store(true);
|
||||
g_interrupt_condition_variable.notify_all();
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_mutex_);
|
||||
for (auto & on_shutdown_callback : on_shutdown_callbacks_) {
|
||||
on_shutdown_callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::utilities::on_shutdown(std::function<void(void)> callback)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_mutex_);
|
||||
on_shutdown_callbacks_.push_back(callback);
|
||||
}
|
||||
|
||||
rcl_guard_condition_t *
|
||||
rclcpp::utilities::get_sigint_guard_condition(rcl_wait_set_t * waitset)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
|
||||
auto kv = g_sigint_guard_cond_handles.find(waitset);
|
||||
if (kv != g_sigint_guard_cond_handles.end()) {
|
||||
return &kv->second;
|
||||
} else {
|
||||
rcl_guard_condition_t handle =
|
||||
rcl_get_zero_initialized_guard_condition();
|
||||
rcl_guard_condition_options_t options = rcl_guard_condition_get_default_options();
|
||||
if (rcl_guard_condition_init(&handle, options) != RCL_RET_OK) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(std::string(
|
||||
"Couldn't initialize guard condition: ") + rcl_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
g_sigint_guard_cond_handles[waitset] = handle;
|
||||
return &g_sigint_guard_cond_handles[waitset];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::utilities::release_sigint_guard_condition(rcl_wait_set_t * waitset)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
|
||||
auto kv = g_sigint_guard_cond_handles.find(waitset);
|
||||
if (kv != g_sigint_guard_cond_handles.end()) {
|
||||
if (rcl_guard_condition_fini(&kv->second) != RCL_RET_OK) {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(std::string(
|
||||
"Failed to destroy sigint guard condition: ") +
|
||||
rcl_get_error_string_safe());
|
||||
// *INDENT-ON*
|
||||
}
|
||||
g_sigint_guard_cond_handles.erase(kv);
|
||||
} else {
|
||||
// *INDENT-OFF* (prevent uncrustify from making unnecessary indents here)
|
||||
throw std::runtime_error(std::string(
|
||||
"Tried to release sigint guard condition for nonexistent waitset"));
|
||||
// *INDENT-ON*
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::utilities::sleep_for(const std::chrono::nanoseconds & nanoseconds)
|
||||
{
|
||||
std::chrono::nanoseconds time_left = nanoseconds;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(::g_interrupt_mutex);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
::g_interrupt_condition_variable.wait_for(lock, nanoseconds);
|
||||
time_left -= std::chrono::steady_clock::now() - start;
|
||||
}
|
||||
if (time_left > std::chrono::nanoseconds::zero() && !g_is_interrupted) {
|
||||
return sleep_for(time_left);
|
||||
}
|
||||
// Return true if the timeout elapsed successfully, otherwise false.
|
||||
return !g_is_interrupted;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user