Compare commits
56 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24769507d3 | ||
|
|
8c00607c39 | ||
|
|
af9ae4a61c | ||
|
|
ed21cf4699 | ||
|
|
ee7e642592 | ||
|
|
0f25f714fe | ||
|
|
d11a10a583 | ||
|
|
8783cdcf96 | ||
|
|
1f2904f980 | ||
|
|
4f2f8def98 | ||
|
|
cb20529e5e | ||
|
|
b352d45031 | ||
|
|
0a44344f43 | ||
|
|
43f891dac8 | ||
|
|
d8d64e1efc | ||
|
|
2929e4b133 | ||
|
|
284d0c1c70 | ||
|
|
ec64b40a9d | ||
|
|
83beaf8a3f | ||
|
|
fce1d4b86f | ||
|
|
b8b875228b | ||
|
|
718d24f942 | ||
|
|
d2d9ad8796 | ||
|
|
c51b28420f | ||
|
|
3919ab1897 | ||
|
|
8743bcb0a1 | ||
|
|
ef5f3d3fc1 | ||
|
|
10d7b7c72b | ||
|
|
4046563de6 | ||
|
|
0f9098e9b6 | ||
|
|
c7ac39a0e6 | ||
|
|
c0a6b474d7 | ||
|
|
99dd0313ab | ||
|
|
1e91face39 | ||
|
|
5c92811739 | ||
|
|
22abd62e31 | ||
|
|
eb2081bb25 | ||
|
|
69d7e69957 | ||
|
|
2e58dde5ef | ||
|
|
c93beb5d16 | ||
|
|
a54a329153 | ||
|
|
9da1b95ece | ||
|
|
8bffd25746 | ||
|
|
ef2014ac4d | ||
|
|
fe09d937b7 | ||
|
|
91167393ea | ||
|
|
33f1e1776c | ||
|
|
9d7b50e4f7 | ||
|
|
9c25ba9a4a | ||
|
|
3af8d2cfed | ||
|
|
36262a5cf5 | ||
|
|
03cbc1c895 | ||
|
|
457b0e7077 | ||
|
|
27b0428f7a | ||
|
|
be010cb2d5 | ||
|
|
f212d73413 |
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
.DS_Store
|
||||
@@ -11,3 +11,8 @@ be under the Apache 2 License, as dictated by that
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
~~~
|
||||
|
||||
Contributors must sign-off each commit by adding a `Signed-off-by: ...`
|
||||
line to commit messages to certify that they have the right to submit
|
||||
the code they are contributing to the project according to the
|
||||
[Developer Certificate of Origin (DCO)](https://developercertificate.org/).
|
||||
|
||||
@@ -2,6 +2,53 @@
|
||||
Changelog for package rclcpp
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
0.7.0 (2019-04-14)
|
||||
------------------
|
||||
* Added Options-struct interfaces for creating publishers/subscribers (pre-QoS, standalone). (`#673 <https://github.com/ros2/rclcpp/issues/673>`_)
|
||||
* Replaced strncpy with memcpy. (`#684 <https://github.com/ros2/rclcpp/issues/684>`_)
|
||||
* Replaced const char * with a std::array<char, TOPIC_NAME_LENGTH> as the key of IPM IDTopicMap. (`#671 <https://github.com/ros2/rclcpp/issues/671>`_)
|
||||
* Refactored SignalHandler logger to avoid race during destruction. (`#682 <https://github.com/ros2/rclcpp/issues/682>`_)
|
||||
* Introduce rclcpp_components to implement composition. (`#665 <https://github.com/ros2/rclcpp/issues/665>`_)
|
||||
* Added QoS policy check when configuring intraprocess, skip interprocess publish when possible. (`#674 <https://github.com/ros2/rclcpp/issues/674>`_)
|
||||
* Updated to use do { .. } while(0) around content of logging macros. (`#681 <https://github.com/ros2/rclcpp/issues/681>`_)
|
||||
* Added function to get publisher's actual QoS settings. (`#667 <https://github.com/ros2/rclcpp/issues/667>`_)
|
||||
* Updated to avoid race that triggers timer too often. (`#621 <https://github.com/ros2/rclcpp/issues/621>`_)
|
||||
* Exposed get_fully_qualified_name in NodeBase API. (`#662 <https://github.com/ros2/rclcpp/issues/662>`_)
|
||||
* Updated to use ament_target_dependencies where possible. (`#659 <https://github.com/ros2/rclcpp/issues/659>`_)
|
||||
* Fixed wait for service memory leak bug. (`#656 <https://github.com/ros2/rclcpp/issues/656>`_)
|
||||
* Fixed test_time_source test. (`#639 <https://github.com/ros2/rclcpp/issues/639>`_)
|
||||
* Fixed hard-coded duration type representation so int64_t isn't assumed. (`#648 <https://github.com/ros2/rclcpp/issues/648>`_)
|
||||
* Fixed cppcheck warning. (`#646 <https://github.com/ros2/rclcpp/issues/646>`_)
|
||||
* Added count matching api and intra-process subscriber count. (`#628 <https://github.com/ros2/rclcpp/issues/628>`_)
|
||||
* Added Sub Node alternative. (`#581 <https://github.com/ros2/rclcpp/issues/581>`_)
|
||||
* Replaced 'auto' with 'const auto &'. (`#630 <https://github.com/ros2/rclcpp/issues/630>`_)
|
||||
* Set Parameter Event Publisher settings. `#591 <https://github.com/ros2/rclcpp/issues/591>`_ (`#614 <https://github.com/ros2/rclcpp/issues/614>`_)
|
||||
* Replaced node constructor arguments with NodeOptions. (`#622 <https://github.com/ros2/rclcpp/issues/622>`_)
|
||||
* Updated to pass context to wait set (`#617 <https://github.com/ros2/rclcpp/issues/617>`_)
|
||||
* Added API to get parameters in a map. (`#575 <https://github.com/ros2/rclcpp/issues/575>`_)
|
||||
* Updated Bind usage since it is is no longer in std::__1. (`#618 <https://github.com/ros2/rclcpp/issues/618>`_)
|
||||
* Fixed errors from uncrustify v0.68. (`#613 <https://github.com/ros2/rclcpp/issues/613>`_)
|
||||
* Added new constructors for SyncParameterClient. (`#612 <https://github.com/ros2/rclcpp/issues/612>`_)
|
||||
* Contributors: Alberto Soragna, Chris Lalancette, Dirk Thomas, Emerson Knapp, Francisco Martín Rico, Jacob Perron, Marko Durkovic, Michael Carroll, Peter Baughman, Shane Loretz, Wei Liu, William Woodall, Yutaka Kondo, ivanpauno, kuzai, rarvolt
|
||||
|
||||
0.6.2 (2018-12-13)
|
||||
------------------
|
||||
* Updated to use signal safe synchronization with platform specific semaphores (`#607 <https://github.com/ros2/rclcpp/issues/607>`_)
|
||||
* Resolved startup race condition for sim time (`#608 <https://github.com/ros2/rclcpp/issues/608>`_)
|
||||
Resolves `#595 <https://github.com/ros2/rclcpp/issues/595>`_
|
||||
* Contributors: Tully Foote, William Woodall
|
||||
|
||||
0.6.1 (2018-12-07)
|
||||
------------------
|
||||
* Added wait_for_action_server() for action clients (`#598 <https://github.com/ros2/rclcpp/issues/598>`_)
|
||||
* Added node path and time stamp to parameter event message (`#584 <https://github.com/ros2/rclcpp/issues/584>`_)
|
||||
* Updated to allow removing a waitable (`#597 <https://github.com/ros2/rclcpp/issues/597>`_)
|
||||
* Refactored init to allow for non-global init (`#587 <https://github.com/ros2/rclcpp/issues/587>`_)
|
||||
* Fixed wrong use of constructor and hanging test (`#596 <https://github.com/ros2/rclcpp/issues/596>`_)
|
||||
* Added class Waitable (`#589 <https://github.com/ros2/rclcpp/issues/589>`_)
|
||||
* Updated rcl_wait_set_add\_* calls (`#586 <https://github.com/ros2/rclcpp/issues/586>`_)
|
||||
* Contributors: Dirk Thomas, Jacob Perron, Shane Loretz, William Woodall, bpwilcox
|
||||
|
||||
0.6.0 (2018-11-19)
|
||||
------------------
|
||||
* Updated to use new error handling API from rcutils (`#577 <https://github.com/ros2/rclcpp/issues/577>`_)
|
||||
|
||||
@@ -40,20 +40,24 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/executors/multi_threaded_executor.cpp
|
||||
src/rclcpp/executors/single_threaded_executor.cpp
|
||||
src/rclcpp/graph_listener.cpp
|
||||
src/rclcpp/init_options.cpp
|
||||
src/rclcpp/intra_process_manager.cpp
|
||||
src/rclcpp/intra_process_manager_impl.cpp
|
||||
src/rclcpp/logger.cpp
|
||||
src/rclcpp/memory_strategies.cpp
|
||||
src/rclcpp/memory_strategy.cpp
|
||||
src/rclcpp/node.cpp
|
||||
src/rclcpp/node_options.cpp
|
||||
src/rclcpp/node_interfaces/node_base.cpp
|
||||
src/rclcpp/node_interfaces/node_clock.cpp
|
||||
src/rclcpp/node_interfaces/node_graph.cpp
|
||||
src/rclcpp/node_interfaces/node_logging.cpp
|
||||
src/rclcpp/node_interfaces/node_parameters.cpp
|
||||
src/rclcpp/node_interfaces/node_services.cpp
|
||||
src/rclcpp/node_interfaces/node_time_source.cpp
|
||||
src/rclcpp/node_interfaces/node_timers.cpp
|
||||
src/rclcpp/node_interfaces/node_topics.cpp
|
||||
src/rclcpp/node_interfaces/node_waitables.cpp
|
||||
src/rclcpp/parameter.cpp
|
||||
src/rclcpp/parameter_value.cpp
|
||||
src/rclcpp/parameter_client.cpp
|
||||
@@ -62,12 +66,14 @@ set(${PROJECT_NAME}_SRCS
|
||||
src/rclcpp/parameter_service.cpp
|
||||
src/rclcpp/publisher.cpp
|
||||
src/rclcpp/service.cpp
|
||||
src/rclcpp/signal_handler.cpp
|
||||
src/rclcpp/subscription.cpp
|
||||
src/rclcpp/time.cpp
|
||||
src/rclcpp/time_source.cpp
|
||||
src/rclcpp/timer.cpp
|
||||
src/rclcpp/type_support.cpp
|
||||
src/rclcpp/utilities.cpp
|
||||
src/rclcpp/waitable.cpp
|
||||
)
|
||||
|
||||
# "watch" template for changes
|
||||
@@ -137,70 +143,70 @@ if(BUILD_TESTING)
|
||||
|
||||
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}
|
||||
ament_target_dependencies(test_client
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
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}
|
||||
ament_target_dependencies(test_expand_topic_or_service_name
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_expand_topic_or_service_name ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_function_traits test/test_function_traits.cpp)
|
||||
if(TARGET test_function_traits)
|
||||
target_include_directories(test_function_traits PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_function_traits
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
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}
|
||||
ament_target_dependencies(test_mapped_ring_buffer
|
||||
"rcl"
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_intra_process_manager test/test_intra_process_manager.cpp)
|
||||
if(TARGET test_intra_process_manager)
|
||||
target_include_directories(test_intra_process_manager PUBLIC
|
||||
${rcl_INCLUDE_DIRS}
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_intra_process_manager
|
||||
"rcl"
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
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}
|
||||
ament_target_dependencies(test_node
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_node ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_node_global_args test/test_node_global_args.cpp)
|
||||
if(TARGET test_node_global_args)
|
||||
target_include_directories(test_node_global_args PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_node_global_args
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_node_global_args ${PROJECT_NAME})
|
||||
endif()
|
||||
@@ -210,21 +216,21 @@ if(BUILD_TESTING)
|
||||
endif()
|
||||
ament_add_gtest(test_parameter_events_filter test/test_parameter_events_filter.cpp)
|
||||
if(TARGET test_parameter_events_filter)
|
||||
target_include_directories(test_parameter_events_filter PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_parameter_events_filter
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_parameter_events_filter ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_parameter test/test_parameter.cpp)
|
||||
if(TARGET test_parameter)
|
||||
target_include_directories(test_parameter PUBLIC
|
||||
${rcl_interfaces_INCLUDE_DIRS}
|
||||
${rmw_INCLUDE_DIRS}
|
||||
${rosidl_generator_cpp_INCLUDE_DIRS}
|
||||
${rosidl_typesupport_cpp_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_parameter
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_parameter ${PROJECT_NAME})
|
||||
endif()
|
||||
@@ -234,21 +240,40 @@ if(BUILD_TESTING)
|
||||
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}
|
||||
ament_target_dependencies(test_publisher
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_publisher ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_pub_sub_option_interface test/test_pub_sub_option_interface.cpp)
|
||||
if(TARGET test_pub_sub_option_interface)
|
||||
ament_target_dependencies(test_pub_sub_option_interface
|
||||
test_msgs
|
||||
)
|
||||
target_link_libraries(test_pub_sub_option_interface
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
ament_add_gtest(test_publisher_subscription_count_api test/test_publisher_subscription_count_api.cpp)
|
||||
if(TARGET test_publisher_subscription_count_api)
|
||||
ament_target_dependencies(test_publisher_subscription_count_api
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_publisher_subscription_count_api ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_rate test/test_rate.cpp)
|
||||
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}
|
||||
ament_target_dependencies(test_rate
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_rate
|
||||
${PROJECT_NAME}
|
||||
@@ -256,48 +281,55 @@ if(BUILD_TESTING)
|
||||
endif()
|
||||
ament_add_gtest(test_serialized_message_allocator test/test_serialized_message_allocator.cpp)
|
||||
if(TARGET test_serialized_message_allocator)
|
||||
target_include_directories(test_serialized_message_allocator PUBLIC
|
||||
${test_msgs_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_serialized_message_allocator
|
||||
test_msgs
|
||||
)
|
||||
target_link_libraries(test_serialized_message_allocator
|
||||
${PROJECT_NAME}
|
||||
${test_msgs_LIBRARIES}
|
||||
)
|
||||
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}
|
||||
ament_target_dependencies(test_service
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
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}
|
||||
ament_target_dependencies(test_subscription
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_subscription ${PROJECT_NAME})
|
||||
endif()
|
||||
ament_add_gtest(test_subscription_publisher_count_api test/test_subscription_publisher_count_api.cpp)
|
||||
if(TARGET test_subscription_publisher_count_api)
|
||||
ament_target_dependencies(test_subscription_publisher_count_api
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_subscription_publisher_count_api ${PROJECT_NAME})
|
||||
endif()
|
||||
find_package(test_msgs REQUIRED)
|
||||
ament_add_gtest(test_subscription_traits test/test_subscription_traits.cpp)
|
||||
if(TARGET test_subscription_traits)
|
||||
target_include_directories(test_subscription_traits PUBLIC
|
||||
${rcl_INCLUDE_DIRS}
|
||||
ament_target_dependencies(test_subscription_traits
|
||||
"rcl"
|
||||
"test_msgs"
|
||||
)
|
||||
ament_target_dependencies(test_subscription_traits
|
||||
"test_msgs"
|
||||
)
|
||||
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}
|
||||
ament_target_dependencies(test_find_weak_nodes
|
||||
"rcl"
|
||||
)
|
||||
target_link_libraries(test_find_weak_nodes ${PROJECT_NAME})
|
||||
endif()
|
||||
@@ -321,8 +353,8 @@ if(BUILD_TESTING)
|
||||
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}
|
||||
ament_target_dependencies(test_externally_defined_services
|
||||
"rcl"
|
||||
)
|
||||
target_link_libraries(test_externally_defined_services ${PROJECT_NAME})
|
||||
foreach(typesupport_impl_cpp ${typesupport_impls_cpp})
|
||||
@@ -396,16 +428,20 @@ if(BUILD_TESTING)
|
||||
"rcl")
|
||||
target_link_libraries(test_multi_threaded_executor ${PROJECT_NAME})
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_local_parameters test/test_local_parameters.cpp)
|
||||
if(TARGET test_local_parameters)
|
||||
ament_target_dependencies(test_local_parameters
|
||||
"rcl_interfaces"
|
||||
"rmw"
|
||||
"rosidl_generator_cpp"
|
||||
"rosidl_typesupport_cpp"
|
||||
)
|
||||
target_link_libraries(test_local_parameters ${PROJECT_NAME})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
ament_package(
|
||||
CONFIG_EXTRAS rclcpp-extras.cmake
|
||||
)
|
||||
|
||||
install(
|
||||
DIRECTORY cmake
|
||||
DESTINATION share/${PROJECT_NAME}
|
||||
)
|
||||
ament_package()
|
||||
|
||||
install(
|
||||
DIRECTORY include/ ${CMAKE_CURRENT_BINARY_DIR}/include/
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
# Copyright 2015 Open Source Robotics Foundation, Inc.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
|
||||
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()
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/timer.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -45,6 +46,7 @@ struct AnyExecutable
|
||||
rclcpp::TimerBase::SharedPtr timer;
|
||||
rclcpp::ServiceBase::SharedPtr service;
|
||||
rclcpp::ClientBase::SharedPtr client;
|
||||
rclcpp::Waitable::SharedPtr waitable;
|
||||
// These are used to keep the scope on the containing items
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group;
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base;
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/timer.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -35,6 +36,7 @@ namespace node_interfaces
|
||||
class NodeServices;
|
||||
class NodeTimers;
|
||||
class NodeTopics;
|
||||
class NodeWaitables;
|
||||
} // namespace node_interfaces
|
||||
|
||||
namespace callback_group
|
||||
@@ -51,6 +53,7 @@ class CallbackGroup
|
||||
friend class rclcpp::node_interfaces::NodeServices;
|
||||
friend class rclcpp::node_interfaces::NodeTimers;
|
||||
friend class rclcpp::node_interfaces::NodeTopics;
|
||||
friend class rclcpp::node_interfaces::NodeWaitables;
|
||||
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(CallbackGroup)
|
||||
@@ -74,6 +77,10 @@ public:
|
||||
const std::vector<rclcpp::ClientBase::WeakPtr> &
|
||||
get_client_ptrs() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<rclcpp::Waitable::WeakPtr> &
|
||||
get_waitable_ptrs() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::atomic_bool &
|
||||
can_be_taken_from();
|
||||
@@ -101,6 +108,14 @@ protected:
|
||||
void
|
||||
add_client(const rclcpp::ClientBase::SharedPtr client_ptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
add_waitable(const rclcpp::Waitable::SharedPtr waitable_ptr);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
remove_waitable(const rclcpp::Waitable::SharedPtr waitable_ptr) noexcept;
|
||||
|
||||
CallbackGroupType type_;
|
||||
// Mutex to protect the subsequent vectors of pointers.
|
||||
mutable std::mutex mutex_;
|
||||
@@ -108,6 +123,7 @@ protected:
|
||||
std::vector<rclcpp::TimerBase::WeakPtr> timer_ptrs_;
|
||||
std::vector<rclcpp::ServiceBase::WeakPtr> service_ptrs_;
|
||||
std::vector<rclcpp::ClientBase::WeakPtr> client_ptrs_;
|
||||
std::vector<rclcpp::Waitable::WeakPtr> waitable_ptrs_;
|
||||
std::atomic_bool can_be_taken_from_;
|
||||
};
|
||||
|
||||
|
||||
@@ -78,10 +78,10 @@ public:
|
||||
bool
|
||||
service_is_ready() const;
|
||||
|
||||
template<typename RatioT = std::milli>
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
bool
|
||||
wait_for_service(
|
||||
std::chrono::duration<int64_t, RatioT> timeout = std::chrono::duration<int64_t, RatioT>(-1))
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return wait_for_service_nanoseconds(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
@@ -110,6 +110,7 @@ protected:
|
||||
|
||||
rclcpp::node_interfaces::NodeGraphInterface::WeakPtr node_graph_;
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
std::shared_ptr<rclcpp::Context> context_;
|
||||
|
||||
std::shared_ptr<rcl_client_t> client_handle_;
|
||||
};
|
||||
@@ -171,13 +172,13 @@ public:
|
||||
}
|
||||
|
||||
std::shared_ptr<void>
|
||||
create_response()
|
||||
create_response() override
|
||||
{
|
||||
return std::shared_ptr<void>(new typename ServiceT::Response());
|
||||
}
|
||||
|
||||
std::shared_ptr<rmw_request_id_t>
|
||||
create_request_header()
|
||||
create_request_header() override
|
||||
{
|
||||
// TODO(wjwwood): This should probably use rmw_request_id's allocator.
|
||||
// (since it is a C type)
|
||||
@@ -187,7 +188,7 @@ public:
|
||||
void
|
||||
handle_response(
|
||||
std::shared_ptr<rmw_request_id_t> request_header,
|
||||
std::shared_ptr<void> response)
|
||||
std::shared_ptr<void> response) override
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(pending_requests_mutex_);
|
||||
auto typed_response = std::static_pointer_cast<typename ServiceT::Response>(response);
|
||||
|
||||
@@ -15,34 +15,292 @@
|
||||
#ifndef RCLCPP__CONTEXT_HPP_
|
||||
#define RCLCPP__CONTEXT_HPP_
|
||||
|
||||
#include <iostream>
|
||||
#include <condition_variable>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <typeindex>
|
||||
#include <typeinfo>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/context.h"
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
#include "rclcpp/init_options.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
class Context
|
||||
/// Thrown when init is called on an already initialized context.
|
||||
class ContextAlreadyInitialized : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
ContextAlreadyInitialized()
|
||||
: std::runtime_error("context is already initialized") {}
|
||||
};
|
||||
|
||||
/// Context which encapsulates shared state between nodes and other similar entities.
|
||||
/**
|
||||
* A context also represents the lifecycle between init and shutdown of rclcpp.
|
||||
* It is often used in conjunction with rclcpp::init, or rclcpp::init_local,
|
||||
* and rclcpp::shutdown.
|
||||
*/
|
||||
class Context : public std::enable_shared_from_this<Context>
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(Context)
|
||||
|
||||
/// Default constructor, after which the Context is still not "initialized".
|
||||
/**
|
||||
* Every context which is constructed is added to a global vector of contexts,
|
||||
* which is used by the signal handler to conditionally shutdown each context
|
||||
* on SIGINT.
|
||||
* See the shutdown_on_sigint option in the InitOptions class.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
Context();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~Context();
|
||||
|
||||
/// Initialize the context, and the underlying elements like the rcl context.
|
||||
/**
|
||||
* This method must be called before passing this context to things like the
|
||||
* constructor of Node.
|
||||
* It must also be called before trying to shutdown the context.
|
||||
*
|
||||
* Note that this function does not setup any signal handlers, so if you want
|
||||
* it to be shutdown by the signal handler, then you need to either install
|
||||
* them manually with rclcpp::install_signal_handers() or use rclcpp::init().
|
||||
* In addition to installing the signal handlers, the shutdown_on_sigint
|
||||
* of the InitOptions needs to be `true` for this context to be shutdown by
|
||||
* the signal handler, otherwise it will be passed over.
|
||||
*
|
||||
* After calling this method, shutdown() can be called to invalidate the
|
||||
* context for derived entities, e.g. nodes, guard conditions, etc.
|
||||
* However, the underlying rcl context is not finalized until this Context's
|
||||
* destructor is called or this function is called again.
|
||||
* Allowing this class to go out of scope and get destructed or calling this
|
||||
* function a second time while derived entities are still using the context
|
||||
* is undefined behavior and should be avoided.
|
||||
* It's a good idea to not reuse context objects and instead create a new one
|
||||
* each time you need to shutdown and init again.
|
||||
* This allows derived entities to hold on to shard pointers to the first
|
||||
* context object until they are done.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* \param[in] argc number of arguments
|
||||
* \param[in] argv argument array which may contain arguments intended for ROS
|
||||
* \param[in] init_options initialization options for rclcpp and underlying layers
|
||||
* \throw ContextAlreadyInitialized if called if init is called more than once
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
init(
|
||||
int argc,
|
||||
char const * const argv[],
|
||||
const rclcpp::InitOptions & init_options = rclcpp::InitOptions());
|
||||
|
||||
/// Return true if the context is valid, otherwise false.
|
||||
/**
|
||||
* The context is valid if it has been initialized but not shutdown.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
* This function is lock free so long as pointers and uint64_t atomics are
|
||||
* lock free.
|
||||
*
|
||||
* \return true if valid, otherwise false
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_valid() const;
|
||||
|
||||
/// Return the init options used during init.
|
||||
RCLCPP_PUBLIC
|
||||
const rclcpp::InitOptions &
|
||||
get_init_options() const;
|
||||
|
||||
/// Return a copy of the init options used during init.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::InitOptions
|
||||
get_init_options();
|
||||
|
||||
/// Return the shutdown reason, or empty string if not shutdown.
|
||||
/**
|
||||
* This function is thread-safe.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::string
|
||||
shutdown_reason();
|
||||
|
||||
/// Shutdown the context, making it uninitialized and therefore invalid for derived entities.
|
||||
/**
|
||||
* Several things happen when the context is shutdown, in this order:
|
||||
*
|
||||
* - acquires a lock to prevent race conditions with init, on_shutdown, etc.
|
||||
* - if the context is not initialized, return false
|
||||
* - rcl_shutdown() is called on the internal rcl_context_t instance
|
||||
* - the shutdown reason is set
|
||||
* - each on_shutdown callback is called, in the order that they were added
|
||||
* - interrupt blocking sleep_for() calls, so they return early due to shutdown
|
||||
* - interrupt blocking executors and wait sets
|
||||
*
|
||||
* The underlying rcl context is not finalized by this function.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* \param[in] reason the description of why shutdown happened
|
||||
* \return true if shutdown was successful, false if context was already shutdown
|
||||
* \throw various exceptions derived from RCLErrorBase, if rcl_shutdown fails
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
shutdown(const std::string & reason);
|
||||
|
||||
using OnShutdownCallback = std::function<void ()>;
|
||||
|
||||
/// Add a on_shutdown callback to be called when shutdown is called for this context.
|
||||
/**
|
||||
* These callbacks will be called in the order they are added as the second
|
||||
* to last step in shutdown().
|
||||
*
|
||||
* When shutdown occurs due to the signal handler, these callbacks are run
|
||||
* asynchronoulsy in the dedicated singal handling thread.
|
||||
*
|
||||
* Also, shutdown() may be called from the destructor of this function.
|
||||
* Therefore, it is not safe to throw exceptions from these callbacks.
|
||||
* Instead, log errors or use some other mechanism to indicate an error has
|
||||
* occurred.
|
||||
*
|
||||
* On shutdown callbacks may be registered before init and after shutdown,
|
||||
* and persist on repeated init's.
|
||||
*
|
||||
* \param[in] callback the on shutdown callback to be registered
|
||||
* \return the callback passed, for convenience when storing a passed lambda
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
OnShutdownCallback
|
||||
on_shutdown(OnShutdownCallback callback);
|
||||
|
||||
/// Return the shutdown callbacks as const.
|
||||
/**
|
||||
* Using the returned reference is not thread-safe with calls that modify
|
||||
* the list of "on shutdown" callbacks, i.e. on_shutdown().
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<OnShutdownCallback> &
|
||||
get_on_shutdown_callbacks() const;
|
||||
|
||||
/// Return the shutdown callbacks.
|
||||
/**
|
||||
* Using the returned reference is not thread-safe with calls that modify
|
||||
* the list of "on shutdown" callbacks, i.e. on_shutdown().
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<OnShutdownCallback> &
|
||||
get_on_shutdown_callbacks();
|
||||
|
||||
/// Return the internal rcl context.
|
||||
RCLCPP_PUBLIC
|
||||
std::shared_ptr<rcl_context_t>
|
||||
get_rcl_context();
|
||||
|
||||
/// Sleep for a given period of time or until shutdown() is called.
|
||||
/**
|
||||
* This function can be interrupted early if:
|
||||
*
|
||||
* - this context is shutdown()
|
||||
* - this context is destructed (resulting in shutdown)
|
||||
* - this context has shutdown_on_sigint=true and SIGINT occurs (resulting in shutdown)
|
||||
* - interrupt_all_sleep_for() is called
|
||||
*
|
||||
* \param[in] nanoseconds A std::chrono::duration representing how long to sleep for.
|
||||
* \return true if the condition variable did not timeout, i.e. you were interrupted.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
sleep_for(const std::chrono::nanoseconds & nanoseconds);
|
||||
|
||||
/// Interrupt any blocking sleep_for calls, causing them to return immediately and return true.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
interrupt_all_sleep_for();
|
||||
|
||||
/// Get a handle to the guard condition which is triggered when interrupted.
|
||||
/**
|
||||
* This guard condition is triggered any time interrupt_all_wait_sets() is
|
||||
* called, which may be called by the user, or shutdown().
|
||||
* And in turn, shutdown() may be called by the user, the destructor of this
|
||||
* context, or the signal handler if installed and shutdown_on_sigint is true
|
||||
* for this context.
|
||||
*
|
||||
* The first time that this function is called for a given wait set a new guard
|
||||
* condition will be created and returned; thereafter the same guard condition
|
||||
* will be returned for the same wait set.
|
||||
* This mechanism is designed to ensure that the same guard condition is not
|
||||
* reused across wait sets (e.g., when using multiple executors in the same
|
||||
* process).
|
||||
* This method will throw an exception if initialization of the guard
|
||||
* condition fails.
|
||||
*
|
||||
* The returned guard condition needs to be released with the
|
||||
* release_interrupt_guard_condition() method in order to reclaim resources.
|
||||
*
|
||||
* \param[in] wait_set 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_interrupt_guard_condition(rcl_wait_set_t * wait_set);
|
||||
|
||||
/// Release the previously allocated guard condition which is triggered when interrupted.
|
||||
/**
|
||||
* If you previously called get_interrupt_guard_condition() for a given wait
|
||||
* set to get a interrupt guard condition, then you should call
|
||||
* release_interrupt_guard_condition() when you're done, to free that
|
||||
* condition.
|
||||
* Will throw an exception if get_interrupt_guard_condition() wasn't
|
||||
* previously called for the given wait set.
|
||||
*
|
||||
* After calling this, the pointer returned by get_interrupt_guard_condition()
|
||||
* for the given wait_set is invalid.
|
||||
*
|
||||
* \param[in] wait_set Pointer to the rcl_wait_set_t that was using the
|
||||
* resulting guard condition.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
release_interrupt_guard_condition(rcl_wait_set_t * wait_set);
|
||||
|
||||
/// Nothrow version of release_interrupt_guard_condition(), logs to RCLCPP_ERROR instead.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
release_interrupt_guard_condition(rcl_wait_set_t * wait_set, const std::nothrow_t &) noexcept;
|
||||
|
||||
/// Interrupt any blocking executors, or wait sets associated with this context.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
interrupt_all_wait_sets();
|
||||
|
||||
/// Return a singleton instance for the SubContext type, constructing one if necessary.
|
||||
template<typename SubContext, typename ... Args>
|
||||
std::shared_ptr<SubContext>
|
||||
get_sub_context(Args && ... args)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
std::lock_guard<std::recursive_mutex> lock(sub_contexts_mutex_);
|
||||
|
||||
std::type_index type_i(typeid(SubContext));
|
||||
std::shared_ptr<SubContext> sub_context;
|
||||
@@ -62,13 +320,51 @@ public:
|
||||
return sub_context;
|
||||
}
|
||||
|
||||
protected:
|
||||
// Called by constructor and destructor to clean up by finalizing the
|
||||
// shutdown rcl context and preparing for a new init cycle.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
clean_up();
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Context)
|
||||
|
||||
// This mutex is recursive so that the destructor can ensure atomicity
|
||||
// between is_initialized and shutdown.
|
||||
std::recursive_mutex init_mutex_;
|
||||
std::shared_ptr<rcl_context_t> rcl_context_;
|
||||
rclcpp::InitOptions init_options_;
|
||||
std::string shutdown_reason_;
|
||||
|
||||
std::unordered_map<std::type_index, std::shared_ptr<void>> sub_contexts_;
|
||||
std::mutex mutex_;
|
||||
// This mutex is recursive so that the constructor of a sub context may
|
||||
// attempt to acquire another sub context.
|
||||
std::recursive_mutex sub_contexts_mutex_;
|
||||
|
||||
std::vector<OnShutdownCallback> on_shutdown_callbacks_;
|
||||
std::mutex on_shutdown_callbacks_mutex_;
|
||||
|
||||
/// Condition variable for timed sleep (see sleep_for).
|
||||
std::condition_variable interrupt_condition_variable_;
|
||||
/// Mutex for protecting the global condition variable.
|
||||
std::mutex interrupt_mutex_;
|
||||
|
||||
/// Mutex to protect sigint_guard_cond_handles_.
|
||||
std::mutex interrupt_guard_cond_handles_mutex_;
|
||||
/// Guard conditions for interrupting of associated wait sets on interrupt_all_wait_sets().
|
||||
std::unordered_map<rcl_wait_set_t *, rcl_guard_condition_t> interrupt_guard_cond_handles_;
|
||||
};
|
||||
|
||||
/// Return a copy of the list of context shared pointers.
|
||||
/**
|
||||
* This function is thread-safe.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<Context::SharedPtr>
|
||||
get_contexts();
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__CONTEXT_HPP_
|
||||
|
||||
@@ -28,9 +28,10 @@
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/memory_strategies.hpp"
|
||||
#include "rclcpp/memory_strategy.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -65,16 +66,20 @@ to_string(const FutureReturnCode & future_return_code);
|
||||
*/
|
||||
struct ExecutorArgs
|
||||
{
|
||||
ExecutorArgs()
|
||||
: memory_strategy(memory_strategies::create_default_strategy()),
|
||||
context(rclcpp::contexts::default_context::get_global_default_context()),
|
||||
max_conditions(0)
|
||||
{}
|
||||
|
||||
memory_strategy::MemoryStrategy::SharedPtr memory_strategy;
|
||||
size_t max_conditions = 0;
|
||||
std::shared_ptr<rclcpp::Context> context;
|
||||
size_t max_conditions;
|
||||
};
|
||||
|
||||
static inline ExecutorArgs create_default_executor_arguments()
|
||||
{
|
||||
ExecutorArgs args;
|
||||
args.memory_strategy = memory_strategies::create_default_strategy();
|
||||
args.max_conditions = 0;
|
||||
return args;
|
||||
return ExecutorArgs();
|
||||
}
|
||||
|
||||
/// Coordinate the order and timing of available communication tasks.
|
||||
@@ -95,7 +100,7 @@ public:
|
||||
/// Default constructor.
|
||||
// \param[in] ms The memory strategy to be used with this executor.
|
||||
RCLCPP_PUBLIC
|
||||
explicit Executor(const ExecutorArgs & args = create_default_executor_arguments());
|
||||
explicit Executor(const ExecutorArgs & args = ExecutorArgs());
|
||||
|
||||
/// Default destructor.
|
||||
RCLCPP_PUBLIC
|
||||
@@ -146,11 +151,11 @@ public:
|
||||
* spin_node_once to block indefinitely (the default behavior). A timeout of 0 causes this
|
||||
* function to be non-blocking.
|
||||
*/
|
||||
template<typename T = std::milli>
|
||||
template<typename RepT = int64_t, typename T = std::milli>
|
||||
void
|
||||
spin_node_once(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node,
|
||||
std::chrono::duration<int64_t, T> timeout = std::chrono::duration<int64_t, T>(-1))
|
||||
std::chrono::duration<RepT, T> timeout = std::chrono::duration<RepT, T>(-1))
|
||||
{
|
||||
return spin_node_once_nanoseconds(
|
||||
node,
|
||||
@@ -159,11 +164,11 @@ public:
|
||||
}
|
||||
|
||||
/// Convenience function which takes Node and forwards NodeBaseInterface.
|
||||
template<typename NodeT = rclcpp::Node, typename T = std::milli>
|
||||
template<typename NodeT = rclcpp::Node, typename RepT = int64_t, 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))
|
||||
std::chrono::duration<RepT, T> timeout = std::chrono::duration<RepT, T>(-1))
|
||||
{
|
||||
return spin_node_once_nanoseconds(
|
||||
node->get_node_base_interface(),
|
||||
@@ -213,11 +218,11 @@ public:
|
||||
* code.
|
||||
* \return The return code, one of `SUCCESS`, `INTERRUPTED`, or `TIMEOUT`.
|
||||
*/
|
||||
template<typename ResponseT, typename TimeT = std::milli>
|
||||
template<typename ResponseT, typename TimeRepT = int64_t, 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))
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
|
||||
// inside a callback executed by an executor.
|
||||
@@ -237,7 +242,7 @@ public:
|
||||
}
|
||||
std::chrono::nanoseconds timeout_left = timeout_ns;
|
||||
|
||||
while (rclcpp::ok()) {
|
||||
while (rclcpp::ok(this->context_)) {
|
||||
// Do one item of work.
|
||||
spin_once(timeout_left);
|
||||
// Check if the future is set, return SUCCESS if it is.
|
||||
@@ -353,6 +358,9 @@ protected:
|
||||
/// The memory strategy: an interface for handling user-defined memory allocation strategies.
|
||||
memory_strategy::MemoryStrategy::SharedPtr memory_strategy_;
|
||||
|
||||
/// The context associated with this executor.
|
||||
std::shared_ptr<rclcpp::Context> context_;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Executor)
|
||||
|
||||
|
||||
@@ -65,13 +65,13 @@ using rclcpp::executors::SingleThreadedExecutor;
|
||||
* 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>
|
||||
template<typename ResponseT, typename TimeRepT = int64_t, typename TimeT = std::milli>
|
||||
rclcpp::executor::FutureReturnCode
|
||||
spin_node_until_future_complete(
|
||||
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))
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
// TODO(wjwwood): does not work recursively; can't call spin_node_until_future_complete
|
||||
// inside a callback executed by an executor.
|
||||
@@ -81,13 +81,14 @@ spin_node_until_future_complete(
|
||||
return retcode;
|
||||
}
|
||||
|
||||
template<typename NodeT = rclcpp::Node, typename ResponseT, typename TimeT = std::milli>
|
||||
template<typename NodeT = rclcpp::Node, typename ResponseT, typename TimeRepT = int64_t,
|
||||
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))
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
return rclcpp::executors::spin_node_until_future_complete(
|
||||
executor,
|
||||
@@ -98,23 +99,24 @@ spin_node_until_future_complete(
|
||||
|
||||
} // namespace executors
|
||||
|
||||
template<typename FutureT, typename TimeT = std::milli>
|
||||
template<typename FutureT, typename TimeRepT = int64_t, 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))
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
return executors::spin_node_until_future_complete<FutureT>(executor, node_ptr, future, timeout);
|
||||
}
|
||||
|
||||
template<typename NodeT = rclcpp::Node, typename FutureT, typename TimeT = std::milli>
|
||||
template<typename NodeT = rclcpp::Node, typename FutureT, typename TimeRepT = int64_t,
|
||||
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))
|
||||
std::chrono::duration<TimeRepT, TimeT> timeout = std::chrono::duration<TimeRepT, TimeT>(-1))
|
||||
{
|
||||
return rclcpp::spin_until_future_complete(node_ptr->get_node_base_interface(), future, timeout);
|
||||
}
|
||||
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
MultiThreadedExecutor(
|
||||
const executor::ExecutorArgs & args = rclcpp::executor::create_default_executor_arguments(),
|
||||
const executor::ExecutorArgs & args = executor::ExecutorArgs(),
|
||||
size_t number_of_threads = 0,
|
||||
bool yield_before_execute = false);
|
||||
|
||||
@@ -78,7 +78,6 @@ private:
|
||||
size_t number_of_threads_;
|
||||
bool yield_before_execute_;
|
||||
|
||||
std::mutex scheduled_timers_mutex_;
|
||||
std::set<TimerBase::SharedPtr> scheduled_timers_;
|
||||
};
|
||||
|
||||
|
||||
@@ -45,7 +45,7 @@ public:
|
||||
/// Default constructor. See the default constructor for Executor.
|
||||
RCLCPP_PUBLIC
|
||||
SingleThreadedExecutor(
|
||||
const executor::ExecutorArgs & args = rclcpp::executor::create_default_executor_arguments());
|
||||
const executor::ExecutorArgs & args = executor::ExecutorArgs());
|
||||
|
||||
/// Default destrcutor.
|
||||
RCLCPP_PUBLIC
|
||||
|
||||
@@ -49,14 +49,14 @@ template<typename FunctionT>
|
||||
struct function_traits
|
||||
{
|
||||
using arguments = typename tuple_tail<
|
||||
typename function_traits<decltype( & FunctionT::operator())>::arguments>::type;
|
||||
typename function_traits<decltype( &FunctionT::operator())>::arguments>::type;
|
||||
|
||||
static constexpr std::size_t arity = std::tuple_size<arguments>::value;
|
||||
|
||||
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;
|
||||
using return_type = typename function_traits<decltype( &FunctionT::operator())>::return_type;
|
||||
};
|
||||
|
||||
// Free functions
|
||||
@@ -81,7 +81,7 @@ struct function_traits<ReturnTypeT (*)(Args ...)>: function_traits<ReturnTypeT(A
|
||||
// std::bind for object methods
|
||||
template<typename ClassT, typename ReturnTypeT, typename ... Args, typename ... FArgs>
|
||||
#if defined _LIBCPP_VERSION // libc++ (Clang)
|
||||
struct function_traits<std::__1::__bind<ReturnTypeT (ClassT::*)(Args ...), FArgs ...>>
|
||||
struct function_traits<std::__bind<ReturnTypeT (ClassT::*)(Args ...), FArgs ...>>
|
||||
#elif defined _GLIBCXX_RELEASE // glibc++ (GNU C++ >= 7.1)
|
||||
struct function_traits<std::_Bind<ReturnTypeT(ClassT::*(FArgs ...))(Args ...)>>
|
||||
#elif defined __GLIBCXX__ // glibc++ (GNU C++)
|
||||
@@ -99,7 +99,7 @@ struct function_traits<
|
||||
// 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 ...>>
|
||||
struct function_traits<std::__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
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
@@ -62,7 +63,7 @@ class GraphListener : public std::enable_shared_from_this<GraphListener>
|
||||
{
|
||||
public:
|
||||
RCLCPP_PUBLIC
|
||||
GraphListener();
|
||||
explicit GraphListener(std::shared_ptr<rclcpp::Context> parent_context);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~GraphListener();
|
||||
@@ -133,6 +134,12 @@ public:
|
||||
void
|
||||
shutdown();
|
||||
|
||||
/// Nothrow version of shutdown(), logs to RCLCPP_ERROR instead.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
shutdown(const std::nothrow_t &) noexcept;
|
||||
|
||||
/// Return true if shutdown() has been called, else false.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -154,6 +161,12 @@ protected:
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(GraphListener)
|
||||
|
||||
/** \internal */
|
||||
void
|
||||
__shutdown(bool);
|
||||
|
||||
rclcpp::Context::SharedPtr parent_context_;
|
||||
|
||||
std::thread listener_thread_;
|
||||
bool is_started_;
|
||||
std::atomic_bool is_shutdown_;
|
||||
@@ -164,6 +177,7 @@ private:
|
||||
std::vector<rclcpp::node_interfaces::NodeGraphInterface *> node_graph_interfaces_;
|
||||
|
||||
rcl_guard_condition_t interrupt_guard_condition_ = rcl_get_zero_initialized_guard_condition();
|
||||
std::shared_ptr<rcl_context_t> interrupt_guard_condition_context_;
|
||||
rcl_guard_condition_t * shutdown_guard_condition_;
|
||||
rcl_wait_set_t wait_set_ = rcl_get_zero_initialized_wait_set();
|
||||
};
|
||||
|
||||
69
rclcpp/include/rclcpp/init_options.hpp
Normal file
69
rclcpp/include/rclcpp/init_options.hpp
Normal file
@@ -0,0 +1,69 @@
|
||||
// Copyright 2018 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__INIT_OPTIONS_HPP_
|
||||
#define RCLCPP__INIT_OPTIONS_HPP_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "rcl/init_options.h"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Encapsulation of options for initializing rclcpp.
|
||||
class InitOptions
|
||||
{
|
||||
public:
|
||||
/// If true, the context will be shutdown on SIGINT by the signal handler (if it was installed).
|
||||
bool shutdown_on_sigint = true;
|
||||
|
||||
/// Constructor which allows you to specify the allocator used within the init options.
|
||||
RCLCPP_PUBLIC
|
||||
explicit InitOptions(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
/// Constructor which is initialized by an existing init_options.
|
||||
RCLCPP_PUBLIC
|
||||
explicit InitOptions(const rcl_init_options_t & init_options);
|
||||
|
||||
/// Copy constructor.
|
||||
RCLCPP_PUBLIC
|
||||
InitOptions(const InitOptions & other);
|
||||
|
||||
/// Assignment operator.
|
||||
RCLCPP_PUBLIC
|
||||
InitOptions &
|
||||
operator=(const InitOptions & other);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~InitOptions();
|
||||
|
||||
/// Return the rcl init options.
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_init_options_t *
|
||||
get_rcl_init_options() const;
|
||||
|
||||
protected:
|
||||
void
|
||||
finalize_init_options();
|
||||
|
||||
private:
|
||||
std::unique_ptr<rcl_init_options_t> init_options_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__INIT_OPTIONS_HPP_
|
||||
@@ -346,6 +346,11 @@ public:
|
||||
bool
|
||||
matches_any_publishers(const rmw_gid_t * id) const;
|
||||
|
||||
/// Return the number of intraprocess subscriptions to a topic, given the publisher id.
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_subscription_count(uint64_t intra_process_publisher_id) const;
|
||||
|
||||
private:
|
||||
RCLCPP_PUBLIC
|
||||
static uint64_t
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#define RCLCPP__INTRA_PROCESS_MANAGER_IMPL_HPP_
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
@@ -24,10 +25,13 @@
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <set>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
#include "rmw/validate_full_topic_name.h"
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/mapped_ring_buffer.hpp"
|
||||
#include "rclcpp/publisher.hpp"
|
||||
@@ -80,6 +84,9 @@ public:
|
||||
virtual bool
|
||||
matches_any_publishers(const rmw_gid_t * id) const = 0;
|
||||
|
||||
virtual size_t
|
||||
get_subscription_count(uint64_t intra_process_publisher_id) const = 0;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(IntraProcessManagerImplBase)
|
||||
};
|
||||
@@ -95,9 +102,7 @@ public:
|
||||
add_subscription(uint64_t id, 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);
|
||||
subscription_ids_by_topic_[fixed_size_string(subscription->get_topic_name())].insert(id);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -172,7 +177,8 @@ public:
|
||||
}
|
||||
|
||||
// Figure out what subscriptions should receive the message.
|
||||
auto & destined_subscriptions = subscription_ids_by_topic_[publisher->get_topic_name()];
|
||||
auto & destined_subscriptions =
|
||||
subscription_ids_by_topic_[fixed_size_string(publisher->get_topic_name())];
|
||||
// Store the list for later comparison.
|
||||
if (info.target_subscriptions_by_message_sequence.count(message_seq) == 0) {
|
||||
info.target_subscriptions_by_message_sequence.emplace(
|
||||
@@ -248,9 +254,52 @@ public:
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t
|
||||
get_subscription_count(uint64_t intra_process_publisher_id) const
|
||||
{
|
||||
auto publisher_it = publishers_.find(intra_process_publisher_id);
|
||||
if (publisher_it == publishers_.end()) {
|
||||
// Publisher is either invalid or no longer exists.
|
||||
return 0;
|
||||
}
|
||||
auto publisher = publisher_it->second.publisher.lock();
|
||||
if (!publisher) {
|
||||
throw std::runtime_error("publisher has unexpectedly gone out of scope");
|
||||
}
|
||||
auto sub_map_it =
|
||||
subscription_ids_by_topic_.find(fixed_size_string(publisher->get_topic_name()));
|
||||
if (sub_map_it == subscription_ids_by_topic_.end()) {
|
||||
// No intraprocess subscribers
|
||||
return 0;
|
||||
}
|
||||
return sub_map_it->second.size();
|
||||
}
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(IntraProcessManagerImpl)
|
||||
|
||||
using FixedSizeString = std::array<char, RMW_TOPIC_MAX_NAME_LENGTH + 1>;
|
||||
|
||||
FixedSizeString
|
||||
fixed_size_string(const char * str) const
|
||||
{
|
||||
FixedSizeString ret;
|
||||
size_t size = std::strlen(str) + 1;
|
||||
if (size > ret.size()) {
|
||||
throw std::runtime_error("failed to copy topic name");
|
||||
}
|
||||
std::memcpy(ret.data(), str, size);
|
||||
return ret;
|
||||
}
|
||||
struct strcmp_wrapper
|
||||
{
|
||||
bool
|
||||
operator()(const FixedSizeString lhs, const FixedSizeString rhs) const
|
||||
{
|
||||
return std::strcmp(lhs.data(), rhs.data()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
using RebindAlloc = typename std::allocator_traits<Allocator>::template rebind_alloc<T>;
|
||||
|
||||
@@ -262,19 +311,11 @@ private:
|
||||
std::hash<uint64_t>, std::equal_to<uint64_t>,
|
||||
RebindAlloc<std::pair<const uint64_t, SubscriptionBase::WeakPtr>>>;
|
||||
|
||||
struct strcmp_wrapper
|
||||
{
|
||||
bool
|
||||
operator()(const char * lhs, const char * rhs) const
|
||||
{
|
||||
return std::strcmp(lhs, rhs) < 0;
|
||||
}
|
||||
};
|
||||
using IDTopicMap = std::map<
|
||||
const char *,
|
||||
FixedSizeString,
|
||||
AllocSet,
|
||||
strcmp_wrapper,
|
||||
RebindAlloc<std::pair<const char * const, AllocSet>>>;
|
||||
RebindAlloc<std::pair<const FixedSizeString, AllocSet>>>;
|
||||
|
||||
SubscriptionMap subscriptions_;
|
||||
|
||||
|
||||
34
rclcpp/include/rclcpp/intra_process_setting.hpp
Normal file
34
rclcpp/include/rclcpp/intra_process_setting.hpp
Normal file
@@ -0,0 +1,34 @@
|
||||
// Copyright 2019 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__INTRA_PROCESS_SETTING_HPP_
|
||||
#define RCLCPP__INTRA_PROCESS_SETTING_HPP_
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Used as argument in create_publisher and create_subscriber.
|
||||
enum class IntraProcessSetting
|
||||
{
|
||||
/// Explicitly enable intraprocess comm at publisher/subscription level.
|
||||
Enable,
|
||||
/// Explicitly disable intraprocess comm at publisher/subscription level.
|
||||
Disable,
|
||||
/// Take intraprocess configuration from the node.
|
||||
NodeDefault
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__INTRA_PROCESS_SETTING_HPP_
|
||||
@@ -20,6 +20,8 @@
|
||||
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rcl/node.h"
|
||||
|
||||
/**
|
||||
* \def RCLCPP_LOGGING_ENABLED
|
||||
* When this define evaluates to true (default), logger factory functions will
|
||||
@@ -60,6 +62,18 @@ RCLCPP_PUBLIC
|
||||
Logger
|
||||
get_logger(const std::string & name);
|
||||
|
||||
/// Return a named logger using an rcl_node_t.
|
||||
/**
|
||||
* This is a convenience function that does error checking and returns the node
|
||||
* logger name, or "rclcpp" if it is unable to get the node name.
|
||||
*
|
||||
* \param[in] node the rcl node from which to get the logger name
|
||||
* \return a logger based on the node name, or "rclcpp" if there's an error
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
Logger
|
||||
get_node_logger(const rcl_node_t * node);
|
||||
|
||||
class Logger
|
||||
{
|
||||
private:
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
@@ -52,6 +53,7 @@ public:
|
||||
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 size_t number_of_waitables() const = 0;
|
||||
|
||||
virtual bool add_handles_to_wait_set(rcl_wait_set_t * wait_set) = 0;
|
||||
virtual void clear_handles() = 0;
|
||||
@@ -76,6 +78,11 @@ public:
|
||||
rclcpp::executor::AnyExecutable & any_exec,
|
||||
const WeakNodeVector & weak_nodes) = 0;
|
||||
|
||||
virtual void
|
||||
get_next_waitable(
|
||||
rclcpp::executor::AnyExecutable & any_exec,
|
||||
const WeakNodeVector & weak_nodes) = 0;
|
||||
|
||||
virtual rcl_allocator_t
|
||||
get_allocator() = 0;
|
||||
|
||||
@@ -113,6 +120,11 @@ public:
|
||||
get_group_by_client(
|
||||
rclcpp::ClientBase::SharedPtr client,
|
||||
const WeakNodeVector & weak_nodes);
|
||||
|
||||
static rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
get_group_by_waitable(
|
||||
rclcpp::Waitable::SharedPtr waitable,
|
||||
const WeakNodeVector & weak_nodes);
|
||||
};
|
||||
|
||||
} // namespace memory_strategy
|
||||
|
||||
@@ -41,18 +41,23 @@
|
||||
#include "rclcpp/logger.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/message_memory_strategy.hpp"
|
||||
#include "rclcpp/node_options.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_clock_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_graph_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_logging_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_services_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_time_source_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_timers_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_waitables_interface.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/publisher.hpp"
|
||||
#include "rclcpp/publisher_options.hpp"
|
||||
#include "rclcpp/service.hpp"
|
||||
#include "rclcpp/subscription.hpp"
|
||||
#include "rclcpp/subscription_options.hpp"
|
||||
#include "rclcpp/subscription_traits.hpp"
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp/timer.hpp"
|
||||
@@ -70,40 +75,24 @@ public:
|
||||
/// 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.
|
||||
* \param[in] options Additional options to control creation of the node.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit Node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_ = "",
|
||||
bool use_intra_process_comms = false);
|
||||
const NodeOptions & options = NodeOptions());
|
||||
|
||||
/// Create a node based on the node name and a rclcpp::Context.
|
||||
/// 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] context The context for the node (usually represents the state of a process).
|
||||
* \param[in] arguments Command line arguments that should apply only to this node.
|
||||
* \param[in] initial_parameters a list of initial values for parameters on the node.
|
||||
* This can be used to provide remapping rules that only affect one instance.
|
||||
* \param[in] use_global_arguments False to prevent node using arguments passed to the 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.
|
||||
* \param[in] start_parameter_services True to setup ROS interfaces for accessing parameters
|
||||
* in the node.
|
||||
* \param[in] options Additional options to control creation of the node.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
Node(
|
||||
explicit Node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::vector<std::string> & arguments,
|
||||
const std::vector<Parameter> & initial_parameters,
|
||||
bool use_global_arguments = true,
|
||||
bool use_intra_process_comms = false,
|
||||
bool start_parameter_services = true);
|
||||
const NodeOptions & options = NodeOptions());
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual ~Node();
|
||||
@@ -115,11 +104,27 @@ public:
|
||||
get_name() const;
|
||||
|
||||
/// Get the namespace of the node.
|
||||
/** \return The namespace of the node. */
|
||||
/**
|
||||
* This namespace is the "node's" namespace, and therefore is not affected
|
||||
* by any sub-namespace's that may affect entities created with this instance.
|
||||
* Use get_effective_namespace() to get the full namespace used by entities.
|
||||
*
|
||||
* \sa get_sub_namespace()
|
||||
* \sa get_effective_namespace()
|
||||
* \return The namespace of the node.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
get_namespace() const;
|
||||
|
||||
/// Get the fully-qualified name of the node.
|
||||
/**
|
||||
* The fully-qualified name includes the local namespace and name of the node.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const char *
|
||||
get_fully_qualified_name() const;
|
||||
|
||||
/// Get the logger of the node.
|
||||
/** \return The logger of the node. */
|
||||
RCLCPP_PUBLIC
|
||||
@@ -136,6 +141,24 @@ 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] options Additional options for the created Publisher.
|
||||
* \return Shared pointer to the created publisher.
|
||||
*/
|
||||
template<
|
||||
typename MessageT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename PublisherT = ::rclcpp::Publisher<MessageT, AllocatorT>>
|
||||
std::shared_ptr<PublisherT>
|
||||
create_publisher(
|
||||
const std::string & topic_name,
|
||||
size_t qos_history_depth,
|
||||
const PublisherOptionsWithAllocator<AllocatorT> &
|
||||
options = PublisherOptionsWithAllocator<AllocatorT>());
|
||||
|
||||
/// Create and return a Publisher.
|
||||
/**
|
||||
* \param[in] topic_name The topic for this publisher to publish on.
|
||||
@@ -146,10 +169,16 @@ public:
|
||||
template<
|
||||
typename MessageT, typename Alloc = std::allocator<void>,
|
||||
typename PublisherT = ::rclcpp::Publisher<MessageT, Alloc>>
|
||||
// cppcheck-suppress syntaxError // bug in cppcheck 1.82 for [[deprecated]] on templated function
|
||||
[[deprecated(
|
||||
"use the create_publisher(const std::string &, size_t, const PublisherOptions<Alloc> & = "
|
||||
"PublisherOptions<Alloc>()) signature instead")]]
|
||||
std::shared_ptr<PublisherT>
|
||||
create_publisher(
|
||||
const std::string & topic_name, size_t qos_history_depth,
|
||||
std::shared_ptr<Alloc> allocator = nullptr);
|
||||
const std::string & topic_name,
|
||||
size_t qos_history_depth,
|
||||
std::shared_ptr<Alloc> allocator,
|
||||
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
|
||||
|
||||
/// Create and return a Publisher.
|
||||
/**
|
||||
@@ -165,7 +194,39 @@ public:
|
||||
create_publisher(
|
||||
const std::string & topic_name,
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_default,
|
||||
std::shared_ptr<Alloc> allocator = nullptr);
|
||||
std::shared_ptr<Alloc> allocator = nullptr,
|
||||
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
|
||||
|
||||
/// Create and return a Subscription.
|
||||
/**
|
||||
* \param[in] topic_name The topic to subscribe on.
|
||||
* \param[in] callback The user-defined callback function to receive a message
|
||||
* \param[in] qos_history_depth The depth of the subscription's incoming message queue.
|
||||
* \param[in] options Additional options for the creation of the Subscription.
|
||||
* \param[in] msg_mem_strat The message memory strategy to use for allocating messages.
|
||||
* \return Shared pointer to the created subscription.
|
||||
*/
|
||||
/* TODO(jacquelinekay):
|
||||
Windows build breaks when static member function passed as default
|
||||
argument to msg_mem_strat, nullptr is a workaround.
|
||||
*/
|
||||
template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT = std::allocator<void>,
|
||||
typename SubscriptionT = rclcpp::Subscription<
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, AllocatorT>>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
const std::string & topic_name,
|
||||
CallbackT && callback,
|
||||
size_t qos_history_depth,
|
||||
const SubscriptionOptionsWithAllocator<AllocatorT> &
|
||||
options = SubscriptionOptionsWithAllocator<AllocatorT>(),
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, AllocatorT
|
||||
>::SharedPtr
|
||||
msg_mem_strat = nullptr);
|
||||
|
||||
/// Create and return a Subscription.
|
||||
/**
|
||||
@@ -198,7 +259,8 @@ public:
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
|
||||
msg_mem_strat = nullptr,
|
||||
std::shared_ptr<Alloc> allocator = nullptr);
|
||||
std::shared_ptr<Alloc> allocator = nullptr,
|
||||
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
|
||||
|
||||
/// Create and return a Subscription.
|
||||
/**
|
||||
@@ -221,17 +283,21 @@ public:
|
||||
typename Alloc = std::allocator<void>,
|
||||
typename SubscriptionT = rclcpp::Subscription<
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>>
|
||||
[[deprecated(
|
||||
"use the create_subscription(const std::string &, CallbackT &&, size_t, "
|
||||
"const SubscriptionOptions<Alloc> & = SubscriptionOptions<Alloc>(), ...) signature instead")]]
|
||||
std::shared_ptr<SubscriptionT>
|
||||
create_subscription(
|
||||
const std::string & topic_name,
|
||||
CallbackT && callback,
|
||||
size_t qos_history_depth,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group,
|
||||
bool ignore_local_publications = false,
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
|
||||
msg_mem_strat = nullptr,
|
||||
std::shared_ptr<Alloc> allocator = nullptr);
|
||||
std::shared_ptr<Alloc> allocator = nullptr,
|
||||
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault);
|
||||
|
||||
/// Create a timer.
|
||||
/**
|
||||
@@ -239,10 +305,10 @@ public:
|
||||
* \param[in] callback User-defined callback function.
|
||||
* \param[in] group Callback group to execute this timer's callback in.
|
||||
*/
|
||||
template<typename DurationT = std::milli, typename CallbackT>
|
||||
template<typename DurationRepT = int64_t, typename DurationT = std::milli, typename CallbackT>
|
||||
typename rclcpp::WallTimer<CallbackT>::SharedPtr
|
||||
create_wall_timer(
|
||||
std::chrono::duration<int64_t, DurationT> period,
|
||||
std::chrono::duration<DurationRepT, DurationT> period,
|
||||
CallbackT callback,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group = nullptr);
|
||||
|
||||
@@ -277,6 +343,20 @@ public:
|
||||
const std::string & name,
|
||||
const ParameterT & value);
|
||||
|
||||
/// Set a map of parameters with the same prefix.
|
||||
/**
|
||||
* For each key in the map, a parameter with a name of "name.key" will be set
|
||||
* to the value in the map.
|
||||
*
|
||||
* \param[in] name The prefix of the parameters to set.
|
||||
* \param[in] values The parameters to set in the given prefix.
|
||||
*/
|
||||
template<typename MapValueT>
|
||||
void
|
||||
set_parameters_if_not_set(
|
||||
const std::string & name,
|
||||
const std::map<std::string, MapValueT> & values);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::Parameter>
|
||||
get_parameters(const std::vector<std::string> & names) const;
|
||||
@@ -303,6 +383,24 @@ public:
|
||||
bool
|
||||
get_parameter(const std::string & name, ParameterT & parameter) const;
|
||||
|
||||
/// Assign the value of the map parameter if set into the values argument.
|
||||
/**
|
||||
* Parameter names that are part of a map are of the form "name.member".
|
||||
* This API gets all parameters that begin with "name", storing them into the
|
||||
* map with the name of the parameter and their value.
|
||||
* If there are no members in the named map, then the "values" argument is not changed.
|
||||
*
|
||||
* \param[in] name The prefix of the parameters to get.
|
||||
* \param[out] values The map of output values, with one std::string,MapValueT
|
||||
* per parameter.
|
||||
* \returns true if values was changed, false otherwise
|
||||
*/
|
||||
template<typename MapValueT>
|
||||
bool
|
||||
get_parameters(
|
||||
const std::string & name,
|
||||
std::map<std::string, MapValueT> & values) const;
|
||||
|
||||
/// Get the parameter value, or the "alternative value" if not set, and assign it to "value".
|
||||
/**
|
||||
* If the parameter was not set, then the "value" argument is assigned
|
||||
@@ -427,6 +525,11 @@ public:
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr
|
||||
get_node_graph_interface();
|
||||
|
||||
/// Return the Node's internal NodeLoggingInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr
|
||||
get_node_logging_interface();
|
||||
|
||||
/// Return the Node's internal NodeTimersInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
|
||||
@@ -442,11 +545,137 @@ public:
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr
|
||||
get_node_services_interface();
|
||||
|
||||
/// Return the Node's internal NodeWaitablesInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr
|
||||
get_node_waitables_interface();
|
||||
|
||||
/// Return the Node's internal NodeParametersInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr
|
||||
get_node_parameters_interface();
|
||||
|
||||
/// Return the Node's internal NodeParametersInterface implementation.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr
|
||||
get_node_time_source_interface();
|
||||
|
||||
/// Return the sub-namespace, if this is a sub-node, otherwise an empty string.
|
||||
/**
|
||||
* The returned sub-namespace is either the accumulated sub-namespaces which
|
||||
* were given to one-to-many create_sub_node() calls, or an empty string if
|
||||
* this is an original node instance, i.e. not a sub-node.
|
||||
*
|
||||
* For example, consider:
|
||||
*
|
||||
* auto node = std::make_shared<rclcpp::Node>("my_node", "my_ns");
|
||||
* node->get_sub_namespace(); // -> ""
|
||||
* auto sub_node1 = node->create_sub_node("a");
|
||||
* sub_node1->get_sub_namespace(); // -> "a"
|
||||
* auto sub_node2 = sub_node1->create_sub_node("b");
|
||||
* sub_node2->get_sub_namespace(); // -> "a/b"
|
||||
* auto sub_node3 = node->create_sub_node("foo");
|
||||
* sub_node3->get_sub_namespace(); // -> "foo"
|
||||
* node->get_sub_namespace(); // -> ""
|
||||
*
|
||||
* get_namespace() will return the original node namespace, and will not
|
||||
* include the sub-namespace if one exists.
|
||||
* To get that you need to call the get_effective_namespace() method.
|
||||
*
|
||||
* \sa get_namespace()
|
||||
* \sa get_effective_namespace()
|
||||
* \return the sub-namespace string, not including the node's original namespace
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const std::string &
|
||||
get_sub_namespace() const;
|
||||
|
||||
/// Return the effective namespace that is used when creating entities.
|
||||
/**
|
||||
* The returned namespace is a concatenation of the node namespace and the
|
||||
* accumulated sub-namespaces, which is used as the namespace when creating
|
||||
* entities which have relative names.
|
||||
*
|
||||
* For example, consider:
|
||||
*
|
||||
* auto node = std::make_shared<rclcpp::Node>("my_node", "my_ns");
|
||||
* node->get_effective_namespace(); // -> "/my_ns"
|
||||
* auto sub_node1 = node->create_sub_node("a");
|
||||
* sub_node1->get_effective_namespace(); // -> "/my_ns/a"
|
||||
* auto sub_node2 = sub_node1->create_sub_node("b");
|
||||
* sub_node2->get_effective_namespace(); // -> "/my_ns/a/b"
|
||||
* auto sub_node3 = node->create_sub_node("foo");
|
||||
* sub_node3->get_effective_namespace(); // -> "/my_ns/foo"
|
||||
* node->get_effective_namespace(); // -> "/my_ns"
|
||||
*
|
||||
* \sa get_namespace()
|
||||
* \sa get_sub_namespace()
|
||||
* \return the sub-namespace string, not including the node's original namespace
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const std::string &
|
||||
get_effective_namespace() const;
|
||||
|
||||
/// Create a sub-node, which will extend the namespace of all entities created with it.
|
||||
/**
|
||||
* A sub-node (short for subordinate node) is an instance of this class
|
||||
* which has been created using an existing instance of this class, but which
|
||||
* has an additional sub-namespace (short for subordinate namespace)
|
||||
* associated with it.
|
||||
* The sub-namespace will extend the node's namespace for the purpose of
|
||||
* creating additional entities, such as Publishers, Subscriptions, Service
|
||||
* Clients and Servers, and so on.
|
||||
*
|
||||
* By default, when an instance of this class is created using one of the
|
||||
* public constructors, it has no sub-namespace associated with it, and
|
||||
* therefore is not a sub-node.
|
||||
* That "normal" node instance may, however, be used to create further
|
||||
* instances of this class, based on the original instance, which have an
|
||||
* additional sub-namespace associated with them.
|
||||
* This may be done by using this method, create_sub_node().
|
||||
*
|
||||
* Furthermore, a sub-node may be used to create additional sub-node's, in
|
||||
* which case the sub-namespace passed to this function will further
|
||||
* extend the sub-namespace of the existing sub-node.
|
||||
* See get_sub_namespace() and get_effective_namespace() for examples.
|
||||
*
|
||||
* Note that entities which use absolute names are not affected by any
|
||||
* namespaces, neither the normal node namespace nor any sub-namespace.
|
||||
* Note also that the fully qualified node name is unaffected by a
|
||||
* sub-namespace.
|
||||
*
|
||||
* The sub-namespace should be relative, and an exception will be thrown if
|
||||
* the sub-namespace is absolute, i.e. if it starts with a leading '/'.
|
||||
*
|
||||
* \sa get_sub_namespace()
|
||||
* \sa get_effective_namespace()
|
||||
* \param[in] sub_namespace sub-namespace of the sub-node.
|
||||
* \return newly created sub-node
|
||||
* \throws NameValidationError if the sub-namespace is absolute, i.e. starts
|
||||
* with a leading '/'.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
Node::SharedPtr
|
||||
create_sub_node(const std::string & sub_namespace);
|
||||
|
||||
/// Return the NodeOptions used when creating this node.
|
||||
RCLCPP_PUBLIC
|
||||
const NodeOptions &
|
||||
get_node_options() const;
|
||||
|
||||
protected:
|
||||
/// Construct a sub-node, which will extend the namespace of all entities created with it.
|
||||
/**
|
||||
* \sa create_sub_node()
|
||||
*
|
||||
* \param[in] other The node from which a new sub-node is created.
|
||||
* \param[in] sub_namespace The sub-namespace of the sub-node.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
Node(
|
||||
const Node & other,
|
||||
const std::string & sub_namespace);
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(Node)
|
||||
|
||||
@@ -460,10 +689,14 @@ private:
|
||||
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_;
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr node_time_source_;
|
||||
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr node_waitables_;
|
||||
|
||||
bool use_intra_process_comms_;
|
||||
const NodeOptions node_options_;
|
||||
const std::string sub_namespace_;
|
||||
const std::string effective_namespace_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -51,34 +51,140 @@
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
RCLCPP_LOCAL
|
||||
inline
|
||||
std::string
|
||||
extend_name_with_sub_namespace(const std::string & name, const std::string & sub_namespace)
|
||||
{
|
||||
std::string name_with_sub_namespace(name);
|
||||
if (sub_namespace != "" && name.front() != '/' && name.front() != '~') {
|
||||
name_with_sub_namespace = sub_namespace + "/" + name;
|
||||
}
|
||||
return name_with_sub_namespace;
|
||||
}
|
||||
|
||||
template<typename MessageT, typename AllocatorT, typename PublisherT>
|
||||
std::shared_ptr<PublisherT>
|
||||
Node::create_publisher(
|
||||
const std::string & topic_name,
|
||||
size_t qos_history_depth,
|
||||
const PublisherOptionsWithAllocator<AllocatorT> & options)
|
||||
{
|
||||
std::shared_ptr<AllocatorT> allocator = options.allocator;
|
||||
if (!allocator) {
|
||||
allocator = std::make_shared<AllocatorT>();
|
||||
}
|
||||
rmw_qos_profile_t qos_profile = options.qos_profile;
|
||||
qos_profile.depth = qos_history_depth;
|
||||
|
||||
bool use_intra_process;
|
||||
switch (options.use_intra_process_comm) {
|
||||
case IntraProcessSetting::Enable:
|
||||
use_intra_process = true;
|
||||
break;
|
||||
case IntraProcessSetting::Disable:
|
||||
use_intra_process = false;
|
||||
break;
|
||||
case IntraProcessSetting::NodeDefault:
|
||||
use_intra_process = this->get_node_options().use_intra_process_comms();
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unrecognized IntraProcessSetting value");
|
||||
break;
|
||||
}
|
||||
|
||||
return rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
|
||||
this->node_topics_.get(),
|
||||
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
|
||||
qos_profile,
|
||||
use_intra_process,
|
||||
allocator);
|
||||
}
|
||||
|
||||
template<typename MessageT, typename Alloc, typename PublisherT>
|
||||
std::shared_ptr<PublisherT>
|
||||
Node::create_publisher(
|
||||
const std::string & topic_name, size_t qos_history_depth,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
std::shared_ptr<Alloc> allocator,
|
||||
IntraProcessSetting use_intra_process_comm)
|
||||
{
|
||||
if (!allocator) {
|
||||
allocator = std::make_shared<Alloc>();
|
||||
}
|
||||
rmw_qos_profile_t qos = rmw_qos_profile_default;
|
||||
qos.depth = qos_history_depth;
|
||||
return this->create_publisher<MessageT, Alloc, PublisherT>(topic_name, qos, allocator);
|
||||
PublisherOptionsWithAllocator<Alloc> pub_options;
|
||||
pub_options.allocator = allocator;
|
||||
pub_options.use_intra_process_comm = use_intra_process_comm;
|
||||
return this->create_publisher<MessageT, Alloc, PublisherT>(
|
||||
topic_name, qos_history_depth, pub_options);
|
||||
}
|
||||
|
||||
template<typename MessageT, typename Alloc, typename PublisherT>
|
||||
std::shared_ptr<PublisherT>
|
||||
Node::create_publisher(
|
||||
const std::string & topic_name, const rmw_qos_profile_t & qos_profile,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
std::shared_ptr<Alloc> allocator, IntraProcessSetting use_intra_process_comm)
|
||||
{
|
||||
PublisherOptionsWithAllocator<Alloc> pub_options;
|
||||
pub_options.qos_profile = qos_profile;
|
||||
pub_options.allocator = allocator;
|
||||
pub_options.use_intra_process_comm = use_intra_process_comm;
|
||||
return this->create_publisher<MessageT, Alloc, PublisherT>(
|
||||
topic_name, qos_profile.depth, pub_options);
|
||||
}
|
||||
|
||||
template<
|
||||
typename MessageT,
|
||||
typename CallbackT,
|
||||
typename AllocatorT,
|
||||
typename SubscriptionT>
|
||||
std::shared_ptr<SubscriptionT>
|
||||
Node::create_subscription(
|
||||
const std::string & topic_name,
|
||||
CallbackT && callback,
|
||||
size_t qos_history_depth,
|
||||
const SubscriptionOptionsWithAllocator<AllocatorT> & options,
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, AllocatorT>::SharedPtr
|
||||
msg_mem_strat)
|
||||
{
|
||||
using CallbackMessageT = typename rclcpp::subscription_traits::has_message_type<CallbackT>::type;
|
||||
|
||||
std::shared_ptr<AllocatorT> allocator = options.allocator;
|
||||
if (!allocator) {
|
||||
allocator = std::make_shared<Alloc>();
|
||||
allocator = std::make_shared<AllocatorT>();
|
||||
}
|
||||
return rclcpp::create_publisher<MessageT, Alloc, PublisherT>(
|
||||
|
||||
rmw_qos_profile_t qos_profile = options.qos_profile;
|
||||
qos_profile.depth = qos_history_depth;
|
||||
|
||||
if (!msg_mem_strat) {
|
||||
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
|
||||
msg_mem_strat = MessageMemoryStrategy<CallbackMessageT, AllocatorT>::create_default();
|
||||
}
|
||||
|
||||
bool use_intra_process;
|
||||
switch (options.use_intra_process_comm) {
|
||||
case IntraProcessSetting::Enable:
|
||||
use_intra_process = true;
|
||||
break;
|
||||
case IntraProcessSetting::Disable:
|
||||
use_intra_process = false;
|
||||
break;
|
||||
case IntraProcessSetting::NodeDefault:
|
||||
use_intra_process = this->get_node_options().use_intra_process_comms();
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("Unrecognized IntraProcessSetting value");
|
||||
break;
|
||||
}
|
||||
|
||||
return rclcpp::create_subscription<
|
||||
MessageT, CallbackT, AllocatorT, CallbackMessageT, SubscriptionT>(
|
||||
this->node_topics_.get(),
|
||||
topic_name,
|
||||
extend_name_with_sub_namespace(topic_name, this->get_sub_namespace()),
|
||||
std::forward<CallbackT>(callback),
|
||||
qos_profile,
|
||||
use_intra_process_comms_,
|
||||
options.callback_group,
|
||||
options.ignore_local_publications,
|
||||
use_intra_process,
|
||||
msg_mem_strat,
|
||||
allocator);
|
||||
}
|
||||
|
||||
@@ -97,29 +203,18 @@ Node::create_subscription(
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
|
||||
msg_mem_strat,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
std::shared_ptr<Alloc> allocator,
|
||||
IntraProcessSetting use_intra_process_comm)
|
||||
{
|
||||
using CallbackMessageT = typename rclcpp::subscription_traits::has_message_type<CallbackT>::type;
|
||||
SubscriptionOptionsWithAllocator<Alloc> sub_options;
|
||||
sub_options.qos_profile = qos_profile;
|
||||
sub_options.callback_group = group;
|
||||
sub_options.ignore_local_publications = ignore_local_publications;
|
||||
sub_options.allocator = allocator;
|
||||
sub_options.use_intra_process_comm = use_intra_process_comm;
|
||||
|
||||
if (!allocator) {
|
||||
allocator = std::make_shared<Alloc>();
|
||||
}
|
||||
|
||||
if (!msg_mem_strat) {
|
||||
using rclcpp::message_memory_strategy::MessageMemoryStrategy;
|
||||
msg_mem_strat = MessageMemoryStrategy<CallbackMessageT, Alloc>::create_default();
|
||||
}
|
||||
|
||||
return rclcpp::create_subscription<MessageT, CallbackT, Alloc, CallbackMessageT, SubscriptionT>(
|
||||
this->node_topics_.get(),
|
||||
topic_name,
|
||||
std::forward<CallbackT>(callback),
|
||||
qos_profile,
|
||||
group,
|
||||
ignore_local_publications,
|
||||
use_intra_process_comms_,
|
||||
msg_mem_strat,
|
||||
allocator);
|
||||
return this->create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
|
||||
topic_name, std::forward<CallbackT>(callback), qos_profile.depth, sub_options, msg_mem_strat);
|
||||
}
|
||||
|
||||
template<
|
||||
@@ -137,30 +232,30 @@ Node::create_subscription(
|
||||
typename rclcpp::message_memory_strategy::MessageMemoryStrategy<
|
||||
typename rclcpp::subscription_traits::has_message_type<CallbackT>::type, Alloc>::SharedPtr
|
||||
msg_mem_strat,
|
||||
std::shared_ptr<Alloc> allocator)
|
||||
std::shared_ptr<Alloc> allocator,
|
||||
IntraProcessSetting use_intra_process_comm)
|
||||
{
|
||||
rmw_qos_profile_t qos = rmw_qos_profile_default;
|
||||
qos.depth = qos_history_depth;
|
||||
return this->create_subscription<MessageT>(
|
||||
topic_name,
|
||||
std::forward<CallbackT>(callback),
|
||||
qos,
|
||||
group,
|
||||
ignore_local_publications,
|
||||
msg_mem_strat,
|
||||
allocator);
|
||||
SubscriptionOptionsWithAllocator<Alloc> sub_options;
|
||||
sub_options.callback_group = group;
|
||||
sub_options.ignore_local_publications = ignore_local_publications;
|
||||
sub_options.allocator = allocator;
|
||||
sub_options.use_intra_process_comm = use_intra_process_comm;
|
||||
|
||||
return this->create_subscription<MessageT, CallbackT, Alloc, SubscriptionT>(
|
||||
topic_name, std::forward<CallbackT>(callback), qos_history_depth, sub_options, msg_mem_strat);
|
||||
}
|
||||
|
||||
template<typename DurationT, typename CallbackT>
|
||||
template<typename DurationRepT, typename DurationT, typename CallbackT>
|
||||
typename rclcpp::WallTimer<CallbackT>::SharedPtr
|
||||
Node::create_wall_timer(
|
||||
std::chrono::duration<int64_t, DurationT> period,
|
||||
std::chrono::duration<DurationRepT, DurationT> period,
|
||||
CallbackT callback,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
auto timer = rclcpp::WallTimer<CallbackT>::make_shared(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(period),
|
||||
std::move(callback));
|
||||
std::move(callback),
|
||||
this->node_base_->get_context());
|
||||
node_timers_->add_timer(timer, group);
|
||||
return timer;
|
||||
}
|
||||
@@ -181,7 +276,7 @@ Node::create_client(
|
||||
auto cli = Client<ServiceT>::make_shared(
|
||||
node_base_.get(),
|
||||
node_graph_,
|
||||
service_name,
|
||||
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
|
||||
options);
|
||||
|
||||
auto cli_base_ptr = std::dynamic_pointer_cast<ClientBase>(cli);
|
||||
@@ -198,8 +293,12 @@ Node::create_service(
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
return rclcpp::create_service<ServiceT, CallbackT>(
|
||||
node_base_, node_services_,
|
||||
service_name, std::forward<CallbackT>(callback), qos_profile, group);
|
||||
node_base_,
|
||||
node_services_,
|
||||
extend_name_with_sub_namespace(service_name, this->get_sub_namespace()),
|
||||
std::forward<CallbackT>(callback),
|
||||
qos_profile,
|
||||
group);
|
||||
}
|
||||
|
||||
template<typename CallbackT>
|
||||
@@ -215,20 +314,48 @@ Node::set_parameter_if_not_set(
|
||||
const std::string & name,
|
||||
const ParameterT & value)
|
||||
{
|
||||
std::string parameter_name_with_sub_namespace =
|
||||
extend_name_with_sub_namespace(name, this->get_sub_namespace());
|
||||
|
||||
rclcpp::Parameter parameter;
|
||||
if (!this->get_parameter(name, parameter)) {
|
||||
if (!this->get_parameter(parameter_name_with_sub_namespace, parameter)) {
|
||||
this->set_parameters({
|
||||
rclcpp::Parameter(name, value),
|
||||
rclcpp::Parameter(parameter_name_with_sub_namespace, value),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// this is a partially-specialized version of set_parameter_if_not_set above,
|
||||
// where our concrete type for ParameterT is std::map, but the to-be-determined
|
||||
// type is the value in the map.
|
||||
template<typename MapValueT>
|
||||
void
|
||||
Node::set_parameters_if_not_set(
|
||||
const std::string & name,
|
||||
const std::map<std::string, MapValueT> & values)
|
||||
{
|
||||
std::vector<rclcpp::Parameter> params;
|
||||
|
||||
for (const auto & val : values) {
|
||||
std::string param_name = name + "." + val.first;
|
||||
rclcpp::Parameter parameter;
|
||||
if (!this->get_parameter(param_name, parameter)) {
|
||||
params.push_back(rclcpp::Parameter(param_name, val.second));
|
||||
}
|
||||
}
|
||||
|
||||
this->set_parameters(params);
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
bool
|
||||
Node::get_parameter(const std::string & name, ParameterT & value) const
|
||||
{
|
||||
std::string sub_name = extend_name_with_sub_namespace(name, this->get_sub_namespace());
|
||||
|
||||
rclcpp::Parameter parameter;
|
||||
bool result = get_parameter(name, parameter);
|
||||
|
||||
bool result = get_parameter(sub_name, parameter);
|
||||
if (result) {
|
||||
value = parameter.get_value<ParameterT>();
|
||||
}
|
||||
@@ -236,6 +363,26 @@ Node::get_parameter(const std::string & name, ParameterT & value) const
|
||||
return result;
|
||||
}
|
||||
|
||||
// this is a partially-specialized version of get_parameter above,
|
||||
// where our concrete type for ParameterT is std::map, but the to-be-determined
|
||||
// type is the value in the map.
|
||||
template<typename MapValueT>
|
||||
bool
|
||||
Node::get_parameters(
|
||||
const std::string & name,
|
||||
std::map<std::string, MapValueT> & values) const
|
||||
{
|
||||
std::map<std::string, rclcpp::Parameter> params;
|
||||
bool result = node_parameters_->get_parameters_by_prefix(name, params);
|
||||
if (result) {
|
||||
for (const auto & param : params) {
|
||||
values[param.first] = param.second.get_value<MapValueT>();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename ParameterT>
|
||||
bool
|
||||
Node::get_parameter_or(
|
||||
@@ -243,7 +390,9 @@ Node::get_parameter_or(
|
||||
ParameterT & value,
|
||||
const ParameterT & alternative_value) const
|
||||
{
|
||||
bool got_parameter = get_parameter(name, value);
|
||||
std::string sub_name = extend_name_with_sub_namespace(name, this->get_sub_namespace());
|
||||
|
||||
bool got_parameter = get_parameter(sub_name, value);
|
||||
if (!got_parameter) {
|
||||
value = alternative_value;
|
||||
}
|
||||
@@ -257,10 +406,12 @@ Node::get_parameter_or_set(
|
||||
ParameterT & value,
|
||||
const ParameterT & alternative_value)
|
||||
{
|
||||
bool got_parameter = get_parameter(name, value);
|
||||
std::string sub_name = extend_name_with_sub_namespace(name, this->get_sub_namespace());
|
||||
|
||||
bool got_parameter = get_parameter(sub_name, value);
|
||||
if (!got_parameter) {
|
||||
this->set_parameters({
|
||||
rclcpp::Parameter(name, alternative_value),
|
||||
rclcpp::Parameter(sub_name, alternative_value),
|
||||
});
|
||||
value = alternative_value;
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_options.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
@@ -39,9 +40,7 @@ public:
|
||||
NodeBase(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::vector<std::string> & arguments,
|
||||
bool use_global_arguments);
|
||||
const NodeOptions & options);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -57,6 +56,11 @@ public:
|
||||
const char *
|
||||
get_namespace() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
const char *
|
||||
get_fully_qualified_name() const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rclcpp::Context::SharedPtr
|
||||
|
||||
@@ -38,6 +38,10 @@ class NodeBaseInterface
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeBaseInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeBaseInterface() = default;
|
||||
|
||||
/// Return the name of the node.
|
||||
/** \return The name of the node. */
|
||||
RCLCPP_PUBLIC
|
||||
@@ -52,6 +56,13 @@ public:
|
||||
const char *
|
||||
get_namespace() const = 0;
|
||||
|
||||
/// Return the fully qualified name of the node.
|
||||
/** \return The fully qualified name of the node. */
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
const char *
|
||||
get_fully_qualified_name() const = 0;
|
||||
|
||||
/// Return the context of the node.
|
||||
/** \return SharedPtr to the node's context. */
|
||||
RCLCPP_PUBLIC
|
||||
|
||||
@@ -41,7 +41,8 @@ public:
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services);
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -60,9 +61,9 @@ private:
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
|
||||
|
||||
rclcpp::Clock::SharedPtr ros_clock_;
|
||||
rclcpp::TimeSource time_source_;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -31,6 +31,10 @@ class NodeClockInterface
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeClockInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeClockInterface() = default;
|
||||
|
||||
/// Get a ROS clock which will be kept up to date by the node.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
|
||||
@@ -38,6 +38,10 @@ class NodeGraphInterface
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeGraphInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeGraphInterface() = default;
|
||||
|
||||
/// 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
|
||||
|
||||
@@ -33,6 +33,10 @@ class NodeLoggingInterface
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeLoggingInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeLoggingInterface() = default;
|
||||
|
||||
/// Return the logger of the node.
|
||||
/** \return The logger of the node. */
|
||||
RCLCPP_PUBLIC
|
||||
|
||||
@@ -51,9 +51,12 @@ public:
|
||||
const node_interfaces::NodeBaseInterface::SharedPtr node_base,
|
||||
const node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
|
||||
const node_interfaces::NodeServicesInterface::SharedPtr node_services,
|
||||
const node_interfaces::NodeClockInterface::SharedPtr node_clock,
|
||||
const std::vector<Parameter> & initial_parameters,
|
||||
bool use_intra_process,
|
||||
bool start_parameter_services);
|
||||
bool start_parameter_services,
|
||||
bool start_parameter_event_publisher,
|
||||
const rmw_qos_profile_t & parameter_event_qos_profile);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
@@ -88,6 +91,13 @@ public:
|
||||
const std::string & name,
|
||||
rclcpp::Parameter & parameter) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
get_parameters_by_prefix(
|
||||
const std::string & prefix,
|
||||
std::map<std::string, rclcpp::Parameter> & parameters) const;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor>
|
||||
@@ -120,6 +130,10 @@ private:
|
||||
Publisher<rcl_interfaces::msg::ParameterEvent>::SharedPtr events_publisher_;
|
||||
|
||||
std::shared_ptr<ParameterService> parameter_service_;
|
||||
|
||||
std::string combined_name_;
|
||||
|
||||
node_interfaces::NodeClockInterface::SharedPtr node_clock_;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#ifndef RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_PARAMETERS_INTERFACE_HPP_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -37,6 +38,10 @@ class NodeParametersInterface
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeParametersInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeParametersInterface() = default;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult>
|
||||
@@ -85,6 +90,20 @@ public:
|
||||
const std::string & name,
|
||||
rclcpp::Parameter & parameter) const = 0;
|
||||
|
||||
/// Get all parameters that have the specified prefix into the parameters map.
|
||||
/*
|
||||
* \param[in] prefix the name of the prefix to look for.
|
||||
* \param[out] parameters a map of parameters that matched the prefix.
|
||||
* \return true if any parameters with the prefix exists on the node, or
|
||||
* \return false otherwise.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
get_parameters_by_prefix(
|
||||
const std::string & prefix,
|
||||
std::map<std::string, rclcpp::Parameter> & parameters) const = 0;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor>
|
||||
|
||||
@@ -32,6 +32,10 @@ class NodeServicesInterface
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeServicesInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeServicesInterface() = default;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
|
||||
72
rclcpp/include/rclcpp/node_interfaces/node_time_source.hpp
Normal file
72
rclcpp/include/rclcpp/node_interfaces/node_time_source.hpp
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright 2018 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_TIME_SOURCE_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_HPP_
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/clock.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node_interfaces/node_base_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_clock_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_parameters_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_time_source_interface.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics_interface.hpp"
|
||||
#include "rclcpp/time_source.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
/// Implementation of the NodeTimeSource part of the Node API.
|
||||
class NodeTimeSource : public NodeTimeSourceInterface
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTimeSource)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit NodeTimeSource(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters
|
||||
);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeTimeSource();
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeTimeSource)
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_;
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
|
||||
|
||||
rclcpp::TimeSource time_source_;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_HPP_
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2018 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_TIME_SOURCE_INTERFACE_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_INTERFACE_HPP_
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/clock.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
/// Pure virtual interface class for the NodeTimeSource part of the Node API.
|
||||
class NodeTimeSourceInterface
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTimeSourceInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeTimeSourceInterface() = default;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_TIME_SOURCE_INTERFACE_HPP_
|
||||
@@ -31,6 +31,10 @@ class NodeTimersInterface
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTimersInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeTimersInterface() = default;
|
||||
|
||||
/// Add a timer to the node.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
|
||||
@@ -40,6 +40,10 @@ class NodeTopicsInterface
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeTopicsInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeTopicsInterface() = default;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
rclcpp::PublisherBase::SharedPtr
|
||||
|
||||
67
rclcpp/include/rclcpp/node_interfaces/node_waitables.hpp
Normal file
67
rclcpp/include/rclcpp/node_interfaces/node_waitables.hpp
Normal file
@@ -0,0 +1,67 @@
|
||||
// Copyright 2018 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_WAITABLES_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_WAITABLES_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_waitables_interface.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
/// Implementation of the NodeWaitables part of the Node API.
|
||||
class NodeWaitables : public NodeWaitablesInterface
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeWaitables)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit NodeWaitables(rclcpp::node_interfaces::NodeBaseInterface * node_base);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeWaitables();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
add_waitable(
|
||||
rclcpp::Waitable::SharedPtr waitable_base_ptr,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
remove_waitable(
|
||||
rclcpp::Waitable::SharedPtr waitable_ptr,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group) noexcept;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(NodeWaitables)
|
||||
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base_;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_WAITABLES_HPP_
|
||||
@@ -0,0 +1,57 @@
|
||||
// Copyright 2018 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_WAITABLES_INTERFACE_HPP_
|
||||
#define RCLCPP__NODE_INTERFACES__NODE_WAITABLES_INTERFACE_HPP_
|
||||
|
||||
#include "rclcpp/callback_group.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
namespace node_interfaces
|
||||
{
|
||||
|
||||
/// Pure virtual interface class for the NodeWaitables part of the Node API.
|
||||
class NodeWaitablesInterface
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_ALIASES_ONLY(NodeWaitablesInterface)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeWaitablesInterface() = default;
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
add_waitable(
|
||||
rclcpp::Waitable::SharedPtr waitable_ptr,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group) = 0;
|
||||
|
||||
/// \note this function should not throw because it may be called in destructors
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
remove_waitable(
|
||||
rclcpp::Waitable::SharedPtr waitable_ptr,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group) noexcept = 0;
|
||||
};
|
||||
|
||||
} // namespace node_interfaces
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_INTERFACES__NODE_WAITABLES_INTERFACE_HPP_
|
||||
264
rclcpp/include/rclcpp/node_options.hpp
Normal file
264
rclcpp/include/rclcpp/node_options.hpp
Normal file
@@ -0,0 +1,264 @@
|
||||
// Copyright 2019 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__NODE_OPTIONS_HPP_
|
||||
#define RCLCPP__NODE_OPTIONS_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/node_options.h"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/parameter.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Encapsulation of options for node initialization.
|
||||
class NodeOptions
|
||||
{
|
||||
public:
|
||||
/// Create NodeOptions with default values, optionally specifying the allocator to use.
|
||||
/**
|
||||
* Default values for the node options:
|
||||
*
|
||||
* - context = rclcpp::contexts::default_context::get_global_default_context()
|
||||
* - arguments = {}
|
||||
* - initial_parameters = {}
|
||||
* - use_global_arguments = true
|
||||
* - use_intra_process_comms = false
|
||||
* - start_parameter_services = true
|
||||
* - start_parameter_event_publisher = true
|
||||
* - parameter_event_qos_profile = rmw_qos_profile_parameter_events
|
||||
* - allocator = rcl_get_default_allocator()
|
||||
*
|
||||
* \param[in] allocator allocator to use in construction of NodeOptions.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
explicit NodeOptions(rcl_allocator_t allocator = rcl_get_default_allocator());
|
||||
|
||||
/// Destructor.
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
~NodeOptions() = default;
|
||||
|
||||
/// Copy constructor.
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions(const NodeOptions & other);
|
||||
|
||||
/// Assignment operator.
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
operator=(const NodeOptions & other);
|
||||
|
||||
/// Return the rcl_node_options used by the node.
|
||||
/**
|
||||
* This data structure is created lazily, on the first call to this function.
|
||||
* Repeated calls will not regenerate it unless one of the input settings
|
||||
* changed, like arguments, use_global_arguments, or the rcl allocator.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_node_options_t *
|
||||
get_rcl_node_options() const;
|
||||
|
||||
/// Return the context to be used by the node.
|
||||
RCLCPP_PUBLIC
|
||||
rclcpp::Context::SharedPtr
|
||||
context() const;
|
||||
|
||||
/// Set the context, return this for parameter idiom.
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
context(rclcpp::Context::SharedPtr context);
|
||||
|
||||
/// Return a reference to the list of arguments for the node.
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<std::string> &
|
||||
arguments() const;
|
||||
|
||||
/// Set the arguments, return this for parameter idiom.
|
||||
/**
|
||||
* These arguments are used to extract remappings used by the node and other
|
||||
* settings.
|
||||
*
|
||||
* This will cause the internal rcl_node_options_t struct to be invalidated.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
arguments(const std::vector<std::string> & arguments);
|
||||
|
||||
/// Return a reference to the list of initial parameters.
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::Parameter> &
|
||||
initial_parameters();
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
const std::vector<rclcpp::Parameter> &
|
||||
initial_parameters() const;
|
||||
|
||||
/// Set the initial parameters, return this for parameter idiom.
|
||||
/**
|
||||
* These initial parameter values are used to change the initial value
|
||||
* of declared parameters within the node, overriding hard coded default
|
||||
* values if necessary.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
initial_parameters(const std::vector<rclcpp::Parameter> & initial_parameters);
|
||||
|
||||
/// Append a single initial parameter, parameter idiom style.
|
||||
template<typename ParameterT>
|
||||
NodeOptions &
|
||||
append_initial_parameter(const std::string & name, const ParameterT & value)
|
||||
{
|
||||
this->initial_parameters().emplace_back(name, rclcpp::ParameterValue(value));
|
||||
return *this;
|
||||
}
|
||||
|
||||
/// Return a reference to the use_global_arguments flag.
|
||||
RCLCPP_PUBLIC
|
||||
const bool &
|
||||
use_global_arguments() const;
|
||||
|
||||
/// Set the use_global_arguments flag, return this for parameter idiom.
|
||||
/**
|
||||
* If true this will cause the node's behavior to be influenced by "global"
|
||||
* arguments, i.e. arguments not targeted at specific nodes, as well as the
|
||||
* arguments targeted at the current node.
|
||||
*
|
||||
* This will cause the internal rcl_node_options_t struct to be invalidated.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
use_global_arguments(const bool & use_global_arguments);
|
||||
|
||||
/// Return a reference to the use_intra_process_comms flag
|
||||
RCLCPP_PUBLIC
|
||||
const bool &
|
||||
use_intra_process_comms() const;
|
||||
|
||||
/// Set the use_intra_process_comms flag, return this for parameter idiom.
|
||||
/**
|
||||
* If true, messages on topics which are published and subscribed to within
|
||||
* this context will go through a special intra-process communication code
|
||||
* code path which can avoid serialization and deserialization, unnecessary
|
||||
* copies, and achieve lower latencies in some cases.
|
||||
*
|
||||
* Defaults to false for now, as there are still some cases where it is not
|
||||
* desirable.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
use_intra_process_comms(const bool & use_intra_process_comms);
|
||||
|
||||
/// Return a reference to the start_parameter_services flag
|
||||
RCLCPP_PUBLIC
|
||||
const bool &
|
||||
start_parameter_services() const;
|
||||
|
||||
/// Set the start_parameter_services flag, return this for parameter idiom.
|
||||
/**
|
||||
* If true, ROS services are created to allow external nodes to list, get,
|
||||
* and request to set parameters of this node.
|
||||
*
|
||||
* If false, parameters will still work locally, but will not be accessible
|
||||
* remotely.
|
||||
*
|
||||
* \sa start_parameter_event_publisher()
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
start_parameter_services(const bool & start_parameter_services);
|
||||
|
||||
/// Return a reference to the start_parameter_event_publisher flag.
|
||||
RCLCPP_PUBLIC
|
||||
const bool &
|
||||
start_parameter_event_publisher() const;
|
||||
|
||||
/// Set the start_parameter_event_publisher flag, return this for parameter idiom.
|
||||
/**
|
||||
* If true, a publisher is created on which an event message is published
|
||||
* each time a parameter's state changes.
|
||||
* This is used for recording and introspection, but is configurable
|
||||
* separately from the other parameter services.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
start_parameter_event_publisher(const bool & start_parameter_event_publisher);
|
||||
|
||||
/// Return a reference to the parameter_event_qos_profile QoS.
|
||||
RCLCPP_PUBLIC
|
||||
const rmw_qos_profile_t &
|
||||
parameter_event_qos_profile() const;
|
||||
|
||||
/// Set the parameter_event_qos_profile QoS, return this for parameter idiom.
|
||||
/**
|
||||
* The QoS settings to be used for the parameter event publisher, if enabled.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
parameter_event_qos_profile(const rmw_qos_profile_t & parameter_event_qos_profile);
|
||||
|
||||
/// Return the rcl_allocator_t to be used.
|
||||
RCLCPP_PUBLIC
|
||||
const rcl_allocator_t &
|
||||
allocator() const;
|
||||
|
||||
/// Set the rcl_allocator_t to be used, may cause deallocation of existing rcl_node_options_t.
|
||||
/**
|
||||
* This will cause the internal rcl_node_options_t struct to be invalidated.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
NodeOptions &
|
||||
allocator(rcl_allocator_t allocator);
|
||||
|
||||
protected:
|
||||
/// Retrieve the ROS_DOMAIN_ID environment variable and populate options.
|
||||
size_t
|
||||
get_domain_id_from_env() const;
|
||||
|
||||
private:
|
||||
// This is mutable to allow for a const accessor which lazily creates the node options instance.
|
||||
/// Underlying rcl_node_options structure.
|
||||
mutable std::unique_ptr<rcl_node_options_t, void (*)(rcl_node_options_t *)> node_options_;
|
||||
|
||||
// IMPORTANT: if any of these default values are changed, please update the
|
||||
// documentation in this class.
|
||||
|
||||
rclcpp::Context::SharedPtr context_ {
|
||||
rclcpp::contexts::default_context::get_global_default_context()};
|
||||
|
||||
std::vector<std::string> arguments_ {};
|
||||
|
||||
std::vector<rclcpp::Parameter> initial_parameters_ {};
|
||||
|
||||
bool use_global_arguments_ {true};
|
||||
|
||||
bool use_intra_process_comms_ {false};
|
||||
|
||||
bool start_parameter_services_ {true};
|
||||
|
||||
bool start_parameter_event_publisher_ {true};
|
||||
|
||||
rmw_qos_profile_t parameter_event_qos_profile_ {rmw_qos_profile_parameter_events};
|
||||
|
||||
rcl_allocator_t allocator_ {rcl_get_default_allocator()};
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__NODE_OPTIONS_HPP_
|
||||
@@ -138,10 +138,10 @@ public:
|
||||
bool
|
||||
service_is_ready() const;
|
||||
|
||||
template<typename RatioT = std::milli>
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
bool
|
||||
wait_for_service(
|
||||
std::chrono::duration<int64_t, RatioT> timeout = std::chrono::duration<int64_t, RatioT>(-1))
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return wait_for_service_nanoseconds(
|
||||
std::chrono::duration_cast<std::chrono::nanoseconds>(timeout)
|
||||
@@ -185,6 +185,29 @@ public:
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit SyncParametersClient(
|
||||
rclcpp::Node * node,
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
SyncParametersClient(
|
||||
rclcpp::executor::Executor::SharedPtr executor,
|
||||
rclcpp::Node * node,
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
SyncParametersClient(
|
||||
rclcpp::executor::Executor::SharedPtr executor,
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
|
||||
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
|
||||
const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
|
||||
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
|
||||
const std::string & remote_node_name = "",
|
||||
const rmw_qos_profile_t & qos_profile = rmw_qos_profile_parameters);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<rclcpp::Parameter>
|
||||
get_parameters(const std::vector<std::string> & parameter_names);
|
||||
@@ -258,17 +281,17 @@ public:
|
||||
return async_parameters_client_->service_is_ready();
|
||||
}
|
||||
|
||||
template<typename RatioT = std::milli>
|
||||
template<typename RepT = int64_t, typename RatioT = std::milli>
|
||||
bool
|
||||
wait_for_service(
|
||||
std::chrono::duration<int64_t, RatioT> timeout = std::chrono::duration<int64_t, RatioT>(-1))
|
||||
std::chrono::duration<RepT, RatioT> timeout = std::chrono::duration<RepT, RatioT>(-1))
|
||||
{
|
||||
return async_parameters_client_->wait_for_service(timeout);
|
||||
}
|
||||
|
||||
private:
|
||||
rclcpp::executor::Executor::SharedPtr executor_;
|
||||
rclcpp::Node::SharedPtr node_;
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface_;
|
||||
AsyncParametersClient::SharedPtr async_parameters_client_;
|
||||
};
|
||||
|
||||
|
||||
@@ -45,6 +45,15 @@ namespace node_interfaces
|
||||
class NodeTopicsInterface;
|
||||
}
|
||||
|
||||
namespace intra_process_manager
|
||||
{
|
||||
/**
|
||||
* NOTE(ivanpauno): IntraProcessManager is forward declared here, avoiding a circular inclusion between intra_process_manager.hpp and publisher.hpp.
|
||||
* SharedPtr and WeakPtr of the IntraProcessManager are defined again here, to avoid a warning for accessing a member of a forward declared class.
|
||||
*/
|
||||
class IntraProcessManager;
|
||||
}
|
||||
|
||||
class PublisherBase
|
||||
{
|
||||
friend ::rclcpp::node_interfaces::NodeTopicsInterface;
|
||||
@@ -107,6 +116,32 @@ public:
|
||||
const rcl_publisher_t *
|
||||
get_publisher_handle() const;
|
||||
|
||||
/// Get subscription count
|
||||
/** \return The number of subscriptions. */
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_subscription_count() const;
|
||||
|
||||
/// Get intraprocess subscription count
|
||||
/** \return The number of intraprocess subscriptions. */
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_intra_process_subscription_count() const;
|
||||
|
||||
/// Get the actual QoS settings, after the defaults have been determined.
|
||||
/**
|
||||
* The actual configuration applied when using RMW_QOS_POLICY_*_SYSTEM_DEFAULT
|
||||
* can only be resolved after the creation of the publisher, and it
|
||||
* depends on the underlying rmw implementation.
|
||||
* If the underlying setting in use can't be represented in ROS terms,
|
||||
* it will be set to RMW_QOS_POLICY_*_UNKNOWN.
|
||||
* May throw runtime_error when an unexpected error occurs.
|
||||
* \return The actual qos settings.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rmw_qos_profile_t
|
||||
get_actual_qos() const;
|
||||
|
||||
/// Compare this publisher to a gid.
|
||||
/**
|
||||
* Note that this function calls the next function.
|
||||
@@ -128,13 +163,16 @@ public:
|
||||
operator==(const rmw_gid_t * gid) const;
|
||||
|
||||
using StoreMessageCallbackT = std::function<uint64_t(uint64_t, void *, const std::type_info &)>;
|
||||
using IntraProcessManagerSharedPtr =
|
||||
std::shared_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
|
||||
|
||||
/// Implementation utility function used to setup intra process publishing after creation.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
setup_intra_process(
|
||||
uint64_t intra_process_publisher_id,
|
||||
StoreMessageCallbackT callback,
|
||||
StoreMessageCallbackT store_callback,
|
||||
IntraProcessManagerSharedPtr ipm,
|
||||
const rcl_publisher_options_t & intra_process_options);
|
||||
|
||||
protected:
|
||||
@@ -143,6 +181,10 @@ protected:
|
||||
rcl_publisher_t publisher_handle_ = rcl_get_zero_initialized_publisher();
|
||||
rcl_publisher_t intra_process_publisher_handle_ = rcl_get_zero_initialized_publisher();
|
||||
|
||||
using IntraProcessManagerWeakPtr =
|
||||
std::weak_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
|
||||
bool intra_process_is_enabled_;
|
||||
IntraProcessManagerWeakPtr weak_ipm_;
|
||||
uint64_t intra_process_publisher_id_;
|
||||
StoreMessageCallbackT store_intra_process_message_;
|
||||
|
||||
@@ -188,7 +230,11 @@ public:
|
||||
virtual void
|
||||
publish(std::unique_ptr<MessageT, MessageDeleter> & msg)
|
||||
{
|
||||
this->do_inter_process_publish(msg.get());
|
||||
bool inter_process_subscriptions_exist =
|
||||
get_subscription_count() > get_intra_process_subscription_count();
|
||||
if (!intra_process_is_enabled_ || inter_process_subscriptions_exist) {
|
||||
this->do_inter_process_publish(msg.get());
|
||||
}
|
||||
if (store_intra_process_message_) {
|
||||
// Take the pointer from the unique_msg, release it and pass as a void *
|
||||
// to the ipm. The ipm should then capture it again as a unique_ptr of
|
||||
@@ -205,6 +251,16 @@ public:
|
||||
ipm.publisher_id = intra_process_publisher_id_;
|
||||
ipm.message_sequence = message_seq;
|
||||
auto status = rcl_publish(&intra_process_publisher_handle_, &ipm);
|
||||
if (RCL_RET_PUBLISHER_INVALID == status) {
|
||||
rcl_reset_error(); // next call will reset error message if not context
|
||||
if (rcl_publisher_is_valid_except_context(&intra_process_publisher_handle_)) {
|
||||
rcl_context_t * context = rcl_publisher_get_context(&intra_process_publisher_handle_);
|
||||
if (nullptr != context && !rcl_context_is_valid(context)) {
|
||||
// publisher is invalid due to context being shutdown
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (RCL_RET_OK != status) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish intra process message");
|
||||
}
|
||||
@@ -305,6 +361,16 @@ protected:
|
||||
do_inter_process_publish(const MessageT * msg)
|
||||
{
|
||||
auto status = rcl_publish(&publisher_handle_, msg);
|
||||
if (RCL_RET_PUBLISHER_INVALID == status) {
|
||||
rcl_reset_error(); // next call will reset error message if not context
|
||||
if (rcl_publisher_is_valid_except_context(&publisher_handle_)) {
|
||||
rcl_context_t * context = rcl_publisher_get_context(&publisher_handle_);
|
||||
if (nullptr != context && !rcl_context_is_valid(context)) {
|
||||
// publisher is invalid due to context being shutdown
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (RCL_RET_OK != status) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(status, "failed to publish message");
|
||||
}
|
||||
|
||||
44
rclcpp/include/rclcpp/publisher_options.hpp
Normal file
44
rclcpp/include/rclcpp/publisher_options.hpp
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2019 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__PUBLISHER_OPTIONS_HPP_
|
||||
#define RCLCPP__PUBLISHER_OPTIONS_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/intra_process_setting.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Structure containing optional configuration for Publishers.
|
||||
template<typename Allocator>
|
||||
struct PublisherOptionsWithAllocator
|
||||
{
|
||||
/// The quality of service profile to pass on to the rmw implementation.
|
||||
rmw_qos_profile_t qos_profile = rmw_qos_profile_default;
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
/// Setting to explicitly set intraprocess communications.
|
||||
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault;
|
||||
};
|
||||
|
||||
using PublisherOptions = PublisherOptionsWithAllocator<std::allocator<void>>;
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__PUBLISHER_OPTIONS_HPP_
|
||||
@@ -151,5 +151,6 @@
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
#include "rclcpp/waitable.hpp"
|
||||
|
||||
#endif // RCLCPP__RCLCPP_HPP_
|
||||
|
||||
@@ -119,7 +119,7 @@ public:
|
||||
if (handle) {
|
||||
if (rcl_service_fini(service, handle.get()) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger(rcl_node_get_logger_name(handle.get())).get_child("rclcpp"),
|
||||
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
|
||||
"Error in destruction of rcl service handle: %s",
|
||||
rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
|
||||
@@ -87,30 +87,42 @@ public:
|
||||
service_handles_.clear();
|
||||
client_handles_.clear();
|
||||
timer_handles_.clear();
|
||||
waitable_handles_.clear();
|
||||
}
|
||||
|
||||
virtual void remove_null_handles(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
for (size_t i = 0; i < wait_set->size_of_subscriptions; ++i) {
|
||||
// TODO(jacobperron): Check if wait set sizes are what we expect them to be?
|
||||
// e.g. wait_set->size_of_clients == client_handles_.size()
|
||||
|
||||
// Important to use subscription_handles_.size() instead of wait set's size since
|
||||
// there may be more subscriptions in the wait set due to Waitables added to the end.
|
||||
// The same logic applies for other entities.
|
||||
for (size_t i = 0; i < subscription_handles_.size(); ++i) {
|
||||
if (!wait_set->subscriptions[i]) {
|
||||
subscription_handles_[i].reset();
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < wait_set->size_of_services; ++i) {
|
||||
for (size_t i = 0; i < service_handles_.size(); ++i) {
|
||||
if (!wait_set->services[i]) {
|
||||
service_handles_[i].reset();
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < wait_set->size_of_clients; ++i) {
|
||||
for (size_t i = 0; i < client_handles_.size(); ++i) {
|
||||
if (!wait_set->clients[i]) {
|
||||
client_handles_[i].reset();
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < wait_set->size_of_timers; ++i) {
|
||||
for (size_t i = 0; i < timer_handles_.size(); ++i) {
|
||||
if (!wait_set->timers[i]) {
|
||||
timer_handles_[i].reset();
|
||||
}
|
||||
}
|
||||
for (size_t i = 0; i < waitable_handles_.size(); ++i) {
|
||||
if (!waitable_handles_[i]->is_ready(wait_set)) {
|
||||
waitable_handles_[i].reset();
|
||||
}
|
||||
}
|
||||
|
||||
subscription_handles_.erase(
|
||||
std::remove(subscription_handles_.begin(), subscription_handles_.end(), nullptr),
|
||||
@@ -131,6 +143,11 @@ public:
|
||||
std::remove(timer_handles_.begin(), timer_handles_.end(), nullptr),
|
||||
timer_handles_.end()
|
||||
);
|
||||
|
||||
waitable_handles_.erase(
|
||||
std::remove(waitable_handles_.begin(), waitable_handles_.end(), nullptr),
|
||||
waitable_handles_.end()
|
||||
);
|
||||
}
|
||||
|
||||
bool collect_entities(const WeakNodeVector & weak_nodes)
|
||||
@@ -175,6 +192,12 @@ public:
|
||||
timer_handles_.push_back(timer->get_timer_handle());
|
||||
}
|
||||
}
|
||||
for (auto & weak_waitable : group->get_waitable_ptrs()) {
|
||||
auto waitable = weak_waitable.lock();
|
||||
if (waitable) {
|
||||
waitable_handles_.push_back(waitable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return has_invalid_weak_nodes;
|
||||
@@ -183,7 +206,7 @@ public:
|
||||
bool add_handles_to_wait_set(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
for (auto subscription : subscription_handles_) {
|
||||
if (rcl_wait_set_add_subscription(wait_set, subscription.get()) != RCL_RET_OK) {
|
||||
if (rcl_wait_set_add_subscription(wait_set, subscription.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't add subscription to wait set: %s", rcl_get_error_string().str);
|
||||
@@ -192,7 +215,7 @@ public:
|
||||
}
|
||||
|
||||
for (auto client : client_handles_) {
|
||||
if (rcl_wait_set_add_client(wait_set, client.get()) != RCL_RET_OK) {
|
||||
if (rcl_wait_set_add_client(wait_set, client.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't add client to wait set: %s", rcl_get_error_string().str);
|
||||
@@ -201,7 +224,7 @@ public:
|
||||
}
|
||||
|
||||
for (auto service : service_handles_) {
|
||||
if (rcl_wait_set_add_service(wait_set, service.get()) != RCL_RET_OK) {
|
||||
if (rcl_wait_set_add_service(wait_set, service.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't add service to wait set: %s", rcl_get_error_string().str);
|
||||
@@ -210,7 +233,7 @@ public:
|
||||
}
|
||||
|
||||
for (auto timer : timer_handles_) {
|
||||
if (rcl_wait_set_add_timer(wait_set, timer.get()) != RCL_RET_OK) {
|
||||
if (rcl_wait_set_add_timer(wait_set, timer.get(), NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't add timer to wait set: %s", rcl_get_error_string().str);
|
||||
@@ -219,7 +242,7 @@ public:
|
||||
}
|
||||
|
||||
for (auto guard_condition : guard_conditions_) {
|
||||
if (rcl_wait_set_add_guard_condition(wait_set, guard_condition) != RCL_RET_OK) {
|
||||
if (rcl_wait_set_add_guard_condition(wait_set, guard_condition, NULL) != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't add guard_condition to wait set: %s",
|
||||
@@ -227,6 +250,15 @@ public:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto waitable : waitable_handles_) {
|
||||
if (!waitable->add_to_wait_set(wait_set)) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Couldn't add waitable to wait set: %s", rcl_get_error_string().str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -342,6 +374,39 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
virtual void
|
||||
get_next_waitable(executor::AnyExecutable & any_exec, const WeakNodeVector & weak_nodes)
|
||||
{
|
||||
auto it = waitable_handles_.begin();
|
||||
while (it != waitable_handles_.end()) {
|
||||
auto waitable = *it;
|
||||
if (waitable) {
|
||||
// Find the group for this handle and see if it can be serviced
|
||||
auto group = get_group_by_waitable(waitable, weak_nodes);
|
||||
if (!group) {
|
||||
// Group was not found, meaning the waitable is not valid...
|
||||
// Remove it from the ready list and continue looking
|
||||
it = waitable_handles_.erase(it);
|
||||
continue;
|
||||
}
|
||||
if (!group->can_be_taken_from().load()) {
|
||||
// Group is mutually exclusive and is being used, so skip it for now
|
||||
// Leave it to be checked next time, but continue searching
|
||||
++it;
|
||||
continue;
|
||||
}
|
||||
// Otherwise it is safe to set and return the any_exec
|
||||
any_exec.waitable = waitable;
|
||||
any_exec.callback_group = group;
|
||||
any_exec.node_base = get_node_by_group(group, weak_nodes);
|
||||
waitable_handles_.erase(it);
|
||||
return;
|
||||
}
|
||||
// Else, the waitable is no longer valid, remove it and continue
|
||||
it = waitable_handles_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
virtual rcl_allocator_t get_allocator()
|
||||
{
|
||||
return rclcpp::allocator::get_rcl_allocator<void *, VoidAlloc>(*allocator_.get());
|
||||
@@ -349,27 +414,52 @@ public:
|
||||
|
||||
size_t number_of_ready_subscriptions() const
|
||||
{
|
||||
return subscription_handles_.size();
|
||||
size_t number_of_subscriptions = subscription_handles_.size();
|
||||
for (auto waitable : waitable_handles_) {
|
||||
number_of_subscriptions += waitable->get_number_of_ready_subscriptions();
|
||||
}
|
||||
return number_of_subscriptions;
|
||||
}
|
||||
|
||||
size_t number_of_ready_services() const
|
||||
{
|
||||
return service_handles_.size();
|
||||
size_t number_of_services = service_handles_.size();
|
||||
for (auto waitable : waitable_handles_) {
|
||||
number_of_services += waitable->get_number_of_ready_services();
|
||||
}
|
||||
return number_of_services;
|
||||
}
|
||||
|
||||
size_t number_of_ready_clients() const
|
||||
{
|
||||
return client_handles_.size();
|
||||
size_t number_of_clients = client_handles_.size();
|
||||
for (auto waitable : waitable_handles_) {
|
||||
number_of_clients += waitable->get_number_of_ready_clients();
|
||||
}
|
||||
return number_of_clients;
|
||||
}
|
||||
|
||||
size_t number_of_guard_conditions() const
|
||||
{
|
||||
return guard_conditions_.size();
|
||||
size_t number_of_guard_conditions = guard_conditions_.size();
|
||||
for (auto waitable : waitable_handles_) {
|
||||
number_of_guard_conditions += waitable->get_number_of_ready_guard_conditions();
|
||||
}
|
||||
return number_of_guard_conditions;
|
||||
}
|
||||
|
||||
size_t number_of_ready_timers() const
|
||||
{
|
||||
return timer_handles_.size();
|
||||
size_t number_of_timers = timer_handles_.size();
|
||||
for (auto waitable : waitable_handles_) {
|
||||
number_of_timers += waitable->get_number_of_ready_timers();
|
||||
}
|
||||
return number_of_timers;
|
||||
}
|
||||
|
||||
size_t number_of_waitables() const
|
||||
{
|
||||
return waitable_handles_.size();
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -383,6 +473,7 @@ private:
|
||||
VectorRebind<std::shared_ptr<const rcl_service_t>> service_handles_;
|
||||
VectorRebind<std::shared_ptr<const rcl_client_t>> client_handles_;
|
||||
VectorRebind<std::shared_ptr<const rcl_timer_t>> timer_handles_;
|
||||
VectorRebind<std::shared_ptr<Waitable>> waitable_handles_;
|
||||
|
||||
std::shared_ptr<VoidAlloc> allocator_;
|
||||
};
|
||||
|
||||
@@ -46,6 +46,15 @@ namespace node_interfaces
|
||||
class NodeTopicsInterface;
|
||||
} // namespace node_interfaces
|
||||
|
||||
namespace intra_process_manager
|
||||
{
|
||||
/**
|
||||
* NOTE(ivanpauno): IntraProcessManager is forward declared here, avoiding a circular inclusion between intra_process_manager.hpp and publisher.hpp.
|
||||
* SharedPtr and WeakPtr of the IntraProcessManager are defined again here, to avoid a warning for accessing a member of a forward declared class.
|
||||
*/
|
||||
class IntraProcessManager;
|
||||
}
|
||||
|
||||
/// Virtual base class for subscriptions. This pattern allows us to iterate over different template
|
||||
/// specializations of Subscription, among other things.
|
||||
class SubscriptionBase
|
||||
@@ -129,11 +138,23 @@ public:
|
||||
bool
|
||||
is_serialized() const;
|
||||
|
||||
/// Get matching publisher count
|
||||
/** \return The number of publishers on this topic. */
|
||||
RCLCPP_PUBLIC
|
||||
size_t
|
||||
get_publisher_count() const;
|
||||
|
||||
protected:
|
||||
std::shared_ptr<rcl_subscription_t> intra_process_subscription_handle_;
|
||||
std::shared_ptr<rcl_subscription_t> subscription_handle_;
|
||||
std::shared_ptr<rcl_node_t> node_handle_;
|
||||
|
||||
using IntraProcessManagerWeakPtr =
|
||||
std::weak_ptr<rclcpp::intra_process_manager::IntraProcessManager>;
|
||||
bool use_intra_process_;
|
||||
IntraProcessManagerWeakPtr weak_ipm_;
|
||||
uint64_t intra_process_subscription_id_;
|
||||
|
||||
private:
|
||||
RCLCPP_DISABLE_COPY(SubscriptionBase)
|
||||
|
||||
@@ -272,10 +293,13 @@ public:
|
||||
using MatchesAnyPublishersCallbackType = std::function<bool (const rmw_gid_t *)>;
|
||||
|
||||
/// Implemenation detail.
|
||||
// TODO(ivanpauno): This can be moved to the base class. No reason to be here.
|
||||
// Also get_intra_process_message_callback_ and matches_any_intra_process_publishers_.
|
||||
void setup_intra_process(
|
||||
uint64_t intra_process_subscription_id,
|
||||
GetMessageCallbackType get_message_callback,
|
||||
MatchesAnyPublishersCallbackType matches_any_publisher_callback,
|
||||
IntraProcessManagerWeakPtr weak_ipm,
|
||||
const rcl_subscription_options_t & intra_process_options)
|
||||
{
|
||||
std::string intra_process_topic_name = std::string(get_topic_name()) + "/_intra";
|
||||
@@ -302,6 +326,8 @@ public:
|
||||
intra_process_subscription_id_ = intra_process_subscription_id;
|
||||
get_intra_process_message_callback_ = get_message_callback;
|
||||
matches_any_intra_process_publishers_ = matches_any_publisher_callback;
|
||||
weak_ipm_ = weak_ipm;
|
||||
use_intra_process_ = true;
|
||||
}
|
||||
|
||||
/// Implemenation detail.
|
||||
@@ -323,7 +349,6 @@ private:
|
||||
|
||||
GetMessageCallbackType get_intra_process_message_callback_;
|
||||
MatchesAnyPublishersCallbackType matches_any_intra_process_publishers_;
|
||||
uint64_t intra_process_subscription_id_;
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -166,6 +166,7 @@ create_subscription_factory(
|
||||
intra_process_subscription_id,
|
||||
take_intra_process_message_func,
|
||||
matches_any_publisher_func,
|
||||
weak_ipm,
|
||||
intra_process_options
|
||||
);
|
||||
};
|
||||
|
||||
48
rclcpp/include/rclcpp/subscription_options.hpp
Normal file
48
rclcpp/include/rclcpp/subscription_options.hpp
Normal file
@@ -0,0 +1,48 @@
|
||||
// Copyright 2019 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#ifndef RCLCPP__SUBSCRIPTION_OPTIONS_HPP_
|
||||
#define RCLCPP__SUBSCRIPTION_OPTIONS_HPP_
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/intra_process_setting.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Structure containing optional configuration for Subscriptions.
|
||||
template<typename Allocator>
|
||||
struct SubscriptionOptionsWithAllocator
|
||||
{
|
||||
/// The quality of service profile to pass on to the rmw implementation.
|
||||
rmw_qos_profile_t qos_profile = rmw_qos_profile_default;
|
||||
/// True to ignore local publications.
|
||||
bool ignore_local_publications = false;
|
||||
/// The callback group for this subscription. NULL to use the default callback group.
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr callback_group = nullptr;
|
||||
/// Optional custom allocator.
|
||||
std::shared_ptr<Allocator> allocator = nullptr;
|
||||
/// Setting to explicitly set intraprocess communications.
|
||||
IntraProcessSetting use_intra_process_comm = IntraProcessSetting::NodeDefault;
|
||||
};
|
||||
|
||||
using SubscriptionOptions = SubscriptionOptionsWithAllocator<std::allocator<void>>;
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__SUBSCRIPTION_OPTIONS_HPP_
|
||||
@@ -47,10 +47,13 @@ public:
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void attachNode(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
|
||||
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
|
||||
const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
|
||||
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface);
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface,
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
void detachNode();
|
||||
@@ -74,6 +77,12 @@ private:
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_;
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_;
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_;
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_;
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_;
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_;
|
||||
|
||||
// Store (and update on node attach) logger for logging.
|
||||
Logger logger_;
|
||||
|
||||
// The subscription for the clock callback
|
||||
using MessageT = rosgraph_msgs::msg::Clock;
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include <utility>
|
||||
|
||||
#include "rclcpp/clock.hpp"
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/function_traits.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/rate.hpp"
|
||||
@@ -45,7 +46,10 @@ public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(TimerBase)
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
explicit TimerBase(Clock::SharedPtr clock, std::chrono::nanoseconds period);
|
||||
explicit TimerBase(
|
||||
Clock::SharedPtr clock,
|
||||
std::chrono::nanoseconds period,
|
||||
rclcpp::Context::SharedPtr context);
|
||||
|
||||
RCLCPP_PUBLIC
|
||||
~TimerBase();
|
||||
@@ -114,9 +118,10 @@ public:
|
||||
* \param[in] callback User-specified callback function.
|
||||
*/
|
||||
explicit GenericTimer(
|
||||
Clock::SharedPtr clock, std::chrono::nanoseconds period, FunctorT && callback
|
||||
Clock::SharedPtr clock, std::chrono::nanoseconds period, FunctorT && callback,
|
||||
rclcpp::Context::SharedPtr context
|
||||
)
|
||||
: TimerBase(clock, period), callback_(std::forward<FunctorT>(callback))
|
||||
: TimerBase(clock, period, context), callback_(std::forward<FunctorT>(callback))
|
||||
{
|
||||
}
|
||||
|
||||
@@ -128,7 +133,7 @@ public:
|
||||
}
|
||||
|
||||
void
|
||||
execute_callback()
|
||||
execute_callback() override
|
||||
{
|
||||
rcl_ret_t ret = rcl_timer_call(timer_handle_.get());
|
||||
if (ret == RCL_RET_TIMER_CANCELED) {
|
||||
@@ -165,8 +170,8 @@ public:
|
||||
callback_(*this);
|
||||
}
|
||||
|
||||
virtual bool
|
||||
is_steady()
|
||||
bool
|
||||
is_steady() override
|
||||
{
|
||||
return clock_->get_clock_type() == RCL_STEADY_TIME;
|
||||
}
|
||||
@@ -189,9 +194,12 @@ class WallTimer : public GenericTimer<FunctorT>
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS(WallTimer)
|
||||
|
||||
explicit WallTimer(std::chrono::nanoseconds period, FunctorT && callback)
|
||||
WallTimer(
|
||||
std::chrono::nanoseconds period,
|
||||
FunctorT && callback,
|
||||
rclcpp::Context::SharedPtr context)
|
||||
: GenericTimer<FunctorT>(
|
||||
std::make_shared<Clock>(RCL_STEADY_TIME), period, std::move(callback))
|
||||
std::make_shared<Clock>(RCL_STEADY_TIME), period, std::move(callback), context)
|
||||
{}
|
||||
|
||||
protected:
|
||||
|
||||
@@ -20,11 +20,10 @@
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/context.hpp"
|
||||
#include "rclcpp/init_options.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rcl/guard_condition.h"
|
||||
#include "rcl/wait.h"
|
||||
|
||||
#include "rmw/macros.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
@@ -48,28 +47,75 @@ namespace rclcpp
|
||||
{
|
||||
/// Initialize communications via the rmw implementation and set up a global signal handler.
|
||||
/**
|
||||
* \param[in] argc Number of arguments.
|
||||
* \param[in] argv Argument vector. Will eventually be used for passing options to rclcpp.
|
||||
* Initializes the global context which is accessible via the function
|
||||
* rclcpp::contexts::default_context::get_global_default_context().
|
||||
* Also, installs the global signal handlers with the function
|
||||
* rclcpp::install_signal_handlers().
|
||||
*
|
||||
* \sa rclcpp::Context::init() for more details on arguments and possible exceptions
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
init(int argc, char const * const argv[]);
|
||||
init(int argc, char const * const argv[], const InitOptions & init_options = InitOptions());
|
||||
|
||||
/// Install the global signal handler for rclcpp.
|
||||
/**
|
||||
* This function should only need to be run one time per process.
|
||||
* It is implicitly run by rclcpp::init(), and therefore this function does not
|
||||
* need to be run manually if rclcpp::init() has already been run.
|
||||
*
|
||||
* The signal handler will shutdown all initialized context.
|
||||
* It will also interrupt any blocking functions in ROS allowing them react to
|
||||
* any changes in the state of the system (like shutdown).
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* \return true if signal handler was installed by this function, false if already installed.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
install_signal_handlers();
|
||||
|
||||
/// Return true if the signal handlers are installed, otherwise false.
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
signal_handlers_installed();
|
||||
|
||||
/// Uninstall the global signal handler for rclcpp.
|
||||
/**
|
||||
* This function does not necessarily need to be called, but can be used to
|
||||
* undo what rclcpp::install_signal_handlers() or rclcpp::init() do with
|
||||
* respect to signal handling.
|
||||
* If you choose to use it, this function only needs to be run one time.
|
||||
* It is implicitly run by rclcpp::shutdown(), and therefore this function does
|
||||
* not need to be run manually if rclcpp::shutdown() has already been run.
|
||||
*
|
||||
* This function is thread-safe.
|
||||
*
|
||||
* \return true if signal handler was uninstalled by this function, false if was not installed.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
uninstall_signal_handlers();
|
||||
|
||||
/// Initialize communications via the rmw implementation and set up a global signal handler.
|
||||
/**
|
||||
* Additionally removes ROS-specific arguments from the argument vector.
|
||||
* \param[in] argc Number of arguments.
|
||||
* \param[in] argv Argument vector.
|
||||
*
|
||||
* \sa rclcpp::Context::init() for more details on arguments and possible exceptions
|
||||
* \returns Members of the argument vector that are not ROS arguments.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
std::vector<std::string>
|
||||
init_and_remove_ros_arguments(int argc, char const * const argv[]);
|
||||
init_and_remove_ros_arguments(
|
||||
int argc,
|
||||
char const * const argv[],
|
||||
const InitOptions & init_options = InitOptions());
|
||||
|
||||
/// Remove ROS-specific arguments from argument vector.
|
||||
/**
|
||||
* Some arguments may not have been intended as ROS arguments.
|
||||
* This function populates a the aruments in a vector.
|
||||
* This function populates the arguments in a vector.
|
||||
* Since the first argument is always assumed to be a process name, the vector
|
||||
* will always contain the process name.
|
||||
*
|
||||
@@ -82,65 +128,91 @@ std::vector<std::string>
|
||||
remove_ros_arguments(int argc, char const * const argv[]);
|
||||
|
||||
/// Check rclcpp's status.
|
||||
/** \return True if SIGINT hasn't fired yet, false otherwise. */
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
ok();
|
||||
|
||||
/// Returns true if init() has already been called.
|
||||
/** \return True if init() has been called, false otherwise. */
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_initialized();
|
||||
|
||||
/// Notify the signal handler and rmw that rclcpp is shutting down.
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
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.
|
||||
/**
|
||||
* The first time that this function is called for a given wait set a new guard
|
||||
* condition will be created and returned; thereafter the same guard condition
|
||||
* will be returned for the same wait set. This mechanism is designed to ensure
|
||||
* that the same guard condition is not reused across wait sets (e.g., when
|
||||
* using multiple executors in the same process). Will throw an exception if
|
||||
* initialization of the guard condition fails.
|
||||
* \param wait_set Pointer to the rcl_wait_set_t that will be using the
|
||||
* resulting guard condition.
|
||||
* \return Pointer to the guard condition.
|
||||
* This may return false for a context which has been shutdown, or for a
|
||||
* context that was shutdown due to SIGINT being received by the rclcpp signal
|
||||
* handler.
|
||||
*
|
||||
* If nullptr is given for the context, then the global context is used, i.e.
|
||||
* the context initialized by rclcpp::init().
|
||||
*
|
||||
* \param[in] context Check for shutdown of this Context.
|
||||
* \return true if shutdown has been called, false otherwise
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
rcl_guard_condition_t *
|
||||
get_sigint_guard_condition(rcl_wait_set_t * wait_set);
|
||||
bool
|
||||
ok(rclcpp::Context::SharedPtr context = nullptr);
|
||||
|
||||
/// Release the previously allocated guard condition that manages the signal handler.
|
||||
/// Return true if init() has already been called for the given context.
|
||||
/**
|
||||
* If you previously called get_sigint_guard_condition() for a given wait set
|
||||
* 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 wait set.
|
||||
* \param wait_set Pointer to the rcl_wait_set_t that was using the
|
||||
* resulting guard condition.
|
||||
* If nullptr is given for the context, then the global context is used, i.e.
|
||||
* the context initialized by rclcpp::init().
|
||||
*
|
||||
* Deprecated, as it is no longer different from rcl_ok().
|
||||
*
|
||||
* \param[in] context Check for initialization of this Context.
|
||||
* \return true if the context is initialized, and false otherwise
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
is_initialized(rclcpp::Context::SharedPtr context = nullptr);
|
||||
|
||||
/// Shutdown rclcpp context, invalidating it for derived entities.
|
||||
/**
|
||||
* If nullptr is given for the context, then the global context is used, i.e.
|
||||
* the context initialized by rclcpp::init().
|
||||
*
|
||||
* If the global context is used, then the signal handlers are also uninstalled.
|
||||
*
|
||||
* This will also cause the "on_shutdown" callbacks to be called.
|
||||
*
|
||||
* \sa rclcpp::Context::shutdown()
|
||||
* \param[in] context to be shutdown
|
||||
* \return true if shutdown was successful, false if context was already shutdown
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
shutdown(
|
||||
rclcpp::Context::SharedPtr context = nullptr,
|
||||
const std::string & reason = "user called rclcpp::shutdown()");
|
||||
|
||||
/// Register a function to be called when shutdown is called on the context.
|
||||
/**
|
||||
* If nullptr is given for the context, then the global context is used, i.e.
|
||||
* the context initialized by rclcpp::init().
|
||||
*
|
||||
* These callbacks are called when the associated Context is shutdown with the
|
||||
* Context::shutdown() method.
|
||||
* When shutdown by the SIGINT handler, shutdown, and therefore these callbacks,
|
||||
* is called asynchronously from the dedicated signal handling thread, at some
|
||||
* point after the SIGINT signal is received.
|
||||
*
|
||||
* \sa rclcpp::Context::on_shutdown()
|
||||
* \param[in] callback to be called when the given context is shutdown
|
||||
* \param[in] context with which to associate the context
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
void
|
||||
release_sigint_guard_condition(rcl_wait_set_t * wait_set);
|
||||
on_shutdown(std::function<void()> callback, rclcpp::Context::SharedPtr context = nullptr);
|
||||
|
||||
/// Use the global condition variable to block for the specified amount of time.
|
||||
/**
|
||||
* This function can be interrupted early if the associated context becomes
|
||||
* invalid due to shutdown() or the signal handler.
|
||||
* \sa rclcpp::Context::sleep_for
|
||||
*
|
||||
* If nullptr is given for the context, then the global context is used, i.e.
|
||||
* the context initialized by rclcpp::init().
|
||||
*
|
||||
* \param[in] nanoseconds A std::chrono::duration representing how long to sleep for.
|
||||
* \return True if the condition variable did not timeout.
|
||||
* \param[in] context which may interrupt this sleep
|
||||
* \return true if the condition variable did not timeout.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
bool
|
||||
sleep_for(const std::chrono::nanoseconds & nanoseconds);
|
||||
sleep_for(
|
||||
const std::chrono::nanoseconds & nanoseconds,
|
||||
rclcpp::Context::SharedPtr context = nullptr);
|
||||
|
||||
/// Safely check if addition will overflow.
|
||||
/**
|
||||
|
||||
141
rclcpp/include/rclcpp/waitable.hpp
Normal file
141
rclcpp/include/rclcpp/waitable.hpp
Normal file
@@ -0,0 +1,141 @@
|
||||
// Copyright 2018 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__WAITABLE_HPP_
|
||||
#define RCLCPP__WAITABLE_HPP_
|
||||
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/visibility_control.hpp"
|
||||
|
||||
#include "rcl/wait.h"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
class Waitable
|
||||
{
|
||||
public:
|
||||
RCLCPP_SMART_PTR_DEFINITIONS_NOT_COPYABLE(Waitable)
|
||||
|
||||
/// Get the number of ready subscriptions
|
||||
/**
|
||||
* Returns a value of 0 by default.
|
||||
* This should be overridden if the Waitable contains one or more subscriptions.
|
||||
* \return The number of subscriptions associated with the Waitable.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
get_number_of_ready_subscriptions();
|
||||
|
||||
/// Get the number of ready timers
|
||||
/**
|
||||
* Returns a value of 0 by default.
|
||||
* This should be overridden if the Waitable contains one or more timers.
|
||||
* \return The number of timers associated with the Waitable.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
get_number_of_ready_timers();
|
||||
|
||||
/// Get the number of ready clients
|
||||
/**
|
||||
* Returns a value of 0 by default.
|
||||
* This should be overridden if the Waitable contains one or more clients.
|
||||
* \return The number of clients associated with the Waitable.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
get_number_of_ready_clients();
|
||||
|
||||
/// Get the number of ready services
|
||||
/**
|
||||
* Returns a value of 0 by default.
|
||||
* This should be overridden if the Waitable contains one or more services.
|
||||
* \return The number of services associated with the Waitable.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
get_number_of_ready_services();
|
||||
|
||||
/// Get the number of ready guard_conditions
|
||||
/**
|
||||
* Returns a value of 0 by default.
|
||||
* This should be overridden if the Waitable contains one or more guard_conditions.
|
||||
* \return The number of guard_conditions associated with the Waitable.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
size_t
|
||||
get_number_of_ready_guard_conditions();
|
||||
|
||||
// TODO(jacobperron): smart pointer?
|
||||
/// Add the Waitable to a wait set.
|
||||
/**
|
||||
* \param[in] wait_set A handle to the wait set to add the Waitable to.
|
||||
* \return `true` if the Waitable is added successfully, `false` otherwise.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
add_to_wait_set(rcl_wait_set_t * wait_set) = 0;
|
||||
|
||||
/// Check if the Waitable is ready.
|
||||
/**
|
||||
* The input wait set should be the same that was used in a previously call to
|
||||
* `add_to_wait_set()`.
|
||||
* The wait set should also have been previously waited on with `rcl_wait()`.
|
||||
*
|
||||
* \param[in] wait_set A handle to the wait set the Waitable was previously added to
|
||||
* and that has been waited on.
|
||||
* \return `true` if the Waitable is ready, `false` otherwise.
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
bool
|
||||
is_ready(rcl_wait_set_t *) = 0;
|
||||
|
||||
/// Execute any entities of the Waitable that are ready.
|
||||
/**
|
||||
* Before calling this method, the Waitable should be added to a wait set,
|
||||
* waited on, and then updated.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* ```
|
||||
* // ... create a wait set and a Waitable
|
||||
* // Add the Waitable to the wait set
|
||||
* bool add_ret = waitable.add_to_wait_set(wait_set);
|
||||
* // ... error handling
|
||||
* // Wait
|
||||
* rcl_ret_t wait_ret = rcl_wait(wait_set);
|
||||
* // ... error handling
|
||||
* // Update the Waitable
|
||||
* waitable.update(wait_set);
|
||||
* // Execute any entities of the Waitable that may be ready
|
||||
* waitable.execute();
|
||||
* ```
|
||||
*/
|
||||
RCLCPP_PUBLIC
|
||||
virtual
|
||||
void
|
||||
execute() = 0;
|
||||
}; // class Waitable
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__WAITABLE_HPP_
|
||||
@@ -2,7 +2,7 @@
|
||||
<?xml-model href="http://download.ros.org/schema/package_format2.xsd" schematypens="http://www.w3.org/2001/XMLSchema"?>
|
||||
<package format="2">
|
||||
<name>rclcpp</name>
|
||||
<version>0.6.0</version>
|
||||
<version>0.7.0</version>
|
||||
<description>The ROS client library in C++.</description>
|
||||
<maintainer email="dthomas@osrfoundation.org">Dirk Thomas</maintainer>
|
||||
<license>Apache License 2.0</license>
|
||||
|
||||
@@ -71,6 +71,9 @@ def is_supported_feature_combination(feature_combination):
|
||||
#else
|
||||
@[ for feature_combination in [fc for fc in feature_combinations if is_supported_feature_combination(fc)]]@
|
||||
@{suffix = get_suffix_from_features(feature_combination)}@
|
||||
// The RCLCPP_@(severity)@(suffix) macro is surrounded by do { .. } while (0)
|
||||
// to implement the standard C macro idiom to make the macro safe in all
|
||||
// contexts; see http://c-faq.com/cpp/multistmt.html for more information.
|
||||
/**
|
||||
* \def RCLCPP_@(severity)@(suffix)
|
||||
* Log a message with severity @(severity)@
|
||||
@@ -90,18 +93,20 @@ def is_supported_feature_combination(feature_combination):
|
||||
* It also accepts a single argument of type std::string.
|
||||
*/
|
||||
#define RCLCPP_@(severity)@(suffix)(logger, @(''.join([p + ', ' for p in get_macro_parameters(feature_combination).keys()]))...) \
|
||||
static_assert( \
|
||||
::std::is_same<typename std::remove_reference<decltype(logger)>::type, \
|
||||
typename ::rclcpp::Logger>::value, \
|
||||
"First argument to logging macros must be an rclcpp::Logger"); \
|
||||
RCUTILS_LOG_@(severity)@(suffix)_NAMED( \
|
||||
do { \
|
||||
static_assert( \
|
||||
::std::is_same<typename std::remove_reference<decltype(logger)>::type, \
|
||||
typename ::rclcpp::Logger>::value, \
|
||||
"First argument to logging macros must be an rclcpp::Logger"); \
|
||||
RCUTILS_LOG_@(severity)@(suffix)_NAMED( \
|
||||
@{params = get_macro_parameters(feature_combination).keys()}@
|
||||
@[ if params]@
|
||||
@(''.join([' ' + p + ', \\\n' for p in params]))@
|
||||
@(''.join([' ' + p + ', \\\n' for p in params]))@
|
||||
@[ end if]@
|
||||
logger.get_name(), \
|
||||
rclcpp::get_c_string(RCLCPP_FIRST_ARG(__VA_ARGS__, "")), \
|
||||
RCLCPP_ALL_BUT_FIRST_ARGS(__VA_ARGS__,""))
|
||||
logger.get_name(), \
|
||||
rclcpp::get_c_string(RCLCPP_FIRST_ARG(__VA_ARGS__, "")), \
|
||||
RCLCPP_ALL_BUT_FIRST_ARGS(__VA_ARGS__,"")); \
|
||||
} while (0)
|
||||
|
||||
@[ end for]@
|
||||
#endif
|
||||
|
||||
@@ -51,6 +51,13 @@ CallbackGroup::get_client_ptrs() const
|
||||
return client_ptrs_;
|
||||
}
|
||||
|
||||
const std::vector<rclcpp::Waitable::WeakPtr> &
|
||||
CallbackGroup::get_waitable_ptrs() const
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
return waitable_ptrs_;
|
||||
}
|
||||
|
||||
std::atomic_bool &
|
||||
CallbackGroup::can_be_taken_from()
|
||||
{
|
||||
@@ -91,3 +98,23 @@ CallbackGroup::add_client(const rclcpp::ClientBase::SharedPtr client_ptr)
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
client_ptrs_.push_back(client_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
CallbackGroup::add_waitable(const rclcpp::Waitable::SharedPtr waitable_ptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
waitable_ptrs_.push_back(waitable_ptr);
|
||||
}
|
||||
|
||||
void
|
||||
CallbackGroup::remove_waitable(const rclcpp::Waitable::SharedPtr waitable_ptr) noexcept
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
for (auto iter = waitable_ptrs_.begin(); iter != waitable_ptrs_.end(); ++iter) {
|
||||
const auto shared_ptr = iter->lock();
|
||||
if (shared_ptr.get() == waitable_ptr.get()) {
|
||||
waitable_ptrs_.erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,17 +37,20 @@ ClientBase::ClientBase(
|
||||
rclcpp::node_interfaces::NodeBaseInterface * node_base,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph)
|
||||
: node_graph_(node_graph),
|
||||
node_handle_(node_base->get_shared_rcl_node_handle())
|
||||
node_handle_(node_base->get_shared_rcl_node_handle()),
|
||||
context_(node_base->get_context())
|
||||
{
|
||||
std::weak_ptr<rcl_node_t> weak_node_handle(node_handle_);
|
||||
client_handle_ = std::shared_ptr<rcl_client_t>(
|
||||
new rcl_client_t, [weak_node_handle](rcl_client_t * client)
|
||||
rcl_client_t * new_rcl_client = new rcl_client_t;
|
||||
*new_rcl_client = rcl_get_zero_initialized_client();
|
||||
client_handle_.reset(
|
||||
new_rcl_client, [weak_node_handle](rcl_client_t * client)
|
||||
{
|
||||
auto handle = weak_node_handle.lock();
|
||||
if (handle) {
|
||||
if (rcl_client_fini(client, handle.get()) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger(rcl_node_get_logger_name(handle.get())).get_child("rclcpp"),
|
||||
rclcpp::get_node_logger(handle.get()).get_child("rclcpp"),
|
||||
"Error in destruction of rcl client handle: %s", rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
@@ -59,7 +62,6 @@ ClientBase::ClientBase(
|
||||
}
|
||||
delete client;
|
||||
});
|
||||
*client_handle_.get() = rcl_get_zero_initialized_client();
|
||||
}
|
||||
|
||||
ClientBase::~ClientBase()
|
||||
@@ -94,6 +96,13 @@ ClientBase::service_is_ready() const
|
||||
this->get_rcl_node_handle(),
|
||||
this->get_client_handle().get(),
|
||||
&is_ready);
|
||||
if (RCL_RET_NODE_INVALID == ret) {
|
||||
const rcl_node_t * node_handle = this->get_rcl_node_handle();
|
||||
if (node_handle && !rcl_context_is_valid(node_handle->context)) {
|
||||
// context is shutdown, do a soft failure
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "rcl_service_server_is_available failed");
|
||||
}
|
||||
@@ -109,7 +118,6 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
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;
|
||||
@@ -118,17 +126,20 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
// check was non-blocking, return immediately
|
||||
return false;
|
||||
}
|
||||
auto event = node_ptr->get_graph_event();
|
||||
// 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)) {
|
||||
std::chrono::nanoseconds time_to_wait =
|
||||
timeout > std::chrono::nanoseconds(0) ?
|
||||
timeout - (std::chrono::steady_clock::now() - start) :
|
||||
std::chrono::nanoseconds::max();
|
||||
if (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
|
||||
do {
|
||||
if (!rclcpp::ok()) {
|
||||
if (!rclcpp::ok(this->context_)) {
|
||||
return false;
|
||||
}
|
||||
// Limit each wait to 100ms to workaround an issue specific to the Connext RMW implementation.
|
||||
@@ -147,8 +158,11 @@ ClientBase::wait_for_service_nanoseconds(std::chrono::nanoseconds timeout)
|
||||
return true;
|
||||
}
|
||||
// server is not ready, loop if there is time left
|
||||
time_to_wait = timeout - (std::chrono::steady_clock::now() - start);
|
||||
} while (time_to_wait > std::chrono::nanoseconds(0) || timeout < std::chrono::nanoseconds(0));
|
||||
if (timeout > std::chrono::nanoseconds(0)) {
|
||||
time_to_wait = timeout - (std::chrono::steady_clock::now() - start);
|
||||
}
|
||||
// if timeout is negative, time_to_wait will never reach zero
|
||||
} while (time_to_wait > std::chrono::nanoseconds(0));
|
||||
return false; // timeout exceeded while waiting for the server to be ready
|
||||
}
|
||||
|
||||
|
||||
@@ -130,10 +130,15 @@ Clock::create_jump_callback(
|
||||
rclcpp::Clock::on_time_jump, handler);
|
||||
if (RCL_RET_OK != ret) {
|
||||
delete handler;
|
||||
handler = NULL;
|
||||
handler = nullptr;
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to add time jump callback");
|
||||
}
|
||||
|
||||
if (nullptr == handler) {
|
||||
// imposible to reach here; added to make cppcheck happy
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// *INDENT-OFF*
|
||||
// create shared_ptr that removes the callback automatically when all copies are destructed
|
||||
return rclcpp::JumpHandler::SharedPtr(handler, [this](rclcpp::JumpHandler * handler) noexcept {
|
||||
|
||||
@@ -14,6 +14,296 @@
|
||||
|
||||
#include "rclcpp/context.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rcl/init.h"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
|
||||
/// Mutex to protect initialized contexts.
|
||||
static std::mutex g_contexts_mutex;
|
||||
/// Weak list of context to be shutdown by the signal handler.
|
||||
static std::vector<std::weak_ptr<rclcpp::Context>> g_contexts;
|
||||
|
||||
using rclcpp::Context;
|
||||
|
||||
Context::Context() {}
|
||||
Context::Context()
|
||||
: rcl_context_(nullptr), shutdown_reason_("") {}
|
||||
|
||||
Context::~Context()
|
||||
{
|
||||
// acquire the init lock to prevent race conditions with init and shutdown
|
||||
// this will not prevent errors, but will maybe make them easier to reproduce
|
||||
std::lock_guard<std::recursive_mutex> lock(init_mutex_);
|
||||
try {
|
||||
this->shutdown("context destructor was called while still not shutdown");
|
||||
// at this point it is shutdown and cannot reinit
|
||||
// clean_up will finalize the rcl context
|
||||
this->clean_up();
|
||||
} catch (const std::exception & exc) {
|
||||
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "unhandled exception in ~Context(): %s", exc.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR(rclcpp::get_logger("rclcpp"), "unhandled exception in ~Context()");
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_LOCAL
|
||||
void
|
||||
__delete_context(rcl_context_t * context)
|
||||
{
|
||||
if (context) {
|
||||
if (rcl_context_is_valid(context)) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"), "rcl context unexpectedly not shutdown during cleanup");
|
||||
} else {
|
||||
// if context pointer is not null and is shutdown, then it's ready for fini
|
||||
rcl_ret_t ret = rcl_context_fini(context);
|
||||
if (RCL_RET_OK != ret) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"failed to finalize context: %s", rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
}
|
||||
delete context;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::init(
|
||||
int argc,
|
||||
char const * const argv[],
|
||||
const rclcpp::InitOptions & init_options)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
|
||||
if (this->is_valid()) {
|
||||
throw rclcpp::ContextAlreadyInitialized();
|
||||
}
|
||||
this->clean_up();
|
||||
rcl_context_.reset(new rcl_context_t, __delete_context);
|
||||
*rcl_context_.get() = rcl_get_zero_initialized_context();
|
||||
rcl_ret_t ret = rcl_init(argc, argv, init_options.get_rcl_init_options(), rcl_context_.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
rcl_context_.reset();
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to initialize rcl");
|
||||
}
|
||||
init_options_ = init_options;
|
||||
|
||||
std::lock_guard<std::mutex> lock(g_contexts_mutex);
|
||||
g_contexts.push_back(this->shared_from_this());
|
||||
}
|
||||
|
||||
bool
|
||||
Context::is_valid() const
|
||||
{
|
||||
// Take a local copy of the shared pointer to avoid it getting nulled under our feet.
|
||||
auto local_rcl_context = rcl_context_;
|
||||
if (!local_rcl_context) {
|
||||
return false;
|
||||
}
|
||||
return rcl_context_is_valid(local_rcl_context.get());
|
||||
}
|
||||
|
||||
const rclcpp::InitOptions &
|
||||
Context::get_init_options() const
|
||||
{
|
||||
return init_options_;
|
||||
}
|
||||
|
||||
rclcpp::InitOptions
|
||||
Context::get_init_options()
|
||||
{
|
||||
return init_options_;
|
||||
}
|
||||
|
||||
std::string
|
||||
Context::shutdown_reason()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(init_mutex_);
|
||||
return shutdown_reason_;
|
||||
}
|
||||
|
||||
bool
|
||||
Context::shutdown(const std::string & reason)
|
||||
{
|
||||
// prevent races
|
||||
std::lock_guard<std::recursive_mutex> init_lock(init_mutex_);
|
||||
// ensure validity
|
||||
if (!this->is_valid()) {
|
||||
// if it is not valid, then it cannot be shutdown
|
||||
return false;
|
||||
}
|
||||
// rcl shutdown
|
||||
rcl_ret_t ret = rcl_shutdown(rcl_context_.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret);
|
||||
}
|
||||
// set shutdown reason
|
||||
shutdown_reason_ = reason;
|
||||
// call each shutdown callback
|
||||
for (const auto & callback : on_shutdown_callbacks_) {
|
||||
callback();
|
||||
}
|
||||
// interrupt all blocking sleep_for() and all blocking executors or wait sets
|
||||
this->interrupt_all_sleep_for();
|
||||
this->interrupt_all_wait_sets();
|
||||
// remove self from the global contexts
|
||||
std::lock_guard<std::mutex> context_lock(g_contexts_mutex);
|
||||
for (auto it = g_contexts.begin(); it != g_contexts.end(); ) {
|
||||
auto shared_context = it->lock();
|
||||
if (shared_context.get() == this) {
|
||||
it = g_contexts.erase(it);
|
||||
break;
|
||||
} else {
|
||||
++it;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
rclcpp::Context::OnShutdownCallback
|
||||
Context::on_shutdown(OnShutdownCallback callback)
|
||||
{
|
||||
on_shutdown_callbacks_.push_back(callback);
|
||||
return callback;
|
||||
}
|
||||
|
||||
const std::vector<rclcpp::Context::OnShutdownCallback> &
|
||||
Context::get_on_shutdown_callbacks() const
|
||||
{
|
||||
return on_shutdown_callbacks_;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Context::OnShutdownCallback> &
|
||||
Context::get_on_shutdown_callbacks()
|
||||
{
|
||||
return on_shutdown_callbacks_;
|
||||
}
|
||||
|
||||
std::shared_ptr<rcl_context_t>
|
||||
Context::get_rcl_context()
|
||||
{
|
||||
return rcl_context_;
|
||||
}
|
||||
|
||||
bool
|
||||
Context::sleep_for(const std::chrono::nanoseconds & nanoseconds)
|
||||
{
|
||||
std::chrono::nanoseconds time_left = nanoseconds;
|
||||
{
|
||||
std::unique_lock<std::mutex> lock(interrupt_mutex_);
|
||||
auto start = std::chrono::steady_clock::now();
|
||||
// this will release the lock while waiting
|
||||
interrupt_condition_variable_.wait_for(lock, nanoseconds);
|
||||
time_left -= std::chrono::steady_clock::now() - start;
|
||||
}
|
||||
if (time_left > std::chrono::nanoseconds::zero() && this->is_valid()) {
|
||||
return sleep_for(time_left);
|
||||
}
|
||||
// Return true if the timeout elapsed successfully, otherwise false.
|
||||
return this->is_valid();
|
||||
}
|
||||
|
||||
void
|
||||
Context::interrupt_all_sleep_for()
|
||||
{
|
||||
interrupt_condition_variable_.notify_all();
|
||||
}
|
||||
|
||||
rcl_guard_condition_t *
|
||||
Context::get_interrupt_guard_condition(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(interrupt_guard_cond_handles_mutex_);
|
||||
auto kv = interrupt_guard_cond_handles_.find(wait_set);
|
||||
if (kv != interrupt_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();
|
||||
auto ret = rcl_guard_condition_init(&handle, this->get_rcl_context().get(), options);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "Couldn't initialize guard condition");
|
||||
}
|
||||
interrupt_guard_cond_handles_.emplace(wait_set, handle);
|
||||
return &interrupt_guard_cond_handles_[wait_set];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::release_interrupt_guard_condition(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(interrupt_guard_cond_handles_mutex_);
|
||||
auto kv = interrupt_guard_cond_handles_.find(wait_set);
|
||||
if (kv != interrupt_guard_cond_handles_.end()) {
|
||||
rcl_ret_t ret = rcl_guard_condition_fini(&kv->second);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "Failed to destroy sigint guard condition");
|
||||
}
|
||||
interrupt_guard_cond_handles_.erase(kv);
|
||||
} else {
|
||||
throw std::runtime_error("Tried to release sigint guard condition for nonexistent wait set");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::release_interrupt_guard_condition(
|
||||
rcl_wait_set_t * wait_set,
|
||||
const std::nothrow_t &) noexcept
|
||||
{
|
||||
try {
|
||||
this->release_interrupt_guard_condition(wait_set);
|
||||
} catch (const std::exception & exc) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"caught %s exception when releasing interrupt guard condition: %s",
|
||||
rmw::impl::cpp::demangle(exc).c_str(), exc.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"caught unknown exception when releasing interrupt guard condition");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::interrupt_all_wait_sets()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(interrupt_guard_cond_handles_mutex_);
|
||||
for (auto & kv : interrupt_guard_cond_handles_) {
|
||||
rcl_ret_t status = rcl_trigger_guard_condition(&(kv.second));
|
||||
if (status != RCL_RET_OK) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"failed to trigger guard condition in Context::interrupt_all_wait_sets(): %s",
|
||||
rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
Context::clean_up()
|
||||
{
|
||||
shutdown_reason_ = "";
|
||||
rcl_context_.reset();
|
||||
}
|
||||
|
||||
std::vector<Context::SharedPtr>
|
||||
rclcpp::get_contexts()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_contexts_mutex);
|
||||
std::vector<Context::SharedPtr> shared_contexts;
|
||||
for (auto it = g_contexts.begin(); it != g_contexts.end(); /* noop */) {
|
||||
auto context_ptr = it->lock();
|
||||
if (!context_ptr) {
|
||||
// remove invalid weak context pointers
|
||||
it = g_contexts.erase(it);
|
||||
} else {
|
||||
++it;
|
||||
shared_contexts.push_back(context_ptr);
|
||||
}
|
||||
}
|
||||
return shared_contexts;
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
using rclcpp::executor::AnyExecutable;
|
||||
using rclcpp::executor::Executor;
|
||||
using rclcpp::executor::ExecutorArgs;
|
||||
@@ -40,27 +41,27 @@ Executor::Executor(const ExecutorArgs & args)
|
||||
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().str);
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&interrupt_guard_condition_, args.context->get_rcl_context().get(), guard_condition_options);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "Failed to create interrupt guard condition in Executor constructor");
|
||||
}
|
||||
|
||||
// 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::get_sigint_guard_condition(&wait_set_));
|
||||
memory_strategy_->add_guard_condition(args.context->get_interrupt_guard_condition(&wait_set_));
|
||||
|
||||
// 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(
|
||||
&wait_set_, 0, 2, 0, 0, 0, allocator) != RCL_RET_OK)
|
||||
{
|
||||
// Store the context for later use.
|
||||
context_ = args.context;
|
||||
|
||||
ret = rcl_wait_set_init(&wait_set_, 0, 2, 0, 0, 0, context_->get_rcl_context().get(), allocator);
|
||||
if (RCL_RET_OK != ret) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"failed to create wait set: %s", rcl_get_error_string().str);
|
||||
@@ -102,9 +103,8 @@ Executor::~Executor()
|
||||
rcl_reset_error();
|
||||
}
|
||||
// Remove and release the sigint guard condition
|
||||
memory_strategy_->remove_guard_condition(
|
||||
rclcpp::get_sigint_guard_condition(&wait_set_));
|
||||
rclcpp::release_sigint_guard_condition(&wait_set_);
|
||||
memory_strategy_->remove_guard_condition(context_->get_interrupt_guard_condition(&wait_set_));
|
||||
context_->release_interrupt_guard_condition(&wait_set_, std::nothrow);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -281,6 +281,9 @@ Executor::execute_any_executable(AnyExecutable & any_exec)
|
||||
if (any_exec.client) {
|
||||
execute_client(any_exec.client);
|
||||
}
|
||||
if (any_exec.waitable) {
|
||||
any_exec.waitable->execute();
|
||||
}
|
||||
// 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
|
||||
@@ -427,6 +430,7 @@ Executor::wait_for_work(std::chrono::nanoseconds timeout)
|
||||
throw std::runtime_error("Couldn't clear wait set");
|
||||
}
|
||||
|
||||
// The size of waitables are accounted for in size of the other entities
|
||||
rcl_ret_t ret = rcl_wait_set_resize(
|
||||
&wait_set_, memory_strategy_->number_of_ready_subscriptions(),
|
||||
memory_strategy_->number_of_guard_conditions(), memory_strategy_->number_of_ready_timers(),
|
||||
@@ -549,6 +553,11 @@ Executor::get_next_ready_executable(AnyExecutable & any_executable)
|
||||
if (any_executable.client) {
|
||||
return true;
|
||||
}
|
||||
// Check the waitables to see if there are any that are ready
|
||||
memory_strategy_->get_next_waitable(any_executable, weak_nodes_);
|
||||
if (any_executable.waitable) {
|
||||
return true;
|
||||
}
|
||||
// If there is no ready executable, return a null ptr
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -70,11 +70,11 @@ MultiThreadedExecutor::get_number_of_threads()
|
||||
void
|
||||
MultiThreadedExecutor::run(size_t)
|
||||
{
|
||||
while (rclcpp::ok() && spinning.load()) {
|
||||
while (rclcpp::ok(this->context_) && spinning.load()) {
|
||||
executor::AnyExecutable any_exec;
|
||||
{
|
||||
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
|
||||
if (!rclcpp::ok() || !spinning.load()) {
|
||||
if (!rclcpp::ok(this->context_) || !spinning.load()) {
|
||||
return;
|
||||
}
|
||||
if (!get_next_executable(any_exec)) {
|
||||
@@ -82,7 +82,6 @@ MultiThreadedExecutor::run(size_t)
|
||||
}
|
||||
if (any_exec.timer) {
|
||||
// Guard against multiple threads getting the same timer.
|
||||
std::lock_guard<std::mutex> lock(scheduled_timers_mutex_);
|
||||
if (scheduled_timers_.count(any_exec.timer) != 0) {
|
||||
continue;
|
||||
}
|
||||
@@ -96,7 +95,7 @@ MultiThreadedExecutor::run(size_t)
|
||||
execute_any_executable(any_exec);
|
||||
|
||||
if (any_exec.timer) {
|
||||
std::lock_guard<std::mutex> lock(scheduled_timers_mutex_);
|
||||
std::lock_guard<std::mutex> wait_lock(wait_mutex_);
|
||||
auto it = scheduled_timers_.find(any_exec.timer);
|
||||
if (it != scheduled_timers_.end()) {
|
||||
scheduled_timers_.erase(it);
|
||||
|
||||
@@ -30,7 +30,7 @@ SingleThreadedExecutor::spin()
|
||||
throw std::runtime_error("spin() called while already spinning");
|
||||
}
|
||||
RCLCPP_SCOPE_EXIT(this->spinning.store(false); );
|
||||
while (rclcpp::ok() && spinning.load()) {
|
||||
while (rclcpp::ok(this->context_) && spinning.load()) {
|
||||
rclcpp::executor::AnyExecutable any_executable;
|
||||
if (get_next_executable(any_executable)) {
|
||||
execute_any_executable(any_executable);
|
||||
|
||||
@@ -35,22 +35,32 @@ namespace rclcpp
|
||||
namespace graph_listener
|
||||
{
|
||||
|
||||
GraphListener::GraphListener()
|
||||
: is_started_(false), is_shutdown_(false), shutdown_guard_condition_(nullptr)
|
||||
GraphListener::GraphListener(std::shared_ptr<rclcpp::Context> parent_context)
|
||||
: parent_context_(parent_context),
|
||||
is_started_(false),
|
||||
is_shutdown_(false),
|
||||
interrupt_guard_condition_context_(nullptr),
|
||||
shutdown_guard_condition_(nullptr)
|
||||
{
|
||||
// TODO(wjwwood): make a guard condition class in rclcpp so this can be tracked
|
||||
// automatically with the rcl guard condition
|
||||
// hold on to this context to prevent it from going out of scope while this
|
||||
// guard condition is using it.
|
||||
interrupt_guard_condition_context_ = parent_context->get_rcl_context();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
&interrupt_guard_condition_,
|
||||
interrupt_guard_condition_context_.get(),
|
||||
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::get_sigint_guard_condition(&wait_set_);
|
||||
shutdown_guard_condition_ = parent_context->get_interrupt_guard_condition(&wait_set_);
|
||||
}
|
||||
|
||||
GraphListener::~GraphListener()
|
||||
{
|
||||
this->shutdown();
|
||||
this->shutdown(std::nothrow);
|
||||
}
|
||||
|
||||
void
|
||||
@@ -69,6 +79,7 @@ GraphListener::start_if_not_started()
|
||||
0, // number_of_timers
|
||||
0, // number_of_clients
|
||||
0, // number_of_services
|
||||
this->parent_context_->get_rcl_context().get(),
|
||||
rcl_get_default_allocator());
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to initialize wait set");
|
||||
@@ -81,7 +92,8 @@ GraphListener::start_if_not_started()
|
||||
[weak_this]() {
|
||||
auto shared_this = weak_this.lock();
|
||||
if (shared_this) {
|
||||
shared_this->shutdown();
|
||||
// should not throw from on_shutdown if it can be avoided
|
||||
shared_this->shutdown(std::nothrow);
|
||||
}
|
||||
});
|
||||
// Start the listener thread.
|
||||
@@ -130,8 +142,10 @@ GraphListener::run_loop()
|
||||
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(&wait_set_, 0, node_graph_interfaces_.size() + 2, 0, 0, 0);
|
||||
const size_t node_graph_interfaces_size = node_graph_interfaces_.size();
|
||||
// Add 2 for the interrupt and shutdown guard conditions
|
||||
if (wait_set_.size_of_guard_conditions < (node_graph_interfaces_size + 2)) {
|
||||
ret = rcl_wait_set_resize(&wait_set_, 0, node_graph_interfaces_size + 2, 0, 0, 0);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to resize wait set");
|
||||
}
|
||||
@@ -142,17 +156,21 @@ GraphListener::run_loop()
|
||||
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_);
|
||||
ret = rcl_wait_set_add_guard_condition(&wait_set_, &interrupt_guard_condition_, NULL);
|
||||
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_);
|
||||
size_t shutdown_guard_condition_index = 0u;
|
||||
ret = rcl_wait_set_add_guard_condition(
|
||||
&wait_set_, shutdown_guard_condition_, &shutdown_guard_condition_index);
|
||||
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_) {
|
||||
std::vector<size_t> graph_gc_indexes(node_graph_interfaces_size, 0u);
|
||||
for (size_t i = 0u; i < node_graph_interfaces_size; ++i) {
|
||||
auto node_ptr = node_graph_interfaces_[i];
|
||||
// Only wait on graph changes if some user of the node is watching.
|
||||
if (node_ptr->count_graph_users() == 0) {
|
||||
continue;
|
||||
@@ -162,7 +180,7 @@ GraphListener::run_loop()
|
||||
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);
|
||||
ret = rcl_wait_set_add_guard_condition(&wait_set_, graph_gc, &graph_gc_indexes[i]);
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to add graph guard condition to wait set");
|
||||
}
|
||||
@@ -177,23 +195,18 @@ GraphListener::run_loop()
|
||||
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;
|
||||
}
|
||||
}
|
||||
bool shutdown_guard_condition_triggered =
|
||||
(shutdown_guard_condition_ == wait_set_.guard_conditions[shutdown_guard_condition_index]);
|
||||
// Notify nodes who's guard conditions are set (triggered).
|
||||
for (const auto node_ptr : node_graph_interfaces_) {
|
||||
for (size_t i = 0u; i < node_graph_interfaces_size; ++i) {
|
||||
const auto node_ptr = node_graph_interfaces_[i];
|
||||
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 (graph_gc == wait_set_.guard_conditions[graph_gc_indexes[i]]) {
|
||||
node_ptr->notify_graph_change();
|
||||
}
|
||||
if (shutdown_guard_condition_triggered) {
|
||||
// If shutdown, then notify the node of this as well.
|
||||
@@ -327,7 +340,7 @@ GraphListener::remove_node(rclcpp::node_interfaces::NodeGraphInterface * node_gr
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::shutdown()
|
||||
GraphListener::__shutdown(bool should_throw)
|
||||
{
|
||||
std::lock_guard<std::mutex> shutdown_lock(shutdown_mutex_);
|
||||
if (!is_shutdown_.exchange(true)) {
|
||||
@@ -336,11 +349,16 @@ GraphListener::shutdown()
|
||||
listener_thread_.join();
|
||||
}
|
||||
rcl_ret_t ret = rcl_guard_condition_fini(&interrupt_guard_condition_);
|
||||
interrupt_guard_condition_context_.reset(); // release context guard condition was using
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to finalize interrupt guard condition");
|
||||
}
|
||||
if (shutdown_guard_condition_) {
|
||||
rclcpp::release_sigint_guard_condition(&wait_set_);
|
||||
if (should_throw) {
|
||||
parent_context_->release_interrupt_guard_condition(&wait_set_);
|
||||
} else {
|
||||
parent_context_->release_interrupt_guard_condition(&wait_set_, std::nothrow);
|
||||
}
|
||||
shutdown_guard_condition_ = nullptr;
|
||||
}
|
||||
if (is_started_) {
|
||||
@@ -352,6 +370,29 @@ GraphListener::shutdown()
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::shutdown()
|
||||
{
|
||||
this->__shutdown(true);
|
||||
}
|
||||
|
||||
void
|
||||
GraphListener::shutdown(const std::nothrow_t &) noexcept
|
||||
{
|
||||
try {
|
||||
this->__shutdown(false);
|
||||
} catch (const std::exception & exc) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"caught %s exception when shutting down GraphListener: %s",
|
||||
rmw::impl::cpp::demangle(exc).c_str(), exc.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"caught unknown exception when shutting down GraphListener");
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
GraphListener::is_shutdown()
|
||||
{
|
||||
|
||||
86
rclcpp/src/rclcpp/init_options.cpp
Normal file
86
rclcpp/src/rclcpp/init_options.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
// Copyright 2018 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/init_options.hpp"
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
InitOptions::InitOptions(rcl_allocator_t allocator)
|
||||
: init_options_(new rcl_init_options_t)
|
||||
{
|
||||
*init_options_ = rcl_get_zero_initialized_init_options();
|
||||
rcl_ret_t ret = rcl_init_options_init(init_options_.get(), allocator);
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to initialized rcl init options");
|
||||
}
|
||||
}
|
||||
|
||||
InitOptions::InitOptions(const rcl_init_options_t & init_options)
|
||||
: init_options_(new rcl_init_options_t)
|
||||
{
|
||||
*init_options_ = rcl_get_zero_initialized_init_options();
|
||||
rcl_ret_t ret = rcl_init_options_copy(&init_options, init_options_.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to copy rcl init options");
|
||||
}
|
||||
}
|
||||
|
||||
InitOptions::InitOptions(const InitOptions & other)
|
||||
: InitOptions(*other.get_rcl_init_options())
|
||||
{}
|
||||
|
||||
InitOptions &
|
||||
InitOptions::operator=(const InitOptions & other)
|
||||
{
|
||||
if (this != &other) {
|
||||
this->finalize_init_options();
|
||||
rcl_ret_t ret = rcl_init_options_copy(other.get_rcl_init_options(), init_options_.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to copy rcl init options");
|
||||
}
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
InitOptions::~InitOptions()
|
||||
{
|
||||
this->finalize_init_options();
|
||||
}
|
||||
|
||||
void
|
||||
InitOptions::finalize_init_options()
|
||||
{
|
||||
if (init_options_) {
|
||||
rcl_ret_t ret = rcl_init_options_fini(init_options_.get());
|
||||
if (RCL_RET_OK != ret) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"failed to finalize rcl init options: %s", rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
*init_options_ = rcl_get_zero_initialized_init_options();
|
||||
}
|
||||
}
|
||||
|
||||
const rcl_init_options_t *
|
||||
InitOptions::get_rcl_init_options() const
|
||||
{
|
||||
return init_options_.get();
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -56,6 +56,12 @@ IntraProcessManager::matches_any_publishers(const rmw_gid_t * id) const
|
||||
return impl_->matches_any_publishers(id);
|
||||
}
|
||||
|
||||
size_t
|
||||
IntraProcessManager::get_subscription_count(uint64_t intra_process_publisher_id) const
|
||||
{
|
||||
return impl_->get_subscription_count(intra_process_publisher_id);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
IntraProcessManager::get_next_unique_id()
|
||||
{
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
#include "rclcpp/logger.hpp"
|
||||
|
||||
#include "rclcpp/logging.hpp"
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
rclcpp::Logger
|
||||
Logger
|
||||
get_logger(const std::string & name)
|
||||
{
|
||||
#if RCLCPP_LOGGING_ENABLED
|
||||
@@ -30,4 +32,16 @@ get_logger(const std::string & name)
|
||||
#endif
|
||||
}
|
||||
|
||||
Logger
|
||||
get_node_logger(const rcl_node_t * node)
|
||||
{
|
||||
const char * logger_name = rcl_node_get_logger_name(node);
|
||||
if (nullptr == logger_name) {
|
||||
auto logger = rclcpp::get_logger("rclcpp");
|
||||
RCLCPP_ERROR(logger, "failed to get logger name from node at address %p", node);
|
||||
return logger;
|
||||
}
|
||||
return rclcpp::get_logger(logger_name);
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
@@ -200,3 +200,29 @@ MemoryStrategy::get_group_by_client(
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr
|
||||
MemoryStrategy::get_group_by_waitable(
|
||||
rclcpp::Waitable::SharedPtr waitable,
|
||||
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_waitable : group->get_waitable_ptrs()) {
|
||||
auto group_waitable = weak_waitable.lock();
|
||||
if (group_waitable && group_waitable == waitable) {
|
||||
return group;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -29,61 +29,152 @@
|
||||
#include "rclcpp/node_interfaces/node_logging.hpp"
|
||||
#include "rclcpp/node_interfaces/node_parameters.hpp"
|
||||
#include "rclcpp/node_interfaces/node_services.hpp"
|
||||
#include "rclcpp/node_interfaces/node_time_source.hpp"
|
||||
#include "rclcpp/node_interfaces/node_timers.hpp"
|
||||
#include "rclcpp/node_interfaces/node_topics.hpp"
|
||||
#include "rclcpp/node_interfaces/node_waitables.hpp"
|
||||
|
||||
#include "rmw/validate_namespace.h"
|
||||
|
||||
using rclcpp::Node;
|
||||
using rclcpp::NodeOptions;
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
|
||||
RCLCPP_LOCAL
|
||||
std::string
|
||||
extend_sub_namespace(const std::string & existing_sub_namespace, const std::string & extension)
|
||||
{
|
||||
// Assumption is that the existing_sub_namespace does not need checking
|
||||
// because it would be checked already when it was set with this function.
|
||||
|
||||
// check if the new sub-namespace extension is absolute
|
||||
if (extension.front() == '/') {
|
||||
throw rclcpp::exceptions::NameValidationError(
|
||||
"sub_namespace",
|
||||
extension.c_str(),
|
||||
"a sub-namespace should not have a leading /",
|
||||
0);
|
||||
}
|
||||
|
||||
std::string new_sub_namespace;
|
||||
if (existing_sub_namespace.empty()) {
|
||||
new_sub_namespace = extension;
|
||||
} else {
|
||||
new_sub_namespace = existing_sub_namespace + "/" + extension;
|
||||
}
|
||||
|
||||
// remove any trailing `/` so that new extensions do no result in `//`
|
||||
if (new_sub_namespace.back() == '/') {
|
||||
new_sub_namespace = new_sub_namespace.substr(0, new_sub_namespace.size() - 1);
|
||||
}
|
||||
|
||||
return new_sub_namespace;
|
||||
}
|
||||
|
||||
RCLCPP_LOCAL
|
||||
std::string
|
||||
create_effective_namespace(const std::string & node_namespace, const std::string & sub_namespace)
|
||||
{
|
||||
// Assumption is that both the node_namespace and sub_namespace are conforming
|
||||
// and do not need trimming of `/` and other things, as they were validated
|
||||
// in other functions already.
|
||||
|
||||
if (node_namespace.back() == '/') {
|
||||
// this is the special case where node_namespace is just `/`
|
||||
return node_namespace + sub_namespace;
|
||||
} else {
|
||||
return node_namespace + "/" + sub_namespace;
|
||||
}
|
||||
}
|
||||
|
||||
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(),
|
||||
{},
|
||||
{},
|
||||
true,
|
||||
use_intra_process_comms,
|
||||
true)
|
||||
{}
|
||||
const NodeOptions & options)
|
||||
: Node(node_name, "", options)
|
||||
{
|
||||
}
|
||||
|
||||
Node::Node(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::vector<std::string> & arguments,
|
||||
const std::vector<rclcpp::Parameter> & initial_parameters,
|
||||
bool use_global_arguments,
|
||||
bool use_intra_process_comms,
|
||||
bool start_parameter_services)
|
||||
const NodeOptions & options)
|
||||
: node_base_(new rclcpp::node_interfaces::NodeBase(
|
||||
node_name, namespace_, context, arguments, use_global_arguments)),
|
||||
node_name, namespace_, options)),
|
||||
node_graph_(new rclcpp::node_interfaces::NodeGraph(node_base_.get())),
|
||||
node_logging_(new rclcpp::node_interfaces::NodeLogging(node_base_.get())),
|
||||
node_timers_(new rclcpp::node_interfaces::NodeTimers(node_base_.get())),
|
||||
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_base_,
|
||||
node_topics_,
|
||||
node_services_,
|
||||
initial_parameters,
|
||||
use_intra_process_comms,
|
||||
start_parameter_services
|
||||
)),
|
||||
node_clock_(new rclcpp::node_interfaces::NodeClock(
|
||||
node_base_,
|
||||
node_topics_,
|
||||
node_graph_,
|
||||
node_services_
|
||||
node_services_,
|
||||
node_logging_
|
||||
)),
|
||||
use_intra_process_comms_(use_intra_process_comms)
|
||||
node_parameters_(new rclcpp::node_interfaces::NodeParameters(
|
||||
node_base_,
|
||||
node_topics_,
|
||||
node_services_,
|
||||
node_clock_,
|
||||
options.initial_parameters(),
|
||||
options.use_intra_process_comms(),
|
||||
options.start_parameter_services(),
|
||||
options.start_parameter_event_publisher(),
|
||||
options.parameter_event_qos_profile()
|
||||
)),
|
||||
node_time_source_(new rclcpp::node_interfaces::NodeTimeSource(
|
||||
node_base_,
|
||||
node_topics_,
|
||||
node_graph_,
|
||||
node_services_,
|
||||
node_logging_,
|
||||
node_clock_,
|
||||
node_parameters_
|
||||
)),
|
||||
node_waitables_(new rclcpp::node_interfaces::NodeWaitables(node_base_.get())),
|
||||
node_options_(options),
|
||||
sub_namespace_(""),
|
||||
effective_namespace_(create_effective_namespace(this->get_namespace(), sub_namespace_))
|
||||
{
|
||||
}
|
||||
|
||||
Node::Node(
|
||||
const Node & other,
|
||||
const std::string & sub_namespace)
|
||||
: node_base_(other.node_base_),
|
||||
node_graph_(other.node_graph_),
|
||||
node_logging_(other.node_logging_),
|
||||
node_timers_(other.node_timers_),
|
||||
node_topics_(other.node_topics_),
|
||||
node_services_(other.node_services_),
|
||||
node_clock_(other.node_clock_),
|
||||
node_parameters_(other.node_parameters_),
|
||||
node_options_(other.node_options_),
|
||||
sub_namespace_(extend_sub_namespace(other.get_sub_namespace(), sub_namespace)),
|
||||
effective_namespace_(create_effective_namespace(other.get_namespace(), sub_namespace_))
|
||||
{
|
||||
// Validate new effective namespace.
|
||||
int validation_result;
|
||||
size_t invalid_index;
|
||||
rmw_ret_t rmw_ret =
|
||||
rmw_validate_namespace(effective_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 subnode namespace");
|
||||
}
|
||||
throw_from_rcl_error(RCL_RET_ERROR, "failed to validate subnode namespace");
|
||||
}
|
||||
|
||||
if (validation_result != RMW_NAMESPACE_VALID) {
|
||||
throw rclcpp::exceptions::InvalidNamespaceError(
|
||||
effective_namespace_.c_str(),
|
||||
rmw_namespace_validation_result_string(validation_result),
|
||||
invalid_index);
|
||||
}
|
||||
}
|
||||
|
||||
Node::~Node()
|
||||
{}
|
||||
|
||||
@@ -99,6 +190,12 @@ Node::get_namespace() const
|
||||
return node_base_->get_namespace();
|
||||
}
|
||||
|
||||
const char *
|
||||
Node::get_fully_qualified_name() const
|
||||
{
|
||||
return node_base_->get_fully_qualified_name();
|
||||
}
|
||||
|
||||
rclcpp::Logger
|
||||
Node::get_logger() const
|
||||
{
|
||||
@@ -253,6 +350,18 @@ Node::get_node_graph_interface()
|
||||
return node_graph_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr
|
||||
Node::get_node_logging_interface()
|
||||
{
|
||||
return node_logging_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeTimeSourceInterface::SharedPtr
|
||||
Node::get_node_time_source_interface()
|
||||
{
|
||||
return node_time_source_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeTimersInterface::SharedPtr
|
||||
Node::get_node_timers_interface()
|
||||
{
|
||||
@@ -276,3 +385,35 @@ Node::get_node_parameters_interface()
|
||||
{
|
||||
return node_parameters_;
|
||||
}
|
||||
|
||||
rclcpp::node_interfaces::NodeWaitablesInterface::SharedPtr
|
||||
Node::get_node_waitables_interface()
|
||||
{
|
||||
return node_waitables_;
|
||||
}
|
||||
|
||||
const std::string &
|
||||
Node::get_sub_namespace() const
|
||||
{
|
||||
return this->sub_namespace_;
|
||||
}
|
||||
|
||||
const std::string &
|
||||
Node::get_effective_namespace() const
|
||||
{
|
||||
return this->effective_namespace_;
|
||||
}
|
||||
|
||||
Node::SharedPtr
|
||||
Node::create_sub_node(const std::string & sub_namespace)
|
||||
{
|
||||
// Cannot use make_shared<Node>() here as it requires the constructor to be
|
||||
// public, and this constructor is intentionally protected instead.
|
||||
return std::shared_ptr<Node>(new Node(*this, sub_namespace));
|
||||
}
|
||||
|
||||
const NodeOptions &
|
||||
Node::get_node_options() const
|
||||
{
|
||||
return this->node_options_;
|
||||
}
|
||||
|
||||
@@ -22,8 +22,8 @@
|
||||
#include "rcl/arguments.h"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rcutils/logging_macros.h"
|
||||
#include "rmw/validate_node_name.h"
|
||||
#include "rmw/validate_namespace.h"
|
||||
#include "rmw/validate_node_name.h"
|
||||
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
|
||||
@@ -32,10 +32,8 @@ using rclcpp::node_interfaces::NodeBase;
|
||||
NodeBase::NodeBase(
|
||||
const std::string & node_name,
|
||||
const std::string & namespace_,
|
||||
rclcpp::Context::SharedPtr context,
|
||||
const std::vector<std::string> & arguments,
|
||||
bool use_global_arguments)
|
||||
: context_(context),
|
||||
const rclcpp::NodeOptions & options)
|
||||
: context_(options.context()),
|
||||
node_handle_(nullptr),
|
||||
default_callback_group_(nullptr),
|
||||
associated_with_executor_(false),
|
||||
@@ -43,7 +41,8 @@ NodeBase::NodeBase(
|
||||
{
|
||||
// Setup the guard condition that is notified when changes occur in the graph.
|
||||
rcl_guard_condition_options_t guard_condition_options = rcl_guard_condition_get_default_options();
|
||||
rcl_ret_t ret = rcl_guard_condition_init(¬ify_guard_condition_, guard_condition_options);
|
||||
rcl_ret_t ret = rcl_guard_condition_init(
|
||||
¬ify_guard_condition_, options.context()->get_rcl_context().get(), guard_condition_options);
|
||||
if (ret != RCL_RET_OK) {
|
||||
throw_from_rcl_error(ret, "failed to create interrupt guard condition");
|
||||
}
|
||||
@@ -58,73 +57,16 @@ NodeBase::NodeBase(
|
||||
}
|
||||
};
|
||||
|
||||
// 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.
|
||||
std::unique_ptr<rcl_node_t> rcl_node(new rcl_node_t(rcl_get_zero_initialized_node()));
|
||||
|
||||
rcl_node_options_t options = rcl_node_get_default_options();
|
||||
std::unique_ptr<const char *[]> c_args;
|
||||
if (!arguments.empty()) {
|
||||
c_args.reset(new const char *[arguments.size()]);
|
||||
for (std::size_t i = 0; i < arguments.size(); ++i) {
|
||||
c_args[i] = arguments[i].c_str();
|
||||
}
|
||||
}
|
||||
// TODO(sloretz) Pass an allocator to argument parsing
|
||||
if (arguments.size() > std::numeric_limits<int>::max()) {
|
||||
throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "Too many args");
|
||||
}
|
||||
ret = rcl_parse_arguments(
|
||||
static_cast<int>(arguments.size()), c_args.get(), rcl_get_default_allocator(),
|
||||
&(options.arguments));
|
||||
if (RCL_RET_OK != ret) {
|
||||
finalize_notify_guard_condition();
|
||||
throw_from_rcl_error(ret, "failed to parse arguments");
|
||||
}
|
||||
|
||||
options.use_global_arguments = use_global_arguments;
|
||||
// TODO(wjwwood): pass the Allocator to the options
|
||||
options.domain_id = domain_id;
|
||||
|
||||
ret = rcl_node_init(rcl_node.get(), node_name.c_str(), namespace_.c_str(), &options);
|
||||
ret = rcl_node_init(
|
||||
rcl_node.get(),
|
||||
node_name.c_str(), namespace_.c_str(),
|
||||
options.context()->get_rcl_context().get(), options.get_rcl_node_options());
|
||||
if (ret != RCL_RET_OK) {
|
||||
// Finalize the interrupt guard condition.
|
||||
finalize_notify_guard_condition();
|
||||
// Finalize previously allocated node arguments
|
||||
if (RCL_RET_OK != rcl_arguments_fini(&options.arguments)) {
|
||||
// Print message because exception will be thrown later in this code block
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Failed to fini arguments during error handling: %s", rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
|
||||
if (ret == RCL_RET_NODE_INVALID_NAME) {
|
||||
rcl_reset_error(); // discard rcl_node_init error
|
||||
@@ -191,15 +133,6 @@ NodeBase::NodeBase(
|
||||
|
||||
// Indicate the notify_guard_condition is now valid.
|
||||
notify_guard_condition_is_valid_ = true;
|
||||
|
||||
// Finalize previously allocated node arguments
|
||||
if (RCL_RET_OK != rcl_arguments_fini(&options.arguments)) {
|
||||
// print message because throwing would prevent the destructor from being called
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"Failed to fini arguments: %s", rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
}
|
||||
|
||||
NodeBase::~NodeBase()
|
||||
@@ -228,6 +161,12 @@ NodeBase::get_namespace() const
|
||||
return rcl_node_get_namespace(node_handle_.get());
|
||||
}
|
||||
|
||||
const char *
|
||||
NodeBase::get_fully_qualified_name() const
|
||||
{
|
||||
return rcl_node_get_fully_qualified_name(node_handle_.get());
|
||||
}
|
||||
|
||||
rclcpp::Context::SharedPtr
|
||||
NodeBase::get_context()
|
||||
{
|
||||
|
||||
@@ -23,20 +23,15 @@ NodeClock::NodeClock(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services)
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging)
|
||||
: node_base_(node_base),
|
||||
node_topics_(node_topics),
|
||||
node_graph_(node_graph),
|
||||
node_services_(node_services),
|
||||
node_logging_(node_logging),
|
||||
ros_clock_(std::make_shared<rclcpp::Clock>(RCL_ROS_TIME))
|
||||
{
|
||||
time_source_.attachNode(
|
||||
node_base_,
|
||||
node_topics_,
|
||||
node_graph_,
|
||||
node_services_);
|
||||
time_source_.attachClock(ros_clock_);
|
||||
}
|
||||
{}
|
||||
|
||||
NodeClock::~NodeClock()
|
||||
{}
|
||||
|
||||
@@ -30,7 +30,9 @@ 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>()),
|
||||
graph_listener_(
|
||||
node_base->get_context()->get_sub_context<GraphListener>(node_base->get_context())
|
||||
),
|
||||
should_add_to_graph_listener_(true),
|
||||
graph_users_count_(0)
|
||||
{}
|
||||
@@ -334,8 +336,8 @@ NodeGraph::wait_for_graph_change(
|
||||
throw EventNotRegisteredError();
|
||||
}
|
||||
}
|
||||
auto pred = [&event]() {
|
||||
return event->check() || !rclcpp::ok();
|
||||
auto pred = [&event, context = node_base_->get_context()]() {
|
||||
return event->check() || !rclcpp::ok(context);
|
||||
};
|
||||
std::unique_lock<std::mutex> graph_lock(graph_mutex_);
|
||||
if (!pred()) {
|
||||
|
||||
@@ -36,9 +36,13 @@ NodeParameters::NodeParameters(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
|
||||
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
|
||||
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
|
||||
const rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
|
||||
const std::vector<rclcpp::Parameter> & initial_parameters,
|
||||
bool use_intra_process,
|
||||
bool start_parameter_services)
|
||||
bool start_parameter_services,
|
||||
bool start_parameter_event_publisher,
|
||||
const rmw_qos_profile_t & parameter_event_qos_profile)
|
||||
: events_publisher_(nullptr), node_clock_(node_clock)
|
||||
{
|
||||
using MessageT = rcl_interfaces::msg::ParameterEvent;
|
||||
using PublisherT = rclcpp::Publisher<MessageT>;
|
||||
@@ -50,12 +54,14 @@ NodeParameters::NodeParameters(
|
||||
parameter_service_ = std::make_shared<ParameterService>(node_base, node_services, this);
|
||||
}
|
||||
|
||||
events_publisher_ = rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
|
||||
node_topics.get(),
|
||||
"parameter_events",
|
||||
rmw_qos_profile_parameter_events,
|
||||
use_intra_process,
|
||||
allocator);
|
||||
if (start_parameter_event_publisher) {
|
||||
events_publisher_ = rclcpp::create_publisher<MessageT, AllocatorT, PublisherT>(
|
||||
node_topics.get(),
|
||||
"parameter_events",
|
||||
parameter_event_qos_profile,
|
||||
use_intra_process,
|
||||
allocator);
|
||||
}
|
||||
|
||||
// Get the node options
|
||||
const rcl_node_t * node = node_base->get_rcl_node_handle();
|
||||
@@ -64,7 +70,7 @@ NodeParameters::NodeParameters(
|
||||
}
|
||||
const rcl_node_options_t * options = rcl_node_get_options(node);
|
||||
if (nullptr == options) {
|
||||
throw std::runtime_error("Need valid node options NodeParameters");
|
||||
throw std::runtime_error("Need valid node options in NodeParameters");
|
||||
}
|
||||
|
||||
// Get paths to yaml files containing initial parameter values
|
||||
@@ -93,7 +99,8 @@ NodeParameters::NodeParameters(
|
||||
|
||||
// global before local so that local overwrites global
|
||||
if (options->use_global_arguments) {
|
||||
get_yaml_paths(rcl_get_global_arguments());
|
||||
auto context_ptr = node_base->get_context()->get_rcl_context();
|
||||
get_yaml_paths(&(context_ptr->global_arguments));
|
||||
}
|
||||
get_yaml_paths(&(options->arguments));
|
||||
|
||||
@@ -104,11 +111,11 @@ NodeParameters::NodeParameters(
|
||||
// Should never happen
|
||||
throw std::runtime_error("Node name and namespace were not set");
|
||||
}
|
||||
std::string combined_name;
|
||||
|
||||
if ('/' == node_namespace.at(node_namespace.size() - 1)) {
|
||||
combined_name = node_namespace + node_name;
|
||||
combined_name_ = node_namespace + node_name;
|
||||
} else {
|
||||
combined_name = node_namespace + '/' + node_name;
|
||||
combined_name_ = node_namespace + '/' + node_name;
|
||||
}
|
||||
|
||||
std::map<std::string, rclcpp::Parameter> parameters;
|
||||
@@ -130,7 +137,7 @@ NodeParameters::NodeParameters(
|
||||
|
||||
rclcpp::ParameterMap initial_map = rclcpp::parameter_map_from(yaml_params);
|
||||
rcl_yaml_node_struct_fini(yaml_params);
|
||||
auto iter = initial_map.find(combined_name);
|
||||
auto iter = initial_map.find(combined_name_);
|
||||
if (initial_map.end() == iter) {
|
||||
continue;
|
||||
}
|
||||
@@ -170,7 +177,7 @@ NodeParameters::set_parameters(
|
||||
const std::vector<rclcpp::Parameter> & parameters)
|
||||
{
|
||||
std::vector<rcl_interfaces::msg::SetParametersResult> results;
|
||||
for (auto p : parameters) {
|
||||
for (const auto & p : parameters) {
|
||||
auto result = set_parameters_atomically({{p}});
|
||||
results.push_back(result);
|
||||
}
|
||||
@@ -185,6 +192,8 @@ NodeParameters::set_parameters_atomically(
|
||||
std::map<std::string, rclcpp::Parameter> tmp_map;
|
||||
auto parameter_event = std::make_shared<rcl_interfaces::msg::ParameterEvent>();
|
||||
|
||||
parameter_event->node = combined_name_;
|
||||
|
||||
// TODO(jacquelinekay): handle parameter constraints
|
||||
rcl_interfaces::msg::SetParametersResult result;
|
||||
if (parameters_callback_) {
|
||||
@@ -197,7 +206,7 @@ NodeParameters::set_parameters_atomically(
|
||||
return result;
|
||||
}
|
||||
|
||||
for (auto p : parameters) {
|
||||
for (const auto & p : parameters) {
|
||||
if (p.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
|
||||
if (parameters_.find(p.get_name()) != parameters_.end()) {
|
||||
// case: parameter was set before, and input is "NOT_SET"
|
||||
@@ -220,7 +229,7 @@ NodeParameters::set_parameters_atomically(
|
||||
tmp_map.insert(parameters_.begin(), parameters_.end());
|
||||
|
||||
// remove explicitly deleted parameters
|
||||
for (auto p : parameters) {
|
||||
for (const auto & p : parameters) {
|
||||
if (p.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
|
||||
tmp_map.erase(p.get_name());
|
||||
}
|
||||
@@ -228,7 +237,11 @@ NodeParameters::set_parameters_atomically(
|
||||
|
||||
std::swap(tmp_map, parameters_);
|
||||
|
||||
events_publisher_->publish(parameter_event);
|
||||
// events_publisher_ may be nullptr if it was disabled in constructor
|
||||
if (nullptr != events_publisher_) {
|
||||
parameter_event->stamp = node_clock_->get_clock()->now();
|
||||
events_publisher_->publish(parameter_event);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -278,6 +291,27 @@ NodeParameters::get_parameter(
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
NodeParameters::get_parameters_by_prefix(
|
||||
const std::string & prefix,
|
||||
std::map<std::string, rclcpp::Parameter> & parameters) const
|
||||
{
|
||||
std::string prefix_with_dot = prefix + ".";
|
||||
bool ret = false;
|
||||
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
for (const auto & param : parameters_) {
|
||||
if (param.first.find(prefix_with_dot) == 0 && param.first.length() > prefix_with_dot.length()) {
|
||||
// Found one!
|
||||
parameters[param.first.substr(prefix_with_dot.length())] = param.second;
|
||||
ret = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<rcl_interfaces::msg::ParameterDescriptor>
|
||||
NodeParameters::describe_parameters(const std::vector<std::string> & names) const
|
||||
{
|
||||
|
||||
50
rclcpp/src/rclcpp/node_interfaces/node_time_source.cpp
Normal file
50
rclcpp/src/rclcpp/node_interfaces/node_time_source.cpp
Normal file
@@ -0,0 +1,50 @@
|
||||
// Copyright 2018 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_time_source.hpp"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
using rclcpp::node_interfaces::NodeTimeSource;
|
||||
|
||||
NodeTimeSource::NodeTimeSource(
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph,
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services,
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging,
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters)
|
||||
: node_base_(node_base),
|
||||
node_topics_(node_topics),
|
||||
node_graph_(node_graph),
|
||||
node_services_(node_services),
|
||||
node_logging_(node_logging),
|
||||
node_clock_(node_clock),
|
||||
node_parameters_(node_parameters)
|
||||
{
|
||||
time_source_.attachNode(
|
||||
node_base_,
|
||||
node_topics_,
|
||||
node_graph_,
|
||||
node_services_,
|
||||
node_logging_,
|
||||
node_clock_,
|
||||
node_parameters_);
|
||||
time_source_.attachClock(node_clock_->get_clock());
|
||||
}
|
||||
|
||||
NodeTimeSource::~NodeTimeSource()
|
||||
{}
|
||||
@@ -54,6 +54,7 @@ NodeTopics::create_publisher(
|
||||
publisher->setup_intra_process(
|
||||
intra_process_publisher_id,
|
||||
shared_publish_callback,
|
||||
ipm,
|
||||
publisher_options);
|
||||
}
|
||||
|
||||
|
||||
68
rclcpp/src/rclcpp/node_interfaces/node_waitables.cpp
Normal file
68
rclcpp/src/rclcpp/node_interfaces/node_waitables.cpp
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright 2018 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_waitables.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
using rclcpp::node_interfaces::NodeWaitables;
|
||||
|
||||
NodeWaitables::NodeWaitables(rclcpp::node_interfaces::NodeBaseInterface * node_base)
|
||||
: node_base_(node_base)
|
||||
{}
|
||||
|
||||
NodeWaitables::~NodeWaitables()
|
||||
{}
|
||||
|
||||
void
|
||||
NodeWaitables::add_waitable(
|
||||
rclcpp::Waitable::SharedPtr waitable_ptr,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group)
|
||||
{
|
||||
if (group) {
|
||||
if (!node_base_->callback_group_in_node(group)) {
|
||||
// TODO(jacobperron): use custom exception
|
||||
throw std::runtime_error("Cannot create waitable, group not in node.");
|
||||
}
|
||||
group->add_waitable(waitable_ptr);
|
||||
} else {
|
||||
node_base_->get_default_callback_group()->add_waitable(waitable_ptr);
|
||||
}
|
||||
|
||||
// Notify the executor that a new waitable was created using the parent Node.
|
||||
{
|
||||
auto notify_guard_condition_lock = node_base_->acquire_notify_guard_condition_lock();
|
||||
if (rcl_trigger_guard_condition(node_base_->get_notify_guard_condition()) != RCL_RET_OK) {
|
||||
throw std::runtime_error(
|
||||
std::string("Failed to notify wait set on waitable creation: ") +
|
||||
rmw_get_error_string().str
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
NodeWaitables::remove_waitable(
|
||||
rclcpp::Waitable::SharedPtr waitable_ptr,
|
||||
rclcpp::callback_group::CallbackGroup::SharedPtr group) noexcept
|
||||
{
|
||||
if (group) {
|
||||
if (!node_base_->callback_group_in_node(group)) {
|
||||
return;
|
||||
}
|
||||
group->remove_waitable(waitable_ptr);
|
||||
} else {
|
||||
node_base_->get_default_callback_group()->remove_waitable(waitable_ptr);
|
||||
}
|
||||
}
|
||||
267
rclcpp/src/rclcpp/node_options.cpp
Normal file
267
rclcpp/src/rclcpp/node_options.cpp
Normal file
@@ -0,0 +1,267 @@
|
||||
// Copyright 2019 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "rclcpp/node_options.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
|
||||
using rclcpp::exceptions::throw_from_rcl_error;
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
static
|
||||
void
|
||||
rcl_node_options_t_destructor(rcl_node_options_t * node_options)
|
||||
{
|
||||
if (node_options) {
|
||||
rcl_ret_t ret = rcl_node_options_fini(node_options);
|
||||
if (RCL_RET_OK != ret) {
|
||||
// Cannot throw here, as it may be called in the destructor.
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"failed to finalize rcl node options: %s", rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
}
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
NodeOptions::NodeOptions(rcl_allocator_t allocator)
|
||||
: node_options_(nullptr, detail::rcl_node_options_t_destructor), allocator_(allocator)
|
||||
{}
|
||||
|
||||
NodeOptions::NodeOptions(const NodeOptions & other)
|
||||
: node_options_(nullptr, detail::rcl_node_options_t_destructor)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::operator=(const NodeOptions & other)
|
||||
{
|
||||
if (this != &other) {
|
||||
this->context_ = other.context_;
|
||||
this->arguments_ = other.arguments_;
|
||||
this->initial_parameters_ = other.initial_parameters_;
|
||||
this->use_global_arguments_ = other.use_global_arguments_;
|
||||
this->use_intra_process_comms_ = other.use_intra_process_comms_;
|
||||
this->start_parameter_services_ = other.start_parameter_services_;
|
||||
this->allocator_ = other.allocator_;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
const rcl_node_options_t *
|
||||
NodeOptions::get_rcl_node_options() const
|
||||
{
|
||||
// If it is nullptr, create it on demand.
|
||||
if (!node_options_) {
|
||||
node_options_.reset(new rcl_node_options_t);
|
||||
*node_options_ = rcl_node_get_default_options();
|
||||
node_options_->allocator = this->allocator_;
|
||||
node_options_->use_global_arguments = this->use_global_arguments_;
|
||||
node_options_->domain_id = this->get_domain_id_from_env();
|
||||
|
||||
std::unique_ptr<const char *[]> c_args;
|
||||
if (!this->arguments_.empty()) {
|
||||
c_args.reset(new const char *[this->arguments_.size()]);
|
||||
for (std::size_t i = 0; i < this->arguments_.size(); ++i) {
|
||||
c_args[i] = this->arguments_[i].c_str();
|
||||
}
|
||||
}
|
||||
|
||||
if (this->arguments_.size() > std::numeric_limits<int>::max()) {
|
||||
throw_from_rcl_error(RCL_RET_INVALID_ARGUMENT, "Too many args");
|
||||
}
|
||||
|
||||
rmw_ret_t ret = rcl_parse_arguments(
|
||||
static_cast<int>(this->arguments_.size()), c_args.get(), this->allocator_,
|
||||
&(node_options_->arguments));
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
throw_from_rcl_error(ret, "failed to parse arguments");
|
||||
}
|
||||
}
|
||||
|
||||
return node_options_.get();
|
||||
}
|
||||
|
||||
rclcpp::Context::SharedPtr
|
||||
NodeOptions::context() const
|
||||
{
|
||||
return this->context_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::context(rclcpp::Context::SharedPtr context)
|
||||
{
|
||||
this->context_ = context;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::vector<std::string> &
|
||||
NodeOptions::arguments() const
|
||||
{
|
||||
return this->arguments_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::arguments(const std::vector<std::string> & arguments)
|
||||
{
|
||||
this->node_options_.reset(); // reset node options to make it be recreated on next access.
|
||||
this->arguments_ = arguments;
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Parameter> &
|
||||
NodeOptions::initial_parameters()
|
||||
{
|
||||
return this->initial_parameters_;
|
||||
}
|
||||
|
||||
const std::vector<rclcpp::Parameter> &
|
||||
NodeOptions::initial_parameters() const
|
||||
{
|
||||
return this->initial_parameters_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::initial_parameters(const std::vector<rclcpp::Parameter> & initial_parameters)
|
||||
{
|
||||
this->initial_parameters_ = initial_parameters;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const bool &
|
||||
NodeOptions::use_global_arguments() const
|
||||
{
|
||||
return this->node_options_->use_global_arguments;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::use_global_arguments(const bool & use_global_arguments)
|
||||
{
|
||||
this->node_options_.reset(); // reset node options to make it be recreated on next access.
|
||||
this->use_global_arguments_ = use_global_arguments;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const bool &
|
||||
NodeOptions::use_intra_process_comms() const
|
||||
{
|
||||
return this->use_intra_process_comms_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::use_intra_process_comms(const bool & use_intra_process_comms)
|
||||
{
|
||||
this->use_intra_process_comms_ = use_intra_process_comms;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const bool &
|
||||
NodeOptions::start_parameter_services() const
|
||||
{
|
||||
return this->start_parameter_services_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::start_parameter_services(const bool & start_parameter_services)
|
||||
{
|
||||
this->start_parameter_services_ = start_parameter_services;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const bool &
|
||||
NodeOptions::start_parameter_event_publisher() const
|
||||
{
|
||||
return this->start_parameter_event_publisher_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::start_parameter_event_publisher(const bool & start_parameter_event_publisher)
|
||||
{
|
||||
this->start_parameter_event_publisher_ = start_parameter_event_publisher;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const rmw_qos_profile_t &
|
||||
NodeOptions::parameter_event_qos_profile() const
|
||||
{
|
||||
return this->parameter_event_qos_profile_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::parameter_event_qos_profile(const rmw_qos_profile_t & parameter_event_qos_profile)
|
||||
{
|
||||
this->parameter_event_qos_profile_ = parameter_event_qos_profile;
|
||||
return *this;
|
||||
}
|
||||
|
||||
const rcl_allocator_t &
|
||||
NodeOptions::allocator() const
|
||||
{
|
||||
return this->allocator_;
|
||||
}
|
||||
|
||||
NodeOptions &
|
||||
NodeOptions::allocator(rcl_allocator_t allocator)
|
||||
{
|
||||
this->node_options_.reset(); // reset node options to make it be recreated on next access.
|
||||
this->allocator_ = allocator;
|
||||
return *this;
|
||||
}
|
||||
|
||||
// TODO(wjwwood): reuse rcutils_get_env() to avoid code duplication.
|
||||
// See also: https://github.com/ros2/rcl/issues/119
|
||||
size_t
|
||||
NodeOptions::get_domain_id_from_env() const
|
||||
{
|
||||
// Determine the domain id based on the options and the ROS_DOMAIN_ID env variable.
|
||||
size_t domain_id = std::numeric_limits<size_t>::max();
|
||||
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)()) {
|
||||
#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
|
||||
}
|
||||
return domain_id;
|
||||
}
|
||||
|
||||
} // namespace rclcpp
|
||||
@@ -350,10 +350,60 @@ SyncParametersClient::SyncParametersClient(
|
||||
rclcpp::Node::SharedPtr node,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: executor_(executor), node_(node)
|
||||
: SyncParametersClient(
|
||||
executor,
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
remote_node_name,
|
||||
qos_profile)
|
||||
{}
|
||||
|
||||
SyncParametersClient::SyncParametersClient(
|
||||
rclcpp::Node * node,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: SyncParametersClient(
|
||||
std::make_shared<rclcpp::executors::SingleThreadedExecutor>(),
|
||||
node,
|
||||
remote_node_name,
|
||||
qos_profile)
|
||||
{}
|
||||
|
||||
SyncParametersClient::SyncParametersClient(
|
||||
rclcpp::executor::Executor::SharedPtr executor,
|
||||
rclcpp::Node * node,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: SyncParametersClient(
|
||||
executor,
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface(),
|
||||
remote_node_name,
|
||||
qos_profile)
|
||||
{}
|
||||
|
||||
SyncParametersClient::SyncParametersClient(
|
||||
rclcpp::executor::Executor::SharedPtr executor,
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
|
||||
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
|
||||
const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
|
||||
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
|
||||
const std::string & remote_node_name,
|
||||
const rmw_qos_profile_t & qos_profile)
|
||||
: executor_(executor), node_base_interface_(node_base_interface)
|
||||
{
|
||||
async_parameters_client_ =
|
||||
std::make_shared<AsyncParametersClient>(node, remote_node_name, qos_profile);
|
||||
std::make_shared<AsyncParametersClient>(
|
||||
node_base_interface,
|
||||
node_topics_interface,
|
||||
node_graph_interface,
|
||||
node_services_interface,
|
||||
remote_node_name,
|
||||
qos_profile);
|
||||
}
|
||||
|
||||
std::vector<rclcpp::Parameter>
|
||||
@@ -361,7 +411,7 @@ SyncParametersClient::get_parameters(const std::vector<std::string> & parameter_
|
||||
{
|
||||
auto f = async_parameters_client_->get_parameters(parameter_names);
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
|
||||
if (spin_node_until_future_complete(*executor_, node_base_interface_, f) ==
|
||||
rclcpp::executor::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
@@ -385,7 +435,7 @@ SyncParametersClient::get_parameter_types(const std::vector<std::string> & param
|
||||
auto f = async_parameters_client_->get_parameter_types(parameter_names);
|
||||
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
|
||||
if (spin_node_until_future_complete(*executor_, node_base_interface_, f) ==
|
||||
rclcpp::executor::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
@@ -399,9 +449,8 @@ SyncParametersClient::set_parameters(
|
||||
{
|
||||
auto f = async_parameters_client_->set_parameters(parameters);
|
||||
|
||||
auto node_base_interface = node_->get_node_base_interface();
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (spin_node_until_future_complete(*executor_, node_base_interface, f) ==
|
||||
if (spin_node_until_future_complete(*executor_, node_base_interface_, f) ==
|
||||
rclcpp::executor::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
@@ -416,7 +465,7 @@ SyncParametersClient::set_parameters_atomically(
|
||||
auto f = async_parameters_client_->set_parameters_atomically(parameters);
|
||||
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
|
||||
if (spin_node_until_future_complete(*executor_, node_base_interface_, f) ==
|
||||
rclcpp::executor::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
@@ -433,7 +482,7 @@ SyncParametersClient::list_parameters(
|
||||
auto f = async_parameters_client_->list_parameters(parameter_prefixes, depth);
|
||||
|
||||
using rclcpp::executors::spin_node_until_future_complete;
|
||||
if (spin_node_until_future_complete(*executor_, node_->get_node_base_interface(), f) ==
|
||||
if (spin_node_until_future_complete(*executor_, node_base_interface_, f) ==
|
||||
rclcpp::executor::FutureReturnCode::SUCCESS)
|
||||
{
|
||||
return f.get();
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "rclcpp/allocator/allocator_common.hpp"
|
||||
#include "rclcpp/allocator/allocator_deleter.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/intra_process_manager.hpp"
|
||||
#include "rclcpp/macros.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
@@ -42,7 +43,8 @@ PublisherBase::PublisherBase(
|
||||
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)
|
||||
intra_process_is_enabled_(false), intra_process_publisher_id_(0),
|
||||
store_intra_process_message_(nullptr)
|
||||
{
|
||||
rcl_ret_t ret = rcl_publisher_init(
|
||||
&publisher_handle_,
|
||||
@@ -94,6 +96,20 @@ PublisherBase::~PublisherBase()
|
||||
rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
}
|
||||
|
||||
auto ipm = weak_ipm_.lock();
|
||||
|
||||
if (!intra_process_is_enabled_) {
|
||||
return;
|
||||
}
|
||||
if (!ipm) {
|
||||
// TODO(ivanpauno): should this raise an error?
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Intra process manager died before than a publisher.");
|
||||
return;
|
||||
}
|
||||
ipm->remove_publisher(intra_process_publisher_id_);
|
||||
}
|
||||
|
||||
const char *
|
||||
@@ -138,6 +154,60 @@ PublisherBase::get_publisher_handle() const
|
||||
return &publisher_handle_;
|
||||
}
|
||||
|
||||
size_t
|
||||
PublisherBase::get_subscription_count() const
|
||||
{
|
||||
size_t inter_process_subscription_count = 0;
|
||||
|
||||
rcl_ret_t status = rcl_publisher_get_subscription_count(
|
||||
&publisher_handle_,
|
||||
&inter_process_subscription_count);
|
||||
|
||||
if (RCL_RET_PUBLISHER_INVALID == status) {
|
||||
rcl_reset_error(); /* next call will reset error message if not context */
|
||||
if (rcl_publisher_is_valid_except_context(&publisher_handle_)) {
|
||||
rcl_context_t * context = rcl_publisher_get_context(&publisher_handle_);
|
||||
if (nullptr != context && !rcl_context_is_valid(context)) {
|
||||
/* publisher is invalid due to context being shutdown */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (RCL_RET_OK != status) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(status, "failed to get get subscription count");
|
||||
}
|
||||
return inter_process_subscription_count;
|
||||
}
|
||||
|
||||
size_t
|
||||
PublisherBase::get_intra_process_subscription_count() const
|
||||
{
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!intra_process_is_enabled_) {
|
||||
return 0;
|
||||
}
|
||||
if (!ipm) {
|
||||
// TODO(ivanpauno): should this just return silently? Or maybe return with a warning?
|
||||
// Same as wjwwood comment in publisher_factory create_shared_publish_callback.
|
||||
throw std::runtime_error(
|
||||
"intra process subscriber count called after "
|
||||
"destruction of intra process manager");
|
||||
}
|
||||
return ipm->get_subscription_count(intra_process_publisher_id_);
|
||||
}
|
||||
|
||||
rmw_qos_profile_t
|
||||
PublisherBase::get_actual_qos() const
|
||||
{
|
||||
const rmw_qos_profile_t * qos = rcl_publisher_get_actual_qos(&publisher_handle_);
|
||||
if (!qos) {
|
||||
auto msg = std::string("failed to get qos settings: ") + rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
return *qos;
|
||||
}
|
||||
|
||||
bool
|
||||
PublisherBase::operator==(const rmw_gid_t & gid) const
|
||||
{
|
||||
@@ -168,9 +238,15 @@ PublisherBase::operator==(const rmw_gid_t * gid) const
|
||||
void
|
||||
PublisherBase::setup_intra_process(
|
||||
uint64_t intra_process_publisher_id,
|
||||
StoreMessageCallbackT callback,
|
||||
StoreMessageCallbackT store_callback,
|
||||
IntraProcessManagerSharedPtr ipm,
|
||||
const rcl_publisher_options_t & intra_process_options)
|
||||
{
|
||||
// Intraprocess configuration is not allowed with "durability" qos policy non "volatile".
|
||||
if (this->get_actual_qos().durability != RMW_QOS_POLICY_DURABILITY_VOLATILE) {
|
||||
throw exceptions::InvalidParametersException(
|
||||
"intraprocess communication is not allowed with durability qos policy non-volatile");
|
||||
}
|
||||
const char * topic_name = this->get_topic_name();
|
||||
if (!topic_name) {
|
||||
throw std::runtime_error("failed to get topic name");
|
||||
@@ -199,7 +275,10 @@ PublisherBase::setup_intra_process(
|
||||
}
|
||||
|
||||
intra_process_publisher_id_ = intra_process_publisher_id;
|
||||
store_intra_process_message_ = callback;
|
||||
store_intra_process_message_ = store_callback;
|
||||
weak_ipm_ = ipm;
|
||||
intra_process_is_enabled_ = true;
|
||||
|
||||
// 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_);
|
||||
|
||||
377
rclcpp/src/rclcpp/signal_handler.cpp
Normal file
377
rclcpp/src/rclcpp/signal_handler.cpp
Normal file
@@ -0,0 +1,377 @@
|
||||
// Copyright 2018 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 "./signal_handler.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
|
||||
// includes for semaphore notification code
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <dispatch/dispatch.h>
|
||||
#else // posix
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rmw/impl/cpp/demangle.hpp"
|
||||
|
||||
using rclcpp::SignalHandler;
|
||||
|
||||
// initialize static storage in SignalHandler class
|
||||
SignalHandler::signal_handler_type SignalHandler::old_signal_handler_;
|
||||
std::atomic_bool SignalHandler::signal_received_ = ATOMIC_VAR_INIT(false);
|
||||
std::atomic_bool SignalHandler::wait_for_signal_is_setup_ = ATOMIC_VAR_INIT(false);
|
||||
#if defined(_WIN32)
|
||||
HANDLE SignalHandler::signal_handler_sem_;
|
||||
#elif defined(__APPLE__)
|
||||
dispatch_semaphore_t SignalHandler::signal_handler_sem_;
|
||||
#else // posix
|
||||
sem_t SignalHandler::signal_handler_sem_;
|
||||
#endif
|
||||
|
||||
// The logger must be initialized before the local static variable signal_handler,
|
||||
// from the method get_global_signal_handler(), so that it is destructed after
|
||||
// it, because the destructor of SignalHandler uses this logger object.
|
||||
static rclcpp::Logger g_logger = rclcpp::get_logger("rclcpp");
|
||||
|
||||
rclcpp::Logger &
|
||||
SignalHandler::get_logger()
|
||||
{
|
||||
return g_logger;
|
||||
}
|
||||
|
||||
SignalHandler &
|
||||
SignalHandler::get_global_signal_handler()
|
||||
{
|
||||
// This is initialized after the g_logger static global, ensuring
|
||||
// SignalHandler::get_logger() may be called from the destructor of
|
||||
// SignalHandler, according to this:
|
||||
//
|
||||
// Variables declared at block scope with the specifier static have static
|
||||
// storage duration but are initialized the first time control passes
|
||||
// through their declaration (unless their initialization is zero- or
|
||||
// constant-initialization, which can be performed before the block is
|
||||
// first entered). On all further calls, the declaration is skipped.
|
||||
//
|
||||
// -- https://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables
|
||||
//
|
||||
// Which is guaranteed to occur after static initialization for global (see:
|
||||
// https://en.cppreference.com/w/cpp/language/initialization#Static_initialization),
|
||||
// which is when g_logger will be initialized.
|
||||
// And destruction will occur in the reverse order.
|
||||
static SignalHandler signal_handler;
|
||||
return signal_handler;
|
||||
}
|
||||
|
||||
bool
|
||||
SignalHandler::install()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(install_mutex_);
|
||||
bool already_installed = installed_.exchange(true);
|
||||
if (already_installed) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
setup_wait_for_signal();
|
||||
signal_received_.store(false);
|
||||
|
||||
SignalHandler::signal_handler_type signal_handler_argument;
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
memset(&signal_handler_argument, 0, sizeof(signal_handler_argument));
|
||||
sigemptyset(&signal_handler_argument.sa_mask);
|
||||
signal_handler_argument.sa_sigaction = signal_handler;
|
||||
signal_handler_argument.sa_flags = SA_SIGINFO;
|
||||
#else
|
||||
signal_handler_argument = signal_handler;
|
||||
#endif
|
||||
|
||||
old_signal_handler_ = SignalHandler::set_signal_handler(SIGINT, signal_handler_argument);
|
||||
|
||||
signal_handler_thread_ = std::thread(&SignalHandler::deferred_signal_handler, this);
|
||||
} catch (...) {
|
||||
installed_.store(false);
|
||||
throw;
|
||||
}
|
||||
RCLCPP_DEBUG(get_logger(), "signal handler installed");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SignalHandler::uninstall()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(install_mutex_);
|
||||
bool installed = installed_.exchange(false);
|
||||
if (!installed) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
// TODO(wjwwood): what happens if someone overrides our signal handler then calls uninstall?
|
||||
// I think we need to assert that we're the current signal handler, and mitigate if not.
|
||||
set_signal_handler(SIGINT, old_signal_handler_);
|
||||
RCLCPP_DEBUG(get_logger(), "SignalHandler::uninstall(): notifying deferred signal handler");
|
||||
notify_signal_handler();
|
||||
signal_handler_thread_.join();
|
||||
teardown_wait_for_signal();
|
||||
} catch (...) {
|
||||
installed_.exchange(true);
|
||||
throw;
|
||||
}
|
||||
RCLCPP_DEBUG(get_logger(), "signal handler uninstalled");
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
SignalHandler::is_installed()
|
||||
{
|
||||
return installed_.load();
|
||||
}
|
||||
|
||||
SignalHandler::~SignalHandler()
|
||||
{
|
||||
try {
|
||||
uninstall();
|
||||
} catch (const std::exception & exc) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger(),
|
||||
"caught %s exception when uninstalling the sigint handler in rclcpp::~SignalHandler: %s",
|
||||
rmw::impl::cpp::demangle(exc).c_str(), exc.what());
|
||||
} catch (...) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger(),
|
||||
"caught unknown exception when uninstalling the sigint handler in rclcpp::~SignalHandler");
|
||||
}
|
||||
}
|
||||
|
||||
RCLCPP_LOCAL
|
||||
void
|
||||
__safe_strerror(int errnum, char * buffer, size_t buffer_length)
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
strerror_s(buffer, buffer_length, errnum);
|
||||
#elif (defined(_GNU_SOURCE) && !defined(ANDROID))
|
||||
char * msg = strerror_r(errnum, buffer, buffer_length);
|
||||
if (msg != buffer) {
|
||||
strncpy(buffer, msg, buffer_length);
|
||||
buffer[buffer_length - 1] = '\0';
|
||||
}
|
||||
#else
|
||||
int error_status = strerror_r(errnum, buffer, buffer_length);
|
||||
if (error_status != 0) {
|
||||
throw std::runtime_error("Failed to get error string for errno: " + std::to_string(errnum));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
SignalHandler::signal_handler_type
|
||||
SignalHandler::set_signal_handler(
|
||||
int signal_value,
|
||||
const SignalHandler::signal_handler_type & signal_handler)
|
||||
{
|
||||
bool signal_handler_install_failed;
|
||||
SignalHandler::signal_handler_type old_signal_handler;
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
ssize_t ret = sigaction(signal_value, &signal_handler, &old_signal_handler);
|
||||
signal_handler_install_failed = (ret == -1);
|
||||
#else
|
||||
old_signal_handler = std::signal(signal_value, signal_handler);
|
||||
signal_handler_install_failed = (old_signal_handler == SIG_ERR);
|
||||
#endif
|
||||
if (signal_handler_install_failed) {
|
||||
char error_string[1024];
|
||||
__safe_strerror(errno, error_string, sizeof(error_string));
|
||||
auto msg =
|
||||
"Failed to set SIGINT signal handler (" + std::to_string(errno) + "): " + error_string;
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
|
||||
return old_signal_handler;
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::signal_handler_common()
|
||||
{
|
||||
signal_received_.store(true);
|
||||
RCLCPP_DEBUG(
|
||||
get_logger(),
|
||||
"signal_handler(): SIGINT received, notifying deferred signal handler");
|
||||
notify_signal_handler();
|
||||
}
|
||||
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
void
|
||||
SignalHandler::signal_handler(int signal_value, siginfo_t * siginfo, void * context)
|
||||
{
|
||||
RCLCPP_INFO(get_logger(), "signal_handler(signal_value=%d)", signal_value);
|
||||
|
||||
if (old_signal_handler_.sa_flags & SA_SIGINFO) {
|
||||
if (old_signal_handler_.sa_sigaction != NULL) {
|
||||
old_signal_handler_.sa_sigaction(signal_value, siginfo, context);
|
||||
}
|
||||
} else {
|
||||
if (
|
||||
old_signal_handler_.sa_handler != NULL && // Is set
|
||||
old_signal_handler_.sa_handler != SIG_DFL && // Is not default
|
||||
old_signal_handler_.sa_handler != SIG_IGN) // Is not ignored
|
||||
{
|
||||
old_signal_handler_.sa_handler(signal_value);
|
||||
}
|
||||
}
|
||||
|
||||
signal_handler_common();
|
||||
}
|
||||
#else
|
||||
void
|
||||
SignalHandler::signal_handler(int signal_value)
|
||||
{
|
||||
RCLCPP_INFO(get_logger(), "signal_handler(signal_value=%d)", signal_value);
|
||||
|
||||
if (old_signal_handler_) {
|
||||
old_signal_handler_(signal_value);
|
||||
}
|
||||
|
||||
signal_handler_common();
|
||||
}
|
||||
#endif
|
||||
|
||||
void
|
||||
SignalHandler::deferred_signal_handler()
|
||||
{
|
||||
while (true) {
|
||||
if (signal_received_.exchange(false)) {
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): SIGINT received, shutting down");
|
||||
for (auto context_ptr : rclcpp::get_contexts()) {
|
||||
if (context_ptr->get_init_options().shutdown_on_sigint) {
|
||||
RCLCPP_DEBUG(
|
||||
get_logger(),
|
||||
"deferred_signal_handler(): "
|
||||
"shutting down rclcpp::Context @ %p, because it had shutdown_on_sigint == true",
|
||||
context_ptr.get());
|
||||
context_ptr->shutdown("signal handler");
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!is_installed()) {
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): signal handling uninstalled");
|
||||
break;
|
||||
}
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): waiting for SIGINT or uninstall");
|
||||
wait_for_signal();
|
||||
RCLCPP_DEBUG(get_logger(), "deferred_signal_handler(): woken up due to SIGINT or uninstall");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::setup_wait_for_signal()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
signal_handler_sem_ = CreateSemaphore(
|
||||
NULL, // default security attributes
|
||||
0, // initial semaphore count
|
||||
1, // maximum semaphore count
|
||||
NULL); // unnamed semaphore
|
||||
if (NULL == signal_handler_sem_) {
|
||||
throw std::runtime_error("CreateSemaphore() failed in setup_wait_for_signal()");
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
signal_handler_sem_ = dispatch_semaphore_create(0);
|
||||
#else // posix
|
||||
if (-1 == sem_init(&signal_handler_sem_, 0, 0)) {
|
||||
throw std::runtime_error(std::string("sem_init() failed: ") + strerror(errno));
|
||||
}
|
||||
#endif
|
||||
wait_for_signal_is_setup_.store(true);
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::teardown_wait_for_signal() noexcept
|
||||
{
|
||||
if (!wait_for_signal_is_setup_.exchange(false)) {
|
||||
return;
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
CloseHandle(signal_handler_sem_);
|
||||
#elif defined(__APPLE__)
|
||||
dispatch_release(signal_handler_sem_);
|
||||
#else // posix
|
||||
if (-1 == sem_destroy(&signal_handler_sem_)) {
|
||||
RCLCPP_ERROR(get_logger(), "invalid semaphore in teardown_wait_for_signal()");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::wait_for_signal()
|
||||
{
|
||||
if (!wait_for_signal_is_setup_.load()) {
|
||||
RCLCPP_ERROR(get_logger(), "called wait_for_signal() before setup_wait_for_signal()");
|
||||
return;
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
DWORD dw_wait_result = WaitForSingleObject(signal_handler_sem_, INFINITE);
|
||||
switch (dw_wait_result) {
|
||||
case WAIT_ABANDONED:
|
||||
RCLCPP_ERROR(
|
||||
get_logger(), "WaitForSingleObject() failed in wait_for_signal() with WAIT_ABANDONED: %s",
|
||||
GetLastError());
|
||||
break;
|
||||
case WAIT_OBJECT_0:
|
||||
// successful
|
||||
break;
|
||||
case WAIT_TIMEOUT:
|
||||
RCLCPP_ERROR(get_logger(), "WaitForSingleObject() timedout out in wait_for_signal()");
|
||||
break;
|
||||
case WAIT_FAILED:
|
||||
RCLCPP_ERROR(
|
||||
get_logger(), "WaitForSingleObject() failed in wait_for_signal(): %s", GetLastError());
|
||||
break;
|
||||
default:
|
||||
RCLCPP_ERROR(
|
||||
get_logger(), "WaitForSingleObject() gave unknown return in wait_for_signal(): %s",
|
||||
GetLastError());
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
dispatch_semaphore_wait(signal_handler_sem_, DISPATCH_TIME_FOREVER);
|
||||
#else // posix
|
||||
int s;
|
||||
do {
|
||||
s = sem_wait(&signal_handler_sem_);
|
||||
} while (-1 == s && EINTR == errno);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
SignalHandler::notify_signal_handler() noexcept
|
||||
{
|
||||
if (!wait_for_signal_is_setup_.load()) {
|
||||
return;
|
||||
}
|
||||
#if defined(_WIN32)
|
||||
if (!ReleaseSemaphore(signal_handler_sem_, 1, NULL)) {
|
||||
RCLCPP_ERROR(
|
||||
get_logger(), "ReleaseSemaphore() failed in notify_signal_handler(): %s", GetLastError());
|
||||
}
|
||||
#elif defined(__APPLE__)
|
||||
dispatch_semaphore_signal(signal_handler_sem_);
|
||||
#else // posix
|
||||
if (-1 == sem_post(&signal_handler_sem_)) {
|
||||
RCLCPP_ERROR(get_logger(), "sem_post failed in notify_signal_handler()");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
189
rclcpp/src/rclcpp/signal_handler.hpp
Normal file
189
rclcpp/src/rclcpp/signal_handler.hpp
Normal file
@@ -0,0 +1,189 @@
|
||||
// Copyright 2018 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__SIGNAL_HANDLER_HPP_
|
||||
#define RCLCPP__SIGNAL_HANDLER_HPP_
|
||||
|
||||
#include <atomic>
|
||||
#include <csignal>
|
||||
#include <mutex>
|
||||
#include <thread>
|
||||
|
||||
#include "rclcpp/logging.hpp"
|
||||
|
||||
// includes for semaphore notification code
|
||||
#if defined(_WIN32)
|
||||
#include <windows.h>
|
||||
#elif defined(__APPLE__)
|
||||
#include <dispatch/dispatch.h>
|
||||
#else // posix
|
||||
#include <semaphore.h>
|
||||
#endif
|
||||
|
||||
// Determine if sigaction is available
|
||||
#if __APPLE__ || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE
|
||||
#define RCLCPP_HAS_SIGACTION
|
||||
#endif
|
||||
|
||||
namespace rclcpp
|
||||
{
|
||||
|
||||
/// Responsible for manaaging the SIGINT signal handling.
|
||||
/**
|
||||
* This class is responsible for:
|
||||
*
|
||||
* - installing the signal handler for SIGINT
|
||||
* - uninstalling the signal handler for SIGINT
|
||||
* - creating a thread to execute "on sigint" work outside of the signal handler
|
||||
* - safely notifying the dedicated signal handling thread when receiving SIGINT
|
||||
* - implementation of all of the signal handling work, like shutting down contexts
|
||||
*
|
||||
* \internal
|
||||
*/
|
||||
class SignalHandler final
|
||||
{
|
||||
public:
|
||||
/// Return the global singleton of this class.
|
||||
static
|
||||
SignalHandler &
|
||||
get_global_signal_handler();
|
||||
|
||||
/// Return a global singleton logger to avoid needing to create it everywhere.
|
||||
static
|
||||
rclcpp::Logger &
|
||||
get_logger();
|
||||
|
||||
/// Install the signal handler for SIGINT and start the dedicated signal handling thread.
|
||||
/**
|
||||
* Also stores the current signal handler to be called on SIGINT and to
|
||||
* restore when uninstalling this signal handler.
|
||||
*/
|
||||
bool
|
||||
install();
|
||||
|
||||
/// Uninstall the signal handler for SIGINT and join the dedicated singal handling thread.
|
||||
/**
|
||||
* Also restores the previous signal handler.
|
||||
*/
|
||||
bool
|
||||
uninstall();
|
||||
|
||||
/// Return true if installed, false otherwise.
|
||||
bool
|
||||
is_installed();
|
||||
|
||||
private:
|
||||
SignalHandler() = default;
|
||||
|
||||
~SignalHandler();
|
||||
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
using signal_handler_type = struct sigaction;
|
||||
#else
|
||||
using signal_handler_type = void (*)(int);
|
||||
#endif
|
||||
// POSIX signal handler structure storage for the existing signal handler.
|
||||
static SignalHandler::signal_handler_type old_signal_handler_;
|
||||
|
||||
/// Set the signal handler function.
|
||||
static
|
||||
SignalHandler::signal_handler_type
|
||||
set_signal_handler(int signal_value, const SignalHandler::signal_handler_type & signal_handler);
|
||||
|
||||
/// Common signal handler code between sigaction and non-sigaction versions.
|
||||
static
|
||||
void
|
||||
signal_handler_common();
|
||||
|
||||
#if defined(RCLCPP_HAS_SIGACTION)
|
||||
/// Signal handler function.
|
||||
static
|
||||
void
|
||||
signal_handler(int signal_value, siginfo_t * siginfo, void * context);
|
||||
#else
|
||||
/// Signal handler function.
|
||||
static
|
||||
void
|
||||
signal_handler(int signal_value);
|
||||
#endif
|
||||
|
||||
/// Target of the dedicated signal handling thread.
|
||||
void
|
||||
deferred_signal_handler();
|
||||
|
||||
/// Setup anything that is necessary for wait_for_signal() or notify_signal_handler().
|
||||
/**
|
||||
* This must be called before wait_for_signal() or notify_signal_handler().
|
||||
* This is not thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
setup_wait_for_signal();
|
||||
|
||||
/// Undo all setup done in setup_wait_for_signal().
|
||||
/**
|
||||
* Must not call wait_for_signal() or notify_signal_handler() after calling this.
|
||||
*
|
||||
* This is not thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
teardown_wait_for_signal() noexcept;
|
||||
|
||||
/// Wait for a notification from notify_signal_handler() in a signal safe way.
|
||||
/**
|
||||
* This static method may throw if posting the semaphore fails.
|
||||
*
|
||||
* This is not thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
wait_for_signal();
|
||||
|
||||
/// Notify blocking wait_for_signal() calls in a signal safe way.
|
||||
/**
|
||||
* This is used to notify the deferred_signal_handler() thread to start work
|
||||
* from the signal handler.
|
||||
*
|
||||
* This is thread-safe.
|
||||
*/
|
||||
static
|
||||
void
|
||||
notify_signal_handler() noexcept;
|
||||
|
||||
// Whether or not a signal has been received.
|
||||
static std::atomic_bool signal_received_;
|
||||
// A thread to which singal handling tasks are deferred.
|
||||
std::thread signal_handler_thread_;
|
||||
|
||||
// A mutex used to synchronize the install() and uninstall() methods.
|
||||
std::mutex install_mutex_;
|
||||
// Whether or not the signal handler has been installed.
|
||||
std::atomic_bool installed_{false};
|
||||
|
||||
// Whether or not the semaphore for wait_for_signal is setup.
|
||||
static std::atomic_bool wait_for_signal_is_setup_;
|
||||
// Storage for the wait_for_signal semaphore.
|
||||
#if defined(_WIN32)
|
||||
static HANDLE signal_handler_sem_;
|
||||
#elif defined(__APPLE__)
|
||||
static dispatch_semaphore_t signal_handler_sem_;
|
||||
#else // posix
|
||||
static sem_t signal_handler_sem_;
|
||||
#endif
|
||||
};
|
||||
|
||||
} // namespace rclcpp
|
||||
|
||||
#endif // RCLCPP__SIGNAL_HANDLER_HPP_
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/expand_topic_or_service_name.hpp"
|
||||
#include "rclcpp/intra_process_manager.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
|
||||
#include "rmw/error_handling.h"
|
||||
@@ -34,6 +35,8 @@ SubscriptionBase::SubscriptionBase(
|
||||
const rcl_subscription_options_t & subscription_options,
|
||||
bool is_serialized)
|
||||
: node_handle_(node_handle),
|
||||
use_intra_process_(false),
|
||||
intra_process_subscription_id_(0),
|
||||
type_support_(type_support_handle),
|
||||
is_serialized_(is_serialized)
|
||||
{
|
||||
@@ -41,7 +44,7 @@ SubscriptionBase::SubscriptionBase(
|
||||
{
|
||||
if (rcl_subscription_fini(rcl_subs, node_handle.get()) != RCL_RET_OK) {
|
||||
RCLCPP_ERROR(
|
||||
rclcpp::get_logger(rcl_node_get_logger_name(node_handle.get())).get_child("rclcpp"),
|
||||
rclcpp::get_node_logger(node_handle.get()).get_child("rclcpp"),
|
||||
"Error in destruction of rcl subscription handle: %s",
|
||||
rcl_get_error_string().str);
|
||||
rcl_reset_error();
|
||||
@@ -80,6 +83,18 @@ SubscriptionBase::SubscriptionBase(
|
||||
|
||||
SubscriptionBase::~SubscriptionBase()
|
||||
{
|
||||
if (!use_intra_process_) {
|
||||
return;
|
||||
}
|
||||
auto ipm = weak_ipm_.lock();
|
||||
if (!ipm) {
|
||||
// TODO(ivanpauno): should this raise an error?
|
||||
RCLCPP_WARN(
|
||||
rclcpp::get_logger("rclcpp"),
|
||||
"Intra process manager died before than a subscription.");
|
||||
return;
|
||||
}
|
||||
ipm->remove_subscription(intra_process_subscription_id_);
|
||||
}
|
||||
|
||||
const char *
|
||||
@@ -117,3 +132,18 @@ SubscriptionBase::is_serialized() const
|
||||
{
|
||||
return is_serialized_;
|
||||
}
|
||||
|
||||
size_t
|
||||
SubscriptionBase::get_publisher_count() const
|
||||
{
|
||||
size_t inter_process_publisher_count = 0;
|
||||
|
||||
rmw_ret_t status = rcl_subscription_get_publisher_count(
|
||||
subscription_handle_.get(),
|
||||
&inter_process_publisher_count);
|
||||
|
||||
if (RCL_RET_OK != status) {
|
||||
rclcpp::exceptions::throw_from_rcl_error(status, "failed to get get publisher count");
|
||||
}
|
||||
return inter_process_publisher_count;
|
||||
}
|
||||
|
||||
@@ -21,10 +21,9 @@
|
||||
|
||||
#include "rcl/time.h"
|
||||
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
#include "rclcpp/clock.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/logging.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/time.hpp"
|
||||
#include "rclcpp/time_source.hpp"
|
||||
@@ -34,14 +33,16 @@ namespace rclcpp
|
||||
{
|
||||
|
||||
TimeSource::TimeSource(std::shared_ptr<rclcpp::Node> node)
|
||||
: clock_subscription_(nullptr),
|
||||
: logger_(rclcpp::get_logger("rclcpp")),
|
||||
clock_subscription_(nullptr),
|
||||
ros_time_active_(false)
|
||||
{
|
||||
this->attachNode(node);
|
||||
}
|
||||
|
||||
TimeSource::TimeSource()
|
||||
: ros_time_active_(false)
|
||||
: logger_(rclcpp::get_logger("rclcpp")),
|
||||
ros_time_active_(false)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -51,21 +52,49 @@ void TimeSource::attachNode(rclcpp::Node::SharedPtr node)
|
||||
node->get_node_base_interface(),
|
||||
node->get_node_topics_interface(),
|
||||
node->get_node_graph_interface(),
|
||||
node->get_node_services_interface());
|
||||
node->get_node_services_interface(),
|
||||
node->get_node_logging_interface(),
|
||||
node->get_node_clock_interface(),
|
||||
node->get_node_parameters_interface());
|
||||
}
|
||||
|
||||
void TimeSource::attachNode(
|
||||
const rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
|
||||
const rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
|
||||
const rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
|
||||
const rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface)
|
||||
rclcpp::node_interfaces::NodeBaseInterface::SharedPtr node_base_interface,
|
||||
rclcpp::node_interfaces::NodeTopicsInterface::SharedPtr node_topics_interface,
|
||||
rclcpp::node_interfaces::NodeGraphInterface::SharedPtr node_graph_interface,
|
||||
rclcpp::node_interfaces::NodeServicesInterface::SharedPtr node_services_interface,
|
||||
rclcpp::node_interfaces::NodeLoggingInterface::SharedPtr node_logging_interface,
|
||||
rclcpp::node_interfaces::NodeClockInterface::SharedPtr node_clock_interface,
|
||||
rclcpp::node_interfaces::NodeParametersInterface::SharedPtr node_parameters_interface)
|
||||
{
|
||||
node_base_ = node_base_interface;
|
||||
node_topics_ = node_topics_interface;
|
||||
node_graph_ = node_graph_interface;
|
||||
node_services_ = node_services_interface;
|
||||
node_logging_ = node_logging_interface;
|
||||
node_clock_ = node_clock_interface;
|
||||
node_parameters_ = node_parameters_interface;
|
||||
// TODO(tfoote): Update QOS
|
||||
|
||||
logger_ = node_logging_->get_logger();
|
||||
|
||||
rclcpp::Parameter use_sim_time_param;
|
||||
if (node_parameters_->get_parameter("use_sim_time", use_sim_time_param)) {
|
||||
if (use_sim_time_param.get_type() == rclcpp::PARAMETER_BOOL) {
|
||||
if (use_sim_time_param.get_value<bool>() == true) {
|
||||
parameter_state_ = SET_TRUE;
|
||||
enable_ros_time();
|
||||
create_clock_sub();
|
||||
}
|
||||
} else {
|
||||
RCLCPP_ERROR(logger_, "Invalid type for parameter 'use_sim_time' %s should be bool",
|
||||
use_sim_time_param.get_type_name().c_str());
|
||||
}
|
||||
} else {
|
||||
RCLCPP_DEBUG(logger_, "'use_sim_time' parameter not set, using wall time by default.");
|
||||
}
|
||||
|
||||
// TODO(tfoote) use parameters interface not subscribe to events via topic ticketed #609
|
||||
parameter_client_ = std::make_shared<rclcpp::AsyncParametersClient>(
|
||||
node_base_,
|
||||
node_topics_,
|
||||
@@ -86,6 +115,9 @@ void TimeSource::detachNode()
|
||||
node_topics_.reset();
|
||||
node_graph_.reset();
|
||||
node_services_.reset();
|
||||
node_logging_.reset();
|
||||
node_clock_.reset();
|
||||
node_parameters_.reset();
|
||||
disable_ros_time();
|
||||
}
|
||||
|
||||
@@ -97,11 +129,12 @@ void TimeSource::attachClock(std::shared_ptr<rclcpp::Clock> clock)
|
||||
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
associated_clocks_.push_back(clock);
|
||||
// Set the clock if there's already data for it
|
||||
// Set the clock to zero unless there's a recently received message
|
||||
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
if (last_msg_set_) {
|
||||
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>(last_msg_set_->clock);
|
||||
set_clock(time_msg, ros_time_active_, clock);
|
||||
time_msg = std::make_shared<builtin_interfaces::msg::Time>(last_msg_set_->clock);
|
||||
}
|
||||
set_clock(time_msg, ros_time_active_, clock);
|
||||
}
|
||||
|
||||
void TimeSource::detachClock(std::shared_ptr<rclcpp::Clock> clock)
|
||||
@@ -111,13 +144,15 @@ void TimeSource::detachClock(std::shared_ptr<rclcpp::Clock> clock)
|
||||
if (result != associated_clocks_.end()) {
|
||||
associated_clocks_.erase(result);
|
||||
} else {
|
||||
RCUTILS_LOG_ERROR("Failed to remove clock");
|
||||
RCLCPP_ERROR(logger_, "failed to remove clock");
|
||||
}
|
||||
}
|
||||
|
||||
TimeSource::~TimeSource()
|
||||
{
|
||||
if (node_base_ || node_topics_ || node_graph_ || node_services_) {
|
||||
if (node_base_ || node_topics_ || node_graph_ || node_services_ ||
|
||||
node_logging_ || node_clock_ || node_parameters_)
|
||||
{
|
||||
this->detachNode();
|
||||
}
|
||||
}
|
||||
@@ -200,7 +235,7 @@ void TimeSource::on_parameter_event(const rcl_interfaces::msg::ParameterEvent::S
|
||||
rclcpp::ParameterEventsFilter::EventType::CHANGED});
|
||||
for (auto & it : filter.get_events()) {
|
||||
if (it.second->value.type != ParameterType::PARAMETER_BOOL) {
|
||||
RCUTILS_LOG_ERROR("use_sim_time parameter set to something besides a bool");
|
||||
RCLCPP_ERROR(logger_, "use_sim_time parameter set to something besides a bool");
|
||||
continue;
|
||||
}
|
||||
if (it.second->value.bool_value) {
|
||||
@@ -251,13 +286,14 @@ void TimeSource::enable_ros_time()
|
||||
// Local storage
|
||||
ros_time_active_ = true;
|
||||
|
||||
// Update all attached clocks
|
||||
// Update all attached clocks to zero or last recorded time
|
||||
std::lock_guard<std::mutex> guard(clock_list_lock_);
|
||||
auto time_msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
if (last_msg_set_) {
|
||||
time_msg = std::make_shared<builtin_interfaces::msg::Time>(last_msg_set_->clock);
|
||||
}
|
||||
for (auto it = associated_clocks_.begin(); it != associated_clocks_.end(); ++it) {
|
||||
auto msg = std::make_shared<builtin_interfaces::msg::Time>();
|
||||
msg->sec = 0;
|
||||
msg->nanosec = 0;
|
||||
set_clock(msg, true, *it);
|
||||
set_clock(time_msg, true, *it);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -18,13 +18,22 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rcutils/logging_macros.h"
|
||||
|
||||
using rclcpp::TimerBase;
|
||||
|
||||
TimerBase::TimerBase(rclcpp::Clock::SharedPtr clock, std::chrono::nanoseconds period)
|
||||
TimerBase::TimerBase(
|
||||
rclcpp::Clock::SharedPtr clock,
|
||||
std::chrono::nanoseconds period,
|
||||
rclcpp::Context::SharedPtr context)
|
||||
: clock_(clock), timer_handle_(nullptr)
|
||||
{
|
||||
clock_ = clock;
|
||||
if (nullptr == context) {
|
||||
context = rclcpp::contexts::default_context::get_global_default_context();
|
||||
}
|
||||
|
||||
auto rcl_context = context->get_rcl_context();
|
||||
|
||||
timer_handle_ = std::shared_ptr<rcl_timer_t>(
|
||||
new rcl_timer_t, [ = ](rcl_timer_t * timer) mutable
|
||||
@@ -36,15 +45,16 @@ TimerBase::TimerBase(rclcpp::Clock::SharedPtr clock, std::chrono::nanoseconds pe
|
||||
rcl_reset_error();
|
||||
}
|
||||
delete timer;
|
||||
// Captured shared pointer by copy, reset to make sure timer is finalized before clock
|
||||
// Captured shared pointers by copy, reset to make sure timer is finalized before clock
|
||||
clock.reset();
|
||||
rcl_context.reset();
|
||||
});
|
||||
|
||||
*timer_handle_.get() = rcl_get_zero_initialized_timer();
|
||||
|
||||
rcl_clock_t * clock_handle = clock_->get_clock_handle();
|
||||
if (rcl_timer_init(
|
||||
timer_handle_.get(), clock_handle, period.count(), nullptr,
|
||||
timer_handle_.get(), clock_handle, rcl_context.get(), period.count(), nullptr,
|
||||
rcl_get_default_allocator()) != RCL_RET_OK)
|
||||
{
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
|
||||
@@ -14,189 +14,50 @@
|
||||
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <csignal>
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "./signal_handler.hpp"
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
|
||||
#include "rcl/error_handling.h"
|
||||
#include "rcl/rcl.h"
|
||||
|
||||
#include "rmw/error_handling.h"
|
||||
#include "rmw/rmw.h"
|
||||
|
||||
#include "rcutils/logging_macros.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
|
||||
typedef void (* signal_handler_t)(int);
|
||||
static signal_handler_t old_signal_handler = 0;
|
||||
#endif
|
||||
|
||||
#ifdef HAS_SIGACTION
|
||||
struct sigaction
|
||||
set_sigaction(int signal_value, const struct sigaction & action)
|
||||
#else
|
||||
signal_handler_t
|
||||
set_signal_handler(int signal_value, signal_handler_t signal_handler)
|
||||
#endif
|
||||
void
|
||||
rclcpp::init(int argc, char const * const argv[], const rclcpp::InitOptions & init_options)
|
||||
{
|
||||
#ifdef HAS_SIGACTION
|
||||
struct sigaction old_action;
|
||||
ssize_t ret = sigaction(signal_value, &action, &old_action);
|
||||
if (ret == -1)
|
||||
#else
|
||||
signal_handler_t old_signal_handler = std::signal(signal_value, 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*
|
||||
}
|
||||
|
||||
#ifdef HAS_SIGACTION
|
||||
return old_action;
|
||||
#else
|
||||
return old_signal_handler;
|
||||
#endif
|
||||
using rclcpp::contexts::default_context::get_global_default_context;
|
||||
get_global_default_context()->init(argc, argv, init_options);
|
||||
// Install the signal handlers.
|
||||
rclcpp::install_signal_handlers();
|
||||
}
|
||||
|
||||
void
|
||||
trigger_interrupt_guard_condition(int signal_value)
|
||||
bool
|
||||
rclcpp::install_signal_handlers()
|
||||
{
|
||||
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) {
|
||||
RCUTILS_LOG_ERROR_NAMED(
|
||||
"rclcpp",
|
||||
"failed to trigger guard condition: %s", rcl_get_error_string().str);
|
||||
}
|
||||
}
|
||||
}
|
||||
g_is_interrupted.store(true);
|
||||
g_interrupt_condition_variable.notify_all();
|
||||
return rclcpp::SignalHandler::get_global_signal_handler().install();
|
||||
}
|
||||
|
||||
void
|
||||
#ifdef HAS_SIGACTION
|
||||
signal_handler(int signal_value, siginfo_t * siginfo, void * context)
|
||||
#else
|
||||
signal_handler(int signal_value)
|
||||
#endif
|
||||
bool
|
||||
rclcpp::signal_handlers_installed()
|
||||
{
|
||||
// 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 {
|
||||
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
|
||||
{
|
||||
old_action.sa_handler(signal_value);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (old_signal_handler) {
|
||||
old_signal_handler(signal_value);
|
||||
}
|
||||
#endif
|
||||
|
||||
trigger_interrupt_guard_condition(signal_value);
|
||||
return rclcpp::SignalHandler::get_global_signal_handler().is_installed();
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::init(int argc, char const * const argv[])
|
||||
bool
|
||||
rclcpp::uninstall_signal_handlers()
|
||||
{
|
||||
g_is_interrupted.store(false);
|
||||
if (rcl_init(argc, argv, rcl_get_default_allocator()) != RCL_RET_OK) {
|
||||
std::string msg = "failed to initialize rmw implementation: ";
|
||||
msg += rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
throw std::runtime_error(msg);
|
||||
}
|
||||
#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;
|
||||
::old_action = set_sigaction(SIGINT, action);
|
||||
// Register an on_shutdown hook to restore the old action.
|
||||
rclcpp::on_shutdown(
|
||||
[]() {
|
||||
set_sigaction(SIGINT, ::old_action);
|
||||
});
|
||||
#else
|
||||
::old_signal_handler = set_signal_handler(SIGINT, ::signal_handler);
|
||||
// Register an on_shutdown hook to restore the old signal handler.
|
||||
rclcpp::on_shutdown(
|
||||
[]() {
|
||||
set_signal_handler(SIGINT, ::old_signal_handler);
|
||||
});
|
||||
#endif
|
||||
return rclcpp::SignalHandler::get_global_signal_handler().uninstall();
|
||||
}
|
||||
|
||||
std::vector<std::string>
|
||||
rclcpp::init_and_remove_ros_arguments(int argc, char const * const argv[])
|
||||
rclcpp::init_and_remove_ros_arguments(
|
||||
int argc,
|
||||
char const * const argv[],
|
||||
const rclcpp::InitOptions & init_options)
|
||||
{
|
||||
rclcpp::init(argc, argv);
|
||||
rclcpp::init(argc, argv, init_options);
|
||||
return rclcpp::remove_ros_arguments(argc, argv);
|
||||
}
|
||||
|
||||
@@ -210,7 +71,7 @@ rclcpp::remove_ros_arguments(int argc, char const * const argv[])
|
||||
|
||||
ret = rcl_parse_arguments(argc, argv, alloc, &parsed_args);
|
||||
if (RCL_RET_OK != ret) {
|
||||
exceptions::throw_from_rcl_error(ret, "failed to parse arguments");
|
||||
rclcpp::exceptions::throw_from_rcl_error(ret, "failed to parse arguments");
|
||||
}
|
||||
|
||||
int nonros_argc = 0;
|
||||
@@ -225,7 +86,7 @@ rclcpp::remove_ros_arguments(int argc, char const * const argv[])
|
||||
|
||||
if (RCL_RET_OK != ret) {
|
||||
// Not using throw_from_rcl_error, because we may need to append deallocation failures.
|
||||
exceptions::RCLErrorBase base_exc(ret, rcl_get_error_state());
|
||||
rclcpp::exceptions::RCLErrorBase base_exc(ret, rcl_get_error_state());
|
||||
rcl_reset_error();
|
||||
if (NULL != nonros_argv) {
|
||||
alloc.deallocate(nonros_argv, alloc.state);
|
||||
@@ -236,7 +97,7 @@ rclcpp::remove_ros_arguments(int argc, char const * const argv[])
|
||||
rcl_get_error_string().str;
|
||||
rcl_reset_error();
|
||||
}
|
||||
throw exceptions::RCLError(base_exc, "");
|
||||
throw rclcpp::exceptions::RCLError(base_exc, "");
|
||||
}
|
||||
|
||||
std::vector<std::string> return_arguments;
|
||||
@@ -252,106 +113,62 @@ rclcpp::remove_ros_arguments(int argc, char const * const argv[])
|
||||
|
||||
ret = rcl_arguments_fini(&parsed_args);
|
||||
if (RCL_RET_OK != ret) {
|
||||
exceptions::throw_from_rcl_error(ret, "failed to cleanup parsed arguments, leaking memory");
|
||||
rclcpp::exceptions::throw_from_rcl_error(
|
||||
ret, "failed to cleanup parsed arguments, leaking memory");
|
||||
}
|
||||
|
||||
return return_arguments;
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::ok()
|
||||
rclcpp::ok(rclcpp::Context::SharedPtr context)
|
||||
{
|
||||
return ::g_signal_status == 0;
|
||||
using rclcpp::contexts::default_context::get_global_default_context;
|
||||
if (nullptr == context) {
|
||||
context = get_global_default_context();
|
||||
}
|
||||
return context->is_valid();
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::is_initialized()
|
||||
rclcpp::is_initialized(rclcpp::Context::SharedPtr context)
|
||||
{
|
||||
return rcl_ok();
|
||||
}
|
||||
|
||||
static std::mutex on_shutdown_mutex_;
|
||||
static std::vector<std::function<void(void)>> on_shutdown_callbacks_;
|
||||
|
||||
void
|
||||
rclcpp::shutdown()
|
||||
{
|
||||
trigger_interrupt_guard_condition(SIGINT);
|
||||
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(on_shutdown_mutex_);
|
||||
for (auto & on_shutdown_callback : on_shutdown_callbacks_) {
|
||||
on_shutdown_callback();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::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::get_sigint_guard_condition(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
|
||||
auto kv = g_sigint_guard_cond_handles.find(wait_set);
|
||||
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().str);
|
||||
// *INDENT-ON*
|
||||
}
|
||||
g_sigint_guard_cond_handles[wait_set] = handle;
|
||||
return &g_sigint_guard_cond_handles[wait_set];
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::release_sigint_guard_condition(rcl_wait_set_t * wait_set)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(g_sigint_guard_cond_handles_mutex);
|
||||
auto kv = g_sigint_guard_cond_handles.find(wait_set);
|
||||
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().str);
|
||||
// *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 wait set"));
|
||||
// *INDENT-ON*
|
||||
}
|
||||
return rclcpp::ok(context);
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::sleep_for(const std::chrono::nanoseconds & nanoseconds)
|
||||
rclcpp::shutdown(rclcpp::Context::SharedPtr context, const std::string & reason)
|
||||
{
|
||||
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;
|
||||
using rclcpp::contexts::default_context::get_global_default_context;
|
||||
auto default_context = get_global_default_context();
|
||||
if (nullptr == context) {
|
||||
context = default_context;
|
||||
}
|
||||
if (time_left > std::chrono::nanoseconds::zero() && !g_is_interrupted) {
|
||||
return sleep_for(time_left);
|
||||
bool ret = context->shutdown(reason);
|
||||
if (context == default_context) {
|
||||
rclcpp::uninstall_signal_handlers();
|
||||
}
|
||||
// Return true if the timeout elapsed successfully, otherwise false.
|
||||
return !g_is_interrupted;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void
|
||||
rclcpp::on_shutdown(std::function<void()> callback, rclcpp::Context::SharedPtr context)
|
||||
{
|
||||
using rclcpp::contexts::default_context::get_global_default_context;
|
||||
if (nullptr == context) {
|
||||
context = get_global_default_context();
|
||||
}
|
||||
context->on_shutdown(callback);
|
||||
}
|
||||
|
||||
bool
|
||||
rclcpp::sleep_for(const std::chrono::nanoseconds & nanoseconds, rclcpp::Context::SharedPtr context)
|
||||
{
|
||||
using rclcpp::contexts::default_context::get_global_default_context;
|
||||
if (nullptr == context) {
|
||||
context = get_global_default_context();
|
||||
}
|
||||
return context->sleep_for(nanoseconds);
|
||||
}
|
||||
|
||||
const char *
|
||||
|
||||
47
rclcpp/src/rclcpp/waitable.cpp
Normal file
47
rclcpp/src/rclcpp/waitable.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2018 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/waitable.hpp"
|
||||
|
||||
using rclcpp::Waitable;
|
||||
|
||||
size_t
|
||||
Waitable::get_number_of_ready_subscriptions()
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
size_t
|
||||
Waitable::get_number_of_ready_timers()
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
size_t
|
||||
Waitable::get_number_of_ready_clients()
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
size_t
|
||||
Waitable::get_number_of_ready_services()
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
|
||||
size_t
|
||||
Waitable::get_number_of_ready_guard_conditions()
|
||||
{
|
||||
return 0u;
|
||||
}
|
||||
@@ -43,6 +43,28 @@ protected:
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
class TestClientSub : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
subnode = node->create_sub_node("sub_ns");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::Node::SharedPtr subnode;
|
||||
};
|
||||
|
||||
/*
|
||||
Testing client construction and destruction.
|
||||
*/
|
||||
@@ -58,3 +80,20 @@ TEST_F(TestClient, construction_and_destruction) {
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing client construction and destruction for subnodes.
|
||||
*/
|
||||
TEST_F(TestClientSub, construction_and_destruction) {
|
||||
using rcl_interfaces::srv::ListParameters;
|
||||
{
|
||||
auto client = subnode->create_client<ListParameters>("service");
|
||||
EXPECT_STREQ(client->get_service_name(), "/ns/sub_ns/service");
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto client = node->create_client<ListParameters>("invalid_service?");
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,5 +25,5 @@ TEST(TestInit, is_initialized) {
|
||||
|
||||
rclcpp::shutdown();
|
||||
|
||||
EXPECT_TRUE(rclcpp::is_initialized());
|
||||
EXPECT_FALSE(rclcpp::is_initialized());
|
||||
}
|
||||
|
||||
71
rclcpp/test/test_local_parameters.cpp
Normal file
71
rclcpp/test/test_local_parameters.cpp
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2018 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 <cstdio>
|
||||
#include <map>
|
||||
#include <string>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
TEST(test_local_parameters, set_parameter_if_not_set) {
|
||||
auto node = rclcpp::Node::make_shared("test_local_parameters_set_parameter_if_not_set");
|
||||
|
||||
{
|
||||
// try to set a map of parameters
|
||||
std::map<std::string, double> bar_map{
|
||||
{"x", 0.5},
|
||||
{"y", 1.0},
|
||||
};
|
||||
node->set_parameters_if_not_set("bar", bar_map);
|
||||
double bar_x_value;
|
||||
ASSERT_TRUE(node->get_parameter("bar.x", bar_x_value));
|
||||
EXPECT_EQ(bar_x_value, 0.5);
|
||||
double bar_y_value;
|
||||
ASSERT_TRUE(node->get_parameter("bar.y", bar_y_value));
|
||||
EXPECT_EQ(bar_y_value, 1.0);
|
||||
std::map<std::string, double> new_map;
|
||||
ASSERT_TRUE(node->get_parameters("bar", new_map));
|
||||
ASSERT_EQ(new_map.size(), 2U);
|
||||
EXPECT_EQ(new_map["x"], 0.5);
|
||||
EXPECT_EQ(new_map["y"], 1.0);
|
||||
}
|
||||
|
||||
{
|
||||
// try to get a map of parameters that doesn't exist
|
||||
std::map<std::string, double> no_exist_map;
|
||||
ASSERT_FALSE(node->get_parameters("no_exist", no_exist_map));
|
||||
}
|
||||
|
||||
{
|
||||
// set parameters for a map with different types, then try to get them back as a map
|
||||
node->set_parameter_if_not_set("baz.x", 1.0);
|
||||
node->set_parameter_if_not_set("baz.y", "hello");
|
||||
std::map<std::string, double> baz_map;
|
||||
EXPECT_THROW(node->get_parameters("baz", baz_map), rclcpp::ParameterTypeException);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char ** argv)
|
||||
{
|
||||
::setvbuf(stdout, NULL, _IONBF, BUFSIZ);
|
||||
|
||||
// NOTE: use custom main to ensure that rclcpp::init is called only once
|
||||
rclcpp::init(argc, argv);
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
int ret = RUN_ALL_TESTS();
|
||||
rclcpp::shutdown();
|
||||
return ret;
|
||||
}
|
||||
@@ -57,21 +57,168 @@ TEST_F(TestNode, get_name_and_namespace) {
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
EXPECT_STREQ("my_node", node->get_name());
|
||||
EXPECT_STREQ("/ns", node->get_namespace());
|
||||
EXPECT_STREQ("/ns/my_node", node->get_fully_qualified_name());
|
||||
}
|
||||
{
|
||||
auto options = rclcpp::NodeOptions()
|
||||
.arguments({"__ns:=/another_ns"});
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns", options);
|
||||
EXPECT_STREQ("my_node", node->get_name());
|
||||
EXPECT_STREQ("/another_ns", node->get_namespace());
|
||||
EXPECT_STREQ("/another_ns/my_node", node->get_fully_qualified_name());
|
||||
}
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "ns");
|
||||
EXPECT_STREQ("my_node", node->get_name());
|
||||
EXPECT_STREQ("/ns", node->get_namespace());
|
||||
EXPECT_STREQ("/ns/my_node", node->get_fully_qualified_name());
|
||||
}
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node");
|
||||
EXPECT_STREQ("my_node", node->get_name());
|
||||
EXPECT_STREQ("/", node->get_namespace());
|
||||
EXPECT_STREQ("/my_node", node->get_fully_qualified_name());
|
||||
}
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "");
|
||||
EXPECT_STREQ("my_node", node->get_name());
|
||||
EXPECT_STREQ("/", node->get_namespace());
|
||||
EXPECT_STREQ("/my_node", node->get_fully_qualified_name());
|
||||
}
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/my/ns");
|
||||
EXPECT_STREQ("my_node", node->get_name());
|
||||
EXPECT_STREQ("/my/ns", node->get_namespace());
|
||||
EXPECT_STREQ("/my/ns/my_node", node->get_fully_qualified_name());
|
||||
}
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "my/ns");
|
||||
EXPECT_STREQ("my_node", node->get_name());
|
||||
EXPECT_STREQ("/my/ns", node->get_namespace());
|
||||
EXPECT_STREQ("/my/ns/my_node", node->get_fully_qualified_name());
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TestNode, subnode_get_name_and_namespace) {
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "ns");
|
||||
auto subnode = node->create_sub_node("sub_ns");
|
||||
EXPECT_STREQ("my_node", subnode->get_name());
|
||||
EXPECT_STREQ("/ns", subnode->get_namespace());
|
||||
EXPECT_STREQ("sub_ns", subnode->get_sub_namespace().c_str());
|
||||
EXPECT_STREQ("/ns/sub_ns", subnode->get_effective_namespace().c_str());
|
||||
}
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto subnode = node->create_sub_node("sub_ns");
|
||||
EXPECT_STREQ("my_node", subnode->get_name());
|
||||
EXPECT_STREQ("/ns", subnode->get_namespace());
|
||||
EXPECT_STREQ("sub_ns", subnode->get_sub_namespace().c_str());
|
||||
EXPECT_STREQ("/ns/sub_ns", subnode->get_effective_namespace().c_str());
|
||||
}
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node");
|
||||
auto subnode = node->create_sub_node("sub_ns");
|
||||
EXPECT_STREQ("my_node", subnode->get_name());
|
||||
EXPECT_STREQ("/", subnode->get_namespace());
|
||||
EXPECT_STREQ("sub_ns", subnode->get_sub_namespace().c_str());
|
||||
EXPECT_STREQ("/sub_ns", subnode->get_effective_namespace().c_str());
|
||||
}
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto subnode = node->create_sub_node("sub_ns");
|
||||
EXPECT_STREQ("my_node", subnode->get_name());
|
||||
EXPECT_STREQ("/ns", subnode->get_namespace());
|
||||
EXPECT_STREQ("sub_ns", subnode->get_sub_namespace().c_str());
|
||||
EXPECT_STREQ("/ns/sub_ns", subnode->get_effective_namespace().c_str());
|
||||
auto subnode2 = subnode->create_sub_node("sub_ns2");
|
||||
EXPECT_STREQ("my_node", subnode2->get_name());
|
||||
EXPECT_STREQ("/ns", subnode2->get_namespace());
|
||||
EXPECT_STREQ("sub_ns/sub_ns2", subnode2->get_sub_namespace().c_str());
|
||||
EXPECT_STREQ("/ns/sub_ns/sub_ns2", subnode2->get_effective_namespace().c_str());
|
||||
}
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node");
|
||||
auto subnode = node->create_sub_node("sub_ns");
|
||||
EXPECT_STREQ("my_node", subnode->get_name());
|
||||
EXPECT_STREQ("/", subnode->get_namespace());
|
||||
EXPECT_STREQ("sub_ns", subnode->get_sub_namespace().c_str());
|
||||
EXPECT_STREQ("/sub_ns", subnode->get_effective_namespace().c_str());
|
||||
auto subnode2 = subnode->create_sub_node("sub_ns2");
|
||||
EXPECT_STREQ("my_node", subnode2->get_name());
|
||||
EXPECT_STREQ("/", subnode2->get_namespace());
|
||||
EXPECT_STREQ("sub_ns/sub_ns2", subnode2->get_sub_namespace().c_str());
|
||||
EXPECT_STREQ("/sub_ns/sub_ns2", subnode2->get_effective_namespace().c_str());
|
||||
}
|
||||
{
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node");
|
||||
ASSERT_THROW({
|
||||
auto subnode = node->create_sub_node("/sub_ns");
|
||||
}, rclcpp::exceptions::NameValidationError);
|
||||
}
|
||||
}
|
||||
/*
|
||||
Testing node construction and destruction.
|
||||
*/
|
||||
TEST_F(TestNode, subnode_construction_and_destruction) {
|
||||
{
|
||||
ASSERT_NO_THROW({
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "ns");
|
||||
auto subnode = node->create_sub_node("sub_ns");
|
||||
});
|
||||
}
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "ns");
|
||||
auto subnode = node->create_sub_node("invalid_ns?");
|
||||
}, rclcpp::exceptions::InvalidNamespaceError);
|
||||
}
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "ns/");
|
||||
}, rclcpp::exceptions::InvalidNamespaceError);
|
||||
}
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "ns/");
|
||||
auto subnode = node->create_sub_node("/sub_ns");
|
||||
}, rclcpp::exceptions::InvalidNamespaceError);
|
||||
}
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "ns");
|
||||
auto subnode = node->create_sub_node("/sub_ns");
|
||||
}, rclcpp::exceptions::NameValidationError);
|
||||
}
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "ns");
|
||||
auto subnode = node->create_sub_node("~sub_ns");
|
||||
}, rclcpp::exceptions::InvalidNamespaceError);
|
||||
}
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto subnode = node->create_sub_node("invalid_ns?");
|
||||
}, rclcpp::exceptions::InvalidNamespaceError);
|
||||
}
|
||||
{
|
||||
ASSERT_NO_THROW({
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto subnode = node->create_sub_node("sub_ns");
|
||||
});
|
||||
}
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto subnode = node->create_sub_node("/sub_ns");
|
||||
}, rclcpp::exceptions::NameValidationError);
|
||||
}
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
auto subnode = node->create_sub_node("~sub_ns");
|
||||
}, rclcpp::exceptions::InvalidNamespaceError);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/node.hpp"
|
||||
#include "rclcpp/node_options.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
class TestNodeWithGlobalArgs : public ::testing::Test
|
||||
@@ -33,32 +34,26 @@ protected:
|
||||
};
|
||||
|
||||
TEST_F(TestNodeWithGlobalArgs, local_arguments_before_global) {
|
||||
auto context = rclcpp::contexts::default_context::get_global_default_context();
|
||||
const std::vector<std::string> arguments = {"__node:=local_arguments_test"};
|
||||
const std::vector<rclcpp::Parameter> initial_values = {};
|
||||
const bool use_global_arguments = true;
|
||||
const bool use_intra_process = false;
|
||||
auto node = rclcpp::Node::make_shared(
|
||||
"orig_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
|
||||
auto options = rclcpp::NodeOptions()
|
||||
.arguments({"__node:=local_arguments_test"});
|
||||
|
||||
auto node = rclcpp::Node::make_shared("orig_name", options);
|
||||
EXPECT_STREQ("local_arguments_test", node->get_name());
|
||||
}
|
||||
|
||||
TEST_F(TestNodeWithGlobalArgs, use_or_ignore_global_arguments) {
|
||||
auto context = rclcpp::contexts::default_context::get_global_default_context();
|
||||
const std::vector<std::string> arguments = {};
|
||||
const std::vector<rclcpp::Parameter> initial_values = {};
|
||||
const bool use_intra_process = false;
|
||||
|
||||
{ // Don't use global args
|
||||
const bool use_global_arguments = false;
|
||||
auto node = rclcpp::Node::make_shared(
|
||||
"orig_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
|
||||
auto options = rclcpp::NodeOptions()
|
||||
.use_global_arguments(false);
|
||||
|
||||
auto node = rclcpp::Node::make_shared("orig_name", options);
|
||||
EXPECT_STREQ("orig_name", node->get_name());
|
||||
}
|
||||
{ // Do use global args
|
||||
const bool use_global_arguments = true;
|
||||
auto node = rclcpp::Node::make_shared(
|
||||
"orig_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
|
||||
auto options = rclcpp::NodeOptions()
|
||||
.use_global_arguments(true);
|
||||
|
||||
auto node = rclcpp::Node::make_shared("orig_name", options);
|
||||
EXPECT_STREQ("global_node_name", node->get_name());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,29 +36,28 @@ protected:
|
||||
};
|
||||
|
||||
TEST_F(TestNodeWithInitialValues, no_initial_values) {
|
||||
auto context = rclcpp::contexts::default_context::get_global_default_context();
|
||||
const std::vector<std::string> arguments = {};
|
||||
const std::vector<rclcpp::Parameter> initial_values = {};
|
||||
const bool use_global_arguments = false;
|
||||
const bool use_intra_process = false;
|
||||
auto node = rclcpp::Node::make_shared(
|
||||
"node_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
|
||||
auto options = rclcpp::NodeOptions()
|
||||
.use_intra_process_comms(false)
|
||||
.use_global_arguments(false);
|
||||
|
||||
auto node = rclcpp::Node::make_shared("node_name", options);
|
||||
auto list_params_result = node->list_parameters({}, 0);
|
||||
EXPECT_EQ(0u, list_params_result.names.size());
|
||||
}
|
||||
|
||||
TEST_F(TestNodeWithInitialValues, multiple_initial_values) {
|
||||
auto context = rclcpp::contexts::default_context::get_global_default_context();
|
||||
const std::vector<std::string> arguments = {};
|
||||
const std::vector<rclcpp::Parameter> initial_values = {
|
||||
auto parameters = std::vector<rclcpp::Parameter>({
|
||||
rclcpp::Parameter("foo", true),
|
||||
rclcpp::Parameter("bar", "hello world"),
|
||||
rclcpp::Parameter("baz", std::vector<double>{3.14, 2.718})
|
||||
};
|
||||
const bool use_global_arguments = false;
|
||||
const bool use_intra_process = false;
|
||||
auto node = rclcpp::Node::make_shared(
|
||||
"node_name", "", context, arguments, initial_values, use_global_arguments, use_intra_process);
|
||||
});
|
||||
|
||||
auto options = rclcpp::NodeOptions()
|
||||
.initial_parameters(parameters)
|
||||
.use_global_arguments(false)
|
||||
.use_intra_process_comms(false);
|
||||
|
||||
auto node = rclcpp::Node::make_shared("node_name", options);
|
||||
auto list_params_result = node->list_parameters({}, 0);
|
||||
EXPECT_EQ(3u, list_params_result.names.size());
|
||||
EXPECT_TRUE(node->get_parameter("foo").get_value<bool>());
|
||||
|
||||
70
rclcpp/test/test_pub_sub_option_interface.cpp
Normal file
70
rclcpp/test/test_pub_sub_option_interface.cpp
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2019 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
#include "test_msgs/msg/empty.hpp"
|
||||
|
||||
class TestPubSubOptionAPI : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
TEST_F(TestPubSubOptionAPI, check_for_ambiguous) {
|
||||
rclcpp::PublisherOptions pub_options;
|
||||
rclcpp::SubscriptionOptions sub_options;
|
||||
|
||||
auto topic_only_pub = node->create_publisher<test_msgs::msg::Empty>(
|
||||
"topic_only");
|
||||
auto topic_depth_pub = node->create_publisher<test_msgs::msg::Empty>(
|
||||
"topic_depth",
|
||||
10);
|
||||
auto all_options_pub = node->create_publisher<test_msgs::msg::Empty>(
|
||||
"topic_options",
|
||||
10,
|
||||
pub_options);
|
||||
|
||||
auto topic_only_sub = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic_only",
|
||||
[](std::shared_ptr<test_msgs::msg::Empty>) {});
|
||||
auto topic_depth_sub = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic_depth",
|
||||
[](std::shared_ptr<test_msgs::msg::Empty>) {},
|
||||
10);
|
||||
auto all_options_sub = node->create_subscription<test_msgs::msg::Empty>(
|
||||
"topic_options",
|
||||
[](std::shared_ptr<test_msgs::msg::Empty>) {},
|
||||
10,
|
||||
sub_options);
|
||||
}
|
||||
@@ -30,9 +30,9 @@ protected:
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
void initialize(const rclcpp::NodeOptions & node_options = rclcpp::NodeOptions())
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
node = std::make_shared<rclcpp::Node>("my_node", "/ns", node_options);
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
@@ -43,10 +43,33 @@ protected:
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
class TestPublisherSub : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
subnode = node->create_sub_node("sub_ns");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::Node::SharedPtr subnode;
|
||||
};
|
||||
|
||||
/*
|
||||
Testing publisher construction and destruction.
|
||||
*/
|
||||
TEST_F(TestPublisher, construction_and_destruction) {
|
||||
initialize();
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
{
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>("topic");
|
||||
@@ -58,3 +81,47 @@ TEST_F(TestPublisher, construction_and_destruction) {
|
||||
}, rclcpp::exceptions::InvalidTopicNameError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing publisher with intraprocess enabled and invalid QoS
|
||||
*/
|
||||
TEST_F(TestPublisher, intraprocess_with_invalid_qos) {
|
||||
initialize(rclcpp::NodeOptions().use_intra_process_comms(true));
|
||||
rmw_qos_profile_t qos = {
|
||||
RMW_QOS_POLICY_HISTORY_KEEP_LAST,
|
||||
1,
|
||||
RMW_QOS_POLICY_RELIABILITY_RELIABLE,
|
||||
RMW_QOS_POLICY_DURABILITY_TRANSIENT_LOCAL,
|
||||
false
|
||||
};
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
{
|
||||
ASSERT_THROW(
|
||||
{auto publisher = node->create_publisher<IntraProcessMessage>("topic", qos);},
|
||||
rclcpp::exceptions::InvalidParametersException);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing publisher construction and destruction for subnodes.
|
||||
*/
|
||||
TEST_F(TestPublisherSub, construction_and_destruction) {
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
{
|
||||
auto publisher = subnode->create_publisher<IntraProcessMessage>("topic");
|
||||
|
||||
EXPECT_STREQ(publisher->get_topic_name(), "/ns/sub_ns/topic");
|
||||
}
|
||||
|
||||
{
|
||||
auto publisher = subnode->create_publisher<IntraProcessMessage>("/topic");
|
||||
|
||||
EXPECT_STREQ(publisher->get_topic_name(), "/topic");
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto publisher = subnode->create_publisher<IntraProcessMessage>("invalid_topic?");
|
||||
}, rclcpp::exceptions::InvalidTopicNameError);
|
||||
}
|
||||
}
|
||||
|
||||
178
rclcpp/test/test_publisher_subscription_count_api.cpp
Normal file
178
rclcpp/test/test_publisher_subscription_count_api.cpp
Normal file
@@ -0,0 +1,178 @@
|
||||
// Copyright 2019 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/publisher.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
|
||||
/**
|
||||
* Parameterized test.
|
||||
* The first param are the NodeOptions used to create the nodes.
|
||||
* The second param are the expect intraprocess count results.
|
||||
*/
|
||||
struct TestParameters
|
||||
{
|
||||
rclcpp::NodeOptions node_options[2];
|
||||
uint64_t intraprocess_count_results[2];
|
||||
std::string description;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & out, const TestParameters & params)
|
||||
{
|
||||
out << params.description;
|
||||
return out;
|
||||
}
|
||||
|
||||
class TestPublisherSubscriptionCount : public ::testing::TestWithParam<TestParameters>
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp() {}
|
||||
|
||||
void TearDown() {}
|
||||
|
||||
static std::chrono::milliseconds offset;
|
||||
};
|
||||
|
||||
std::chrono::milliseconds TestPublisherSubscriptionCount::offset = std::chrono::milliseconds(2000);
|
||||
|
||||
void OnMessage(const rcl_interfaces::msg::IntraProcessMessage::SharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
|
||||
TEST_P(TestPublisherSubscriptionCount, increasing_and_decreasing_counts)
|
||||
{
|
||||
TestParameters parameters = GetParam();
|
||||
rclcpp::Node::SharedPtr node = std::make_shared<rclcpp::Node>(
|
||||
"my_node",
|
||||
"/ns",
|
||||
parameters.node_options[0]);
|
||||
auto publisher = node->create_publisher<IntraProcessMessage>("/topic");
|
||||
|
||||
EXPECT_EQ(publisher->get_subscription_count(), 0u);
|
||||
EXPECT_EQ(publisher->get_intra_process_subscription_count(), 0u);
|
||||
{
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("/topic", &OnMessage);
|
||||
rclcpp::sleep_for(offset);
|
||||
EXPECT_EQ(publisher->get_subscription_count(), 1u);
|
||||
EXPECT_EQ(
|
||||
publisher->get_intra_process_subscription_count(),
|
||||
parameters.intraprocess_count_results[0]);
|
||||
{
|
||||
rclcpp::Node::SharedPtr another_node = std::make_shared<rclcpp::Node>(
|
||||
"another_node",
|
||||
"/ns",
|
||||
parameters.node_options[1]);
|
||||
auto another_sub =
|
||||
another_node->create_subscription<IntraProcessMessage>("/topic", &OnMessage);
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
EXPECT_EQ(publisher->get_subscription_count(), 2u);
|
||||
EXPECT_EQ(
|
||||
publisher->get_intra_process_subscription_count(),
|
||||
parameters.intraprocess_count_results[1]);
|
||||
}
|
||||
rclcpp::sleep_for(offset);
|
||||
EXPECT_EQ(publisher->get_subscription_count(), 1u);
|
||||
EXPECT_EQ(
|
||||
publisher->get_intra_process_subscription_count(),
|
||||
parameters.intraprocess_count_results[0]);
|
||||
}
|
||||
/**
|
||||
* Counts should be zero here, as all are subscriptions are out of scope.
|
||||
* Subscriptions count checking is always preceeded with an sleep, as random failures had been
|
||||
* detected without it. */
|
||||
rclcpp::sleep_for(offset);
|
||||
EXPECT_EQ(publisher->get_subscription_count(), 0u);
|
||||
EXPECT_EQ(publisher->get_intra_process_subscription_count(), 0u);
|
||||
}
|
||||
|
||||
auto get_new_context()
|
||||
{
|
||||
auto context = rclcpp::Context::make_shared();
|
||||
context->init(0, nullptr);
|
||||
return context;
|
||||
}
|
||||
|
||||
TestParameters parameters[] = {
|
||||
/*
|
||||
Testing publisher subscription count api and internal process subscription count.
|
||||
Two subscriptions in the same topic, both using intraprocess comm.
|
||||
*/
|
||||
{
|
||||
{
|
||||
rclcpp::NodeOptions().use_intra_process_comms(true),
|
||||
rclcpp::NodeOptions().use_intra_process_comms(true)
|
||||
},
|
||||
{1u, 2u},
|
||||
"two_subscriptions_intraprocess_comm"
|
||||
},
|
||||
/*
|
||||
Testing publisher subscription count api and internal process subscription count.
|
||||
Two subscriptions, one using intra-process comm and the other not using it.
|
||||
*/
|
||||
{
|
||||
{
|
||||
rclcpp::NodeOptions().use_intra_process_comms(true),
|
||||
rclcpp::NodeOptions().use_intra_process_comms(false)
|
||||
},
|
||||
{1u, 1u},
|
||||
"two_subscriptions_one_intraprocess_one_not"
|
||||
},
|
||||
/*
|
||||
Testing publisher subscription count api and internal process subscription count.
|
||||
Two contexts, both using intra-process.
|
||||
*/
|
||||
{
|
||||
{
|
||||
rclcpp::NodeOptions().use_intra_process_comms(true),
|
||||
rclcpp::NodeOptions().context(get_new_context()).use_intra_process_comms(true)
|
||||
},
|
||||
{1u, 1u},
|
||||
"two_subscriptions_in_two_contexts_with_intraprocess_comm"
|
||||
},
|
||||
/*
|
||||
Testing publisher subscription count api and internal process subscription count.
|
||||
Two contexts, both of them not using intra-process comm.
|
||||
*/
|
||||
{
|
||||
{
|
||||
rclcpp::NodeOptions().use_intra_process_comms(false),
|
||||
rclcpp::NodeOptions().context(get_new_context()).use_intra_process_comms(false)
|
||||
},
|
||||
{0u, 0u},
|
||||
"two_subscriptions_in_two_contexts_without_intraprocess_comm"
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
TestWithDifferentNodeOptions, TestPublisherSubscriptionCount,
|
||||
::testing::ValuesIn(parameters),
|
||||
::testing::PrintToStringParamName());
|
||||
@@ -43,6 +43,28 @@ protected:
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
class TestServiceSub : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("my_node", "/ns");
|
||||
subnode = node->create_sub_node("sub_ns");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::Node::SharedPtr subnode;
|
||||
};
|
||||
|
||||
/*
|
||||
Testing service construction and destruction.
|
||||
*/
|
||||
@@ -61,3 +83,23 @@ TEST_F(TestService, construction_and_destruction) {
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing service construction and destruction for subnodes.
|
||||
*/
|
||||
TEST_F(TestServiceSub, construction_and_destruction) {
|
||||
using rcl_interfaces::srv::ListParameters;
|
||||
auto callback =
|
||||
[](const ListParameters::Request::SharedPtr, ListParameters::Response::SharedPtr) {
|
||||
};
|
||||
{
|
||||
auto service = subnode->create_service<ListParameters>("service", callback);
|
||||
EXPECT_STREQ(service->get_service_name(), "/ns/sub_ns/service");
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto service = node->create_service<ListParameters>("invalid_service?", callback);
|
||||
}, rclcpp::exceptions::InvalidServiceNameError);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,6 +49,34 @@ protected:
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
class TestSubscriptionSub : public ::testing::Test
|
||||
{
|
||||
public:
|
||||
void OnMessage(const rcl_interfaces::msg::IntraProcessMessage::SharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
|
||||
protected:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("test_subscription", "/ns");
|
||||
subnode = node->create_sub_node("sub_ns");
|
||||
}
|
||||
|
||||
void TearDown()
|
||||
{
|
||||
node.reset();
|
||||
}
|
||||
|
||||
rclcpp::Node::SharedPtr node;
|
||||
rclcpp::Node::SharedPtr subnode;
|
||||
};
|
||||
|
||||
class SubscriptionClassNodeInheritance : public rclcpp::Node
|
||||
{
|
||||
public:
|
||||
@@ -107,6 +135,38 @@ TEST_F(TestSubscription, construction_and_destruction) {
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing subscription construction and destruction for subnodes.
|
||||
*/
|
||||
TEST_F(TestSubscriptionSub, construction_and_destruction) {
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
auto callback = [](const IntraProcessMessage::SharedPtr msg) {
|
||||
(void)msg;
|
||||
};
|
||||
{
|
||||
auto sub = subnode->create_subscription<IntraProcessMessage>("topic", callback);
|
||||
EXPECT_STREQ(sub->get_topic_name(), "/ns/sub_ns/topic");
|
||||
}
|
||||
|
||||
{
|
||||
auto sub = subnode->create_subscription<IntraProcessMessage>("/topic", callback);
|
||||
EXPECT_STREQ(sub->get_topic_name(), "/topic");
|
||||
}
|
||||
|
||||
{
|
||||
auto sub = subnode->create_subscription<IntraProcessMessage>("~/topic", callback);
|
||||
std::string expected_topic_name =
|
||||
std::string(node->get_namespace()) + "/" + node->get_name() + "/topic";
|
||||
EXPECT_STREQ(sub->get_topic_name(), expected_topic_name.c_str());
|
||||
}
|
||||
|
||||
{
|
||||
ASSERT_THROW({
|
||||
auto sub = node->create_subscription<IntraProcessMessage>("invalid_topic?", callback);
|
||||
}, rclcpp::exceptions::InvalidTopicNameError);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Testing subscriptions using std::bind.
|
||||
*/
|
||||
|
||||
123
rclcpp/test/test_subscription_publisher_count_api.cpp
Normal file
123
rclcpp/test/test_subscription_publisher_count_api.cpp
Normal file
@@ -0,0 +1,123 @@
|
||||
// Copyright 2019 Open Source Robotics Foundation, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/rclcpp.hpp"
|
||||
|
||||
#include "rcl_interfaces/msg/intra_process_message.hpp"
|
||||
|
||||
using rcl_interfaces::msg::IntraProcessMessage;
|
||||
|
||||
struct TestParameters
|
||||
{
|
||||
rclcpp::NodeOptions node_options;
|
||||
std::string description;
|
||||
};
|
||||
|
||||
std::ostream & operator<<(std::ostream & out, const TestParameters & params)
|
||||
{
|
||||
out << params.description;
|
||||
return out;
|
||||
}
|
||||
|
||||
class TestSubscriptionPublisherCount : public ::testing::TestWithParam<TestParameters>
|
||||
{
|
||||
public:
|
||||
static void SetUpTestCase()
|
||||
{
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
protected:
|
||||
void SetUp() {}
|
||||
void TearDown() {}
|
||||
static std::chrono::milliseconds offset;
|
||||
};
|
||||
|
||||
std::chrono::milliseconds TestSubscriptionPublisherCount::offset = std::chrono::milliseconds(2000);
|
||||
|
||||
void OnMessage(const rcl_interfaces::msg::IntraProcessMessage::SharedPtr msg)
|
||||
{
|
||||
(void)msg;
|
||||
}
|
||||
|
||||
TEST_P(TestSubscriptionPublisherCount, increasing_and_decreasing_counts)
|
||||
{
|
||||
rclcpp::NodeOptions node_options = GetParam().node_options;
|
||||
rclcpp::Node::SharedPtr node = std::make_shared<rclcpp::Node>(
|
||||
"my_node",
|
||||
"/ns",
|
||||
node_options);
|
||||
auto subscription = node->create_subscription<IntraProcessMessage>("/topic", &OnMessage);
|
||||
|
||||
EXPECT_EQ(subscription->get_publisher_count(), 0u);
|
||||
{
|
||||
auto pub = node->create_publisher<IntraProcessMessage>("/topic");
|
||||
rclcpp::sleep_for(offset);
|
||||
EXPECT_EQ(subscription->get_publisher_count(), 1u);
|
||||
{
|
||||
rclcpp::Node::SharedPtr another_node = std::make_shared<rclcpp::Node>(
|
||||
"another_node",
|
||||
"/ns",
|
||||
node_options);
|
||||
auto another_pub =
|
||||
another_node->create_publisher<IntraProcessMessage>("/topic");
|
||||
|
||||
rclcpp::sleep_for(offset);
|
||||
EXPECT_EQ(subscription->get_publisher_count(), 2u);
|
||||
}
|
||||
rclcpp::sleep_for(offset);
|
||||
EXPECT_EQ(subscription->get_publisher_count(), 1u);
|
||||
}
|
||||
rclcpp::sleep_for(offset);
|
||||
EXPECT_EQ(subscription->get_publisher_count(), 0u);
|
||||
}
|
||||
|
||||
auto get_new_context()
|
||||
{
|
||||
auto context = rclcpp::Context::make_shared();
|
||||
context->init(0, nullptr);
|
||||
return context;
|
||||
}
|
||||
|
||||
TestParameters parameters[] = {
|
||||
/*
|
||||
Testing subscription publisher count api.
|
||||
One context.
|
||||
*/
|
||||
{
|
||||
rclcpp::NodeOptions(),
|
||||
"one_context_test"
|
||||
},
|
||||
/*
|
||||
Testing subscription publisher count api.
|
||||
Two contexts.
|
||||
*/
|
||||
{
|
||||
rclcpp::NodeOptions().context(get_new_context()),
|
||||
"two_contexts_test"
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
TestWithDifferentNodeOptions,
|
||||
TestSubscriptionPublisherCount,
|
||||
testing::ValuesIn(parameters),
|
||||
testing::PrintToStringParamName());
|
||||
@@ -37,6 +37,11 @@ protected:
|
||||
rclcpp::init(0, nullptr);
|
||||
}
|
||||
|
||||
static void TearDownTestCase()
|
||||
{
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
void SetUp()
|
||||
{
|
||||
node = std::make_shared<rclcpp::Node>("my_node");
|
||||
@@ -50,16 +55,83 @@ protected:
|
||||
rclcpp::Node::SharedPtr node;
|
||||
};
|
||||
|
||||
void trigger_clock_changes(
|
||||
rclcpp::Node::SharedPtr node)
|
||||
void spin_until_time(
|
||||
rclcpp::Clock::SharedPtr clock,
|
||||
rclcpp::Node::SharedPtr node,
|
||||
std::chrono::nanoseconds end_time,
|
||||
bool expect_time_update)
|
||||
{
|
||||
auto clock_pub = node->create_publisher<rosgraph_msgs::msg::Clock>("clock",
|
||||
rmw_qos_profile_default);
|
||||
// Call spin_once on the node until either:
|
||||
// 1) We see the ros_clock's simulated time change to the expected end_time
|
||||
// -or-
|
||||
// 2) 1 second has elapsed in the real world
|
||||
// If 'expect_time_update' is True, and we timed out waiting for simulated time to
|
||||
// update, we'll have the test fail
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node);
|
||||
|
||||
rclcpp::WallRate loop_rate(50);
|
||||
auto start = std::chrono::system_clock::now();
|
||||
while (std::chrono::system_clock::now() < (start + 1s)) {
|
||||
if (!rclcpp::ok()) {
|
||||
break; // Break for ctrl-c
|
||||
}
|
||||
|
||||
executor.spin_once(10ms);
|
||||
|
||||
if (clock->now().nanoseconds() == end_time.count()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (expect_time_update) {
|
||||
// If we were expecting ROS clock->now to be updated and we didn't take the early return from
|
||||
// the loop up above, that's a failure
|
||||
ASSERT_TRUE(false) << "Timed out waiting for ROS time to update";
|
||||
}
|
||||
}
|
||||
|
||||
void spin_until_ros_time_updated(
|
||||
rclcpp::Clock::SharedPtr clock,
|
||||
rclcpp::Node::SharedPtr node,
|
||||
rclcpp::ParameterValue value)
|
||||
{
|
||||
// Similar to above: Call spin_once until we see the clock's ros_time_is_active method
|
||||
// match the ParameterValue
|
||||
// Unlike spin_until_time, there aren't any test cases where we don't expect the value to
|
||||
// update. In the event that the ParameterValue is not set, we'll pump messages for a full second
|
||||
// but we don't cause the test to fail
|
||||
|
||||
rclcpp::executors::SingleThreadedExecutor executor;
|
||||
executor.add_node(node);
|
||||
|
||||
auto start = std::chrono::system_clock::now();
|
||||
while (std::chrono::system_clock::now() < (start + 1s)) {
|
||||
if (!rclcpp::ok()) {
|
||||
break; // Break for ctrl-c
|
||||
}
|
||||
|
||||
executor.spin_once(10ms);
|
||||
|
||||
// In the case where we didn't intend to change the parameter, we'll still pump
|
||||
if (value.get_type() == rclcpp::ParameterType::PARAMETER_NOT_SET) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (clock->ros_time_is_active() == value.get<bool>()) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void trigger_clock_changes(
|
||||
rclcpp::Node::SharedPtr node,
|
||||
std::shared_ptr<rclcpp::Clock> clock,
|
||||
bool expect_time_update = true)
|
||||
{
|
||||
auto clock_pub = node->create_publisher<rosgraph_msgs::msg::Clock>("clock",
|
||||
rmw_qos_profile_default);
|
||||
|
||||
for (int i = 0; i < 5; ++i) {
|
||||
if (!rclcpp::ok()) {
|
||||
break; // Break for ctrl-c
|
||||
@@ -68,12 +140,23 @@ void trigger_clock_changes(
|
||||
msg->clock.sec = i;
|
||||
msg->clock.nanosec = 1000;
|
||||
clock_pub->publish(msg);
|
||||
executor.spin_once(1000000ns);
|
||||
loop_rate.sleep();
|
||||
|
||||
// workaround. Long-term, there can be a more elegant fix where we hook a future up
|
||||
// to a clock change callback and spin until future complete, but that's an upstream
|
||||
// change
|
||||
spin_until_time(
|
||||
clock,
|
||||
node,
|
||||
std::chrono::seconds(i) + std::chrono::nanoseconds(1000),
|
||||
expect_time_update
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void set_use_sim_time_parameter(rclcpp::Node::SharedPtr node, rclcpp::ParameterValue value)
|
||||
void set_use_sim_time_parameter(
|
||||
rclcpp::Node::SharedPtr node,
|
||||
rclcpp::ParameterValue value,
|
||||
rclcpp::Clock::SharedPtr clock)
|
||||
{
|
||||
auto parameters_client = std::make_shared<rclcpp::SyncParametersClient>(node);
|
||||
|
||||
@@ -85,10 +168,12 @@ void set_use_sim_time_parameter(rclcpp::Node::SharedPtr node, rclcpp::ParameterV
|
||||
for (auto & result : set_parameters_results) {
|
||||
EXPECT_TRUE(result.successful);
|
||||
}
|
||||
// SyncParametersClient returns when parameters have been set on the node_parameters interface,
|
||||
// but it doesn't mean the on_parameter_event subscription in TimeSource has been called.
|
||||
// Spin some to handle that subscription.
|
||||
rclcpp::spin_some(node);
|
||||
|
||||
// Same as above - workaround for a little bit of asynchronus behavior. The sim_time paramater
|
||||
// is set synchronously, but the way the ros clock gets notified involves a pub/sub that happens
|
||||
// AFTER the synchronous notification that the parameter was set. This may also get fixed
|
||||
// upstream
|
||||
spin_until_ros_time_updated(clock, node, value);
|
||||
}
|
||||
|
||||
TEST_F(TestTimeSource, detachUnattached) {
|
||||
@@ -107,7 +192,7 @@ TEST_F(TestTimeSource, reattach) {
|
||||
ASSERT_NO_THROW(ts.attachNode(node));
|
||||
}
|
||||
|
||||
TEST_F(TestTimeSource, ROS_time_valid) {
|
||||
TEST_F(TestTimeSource, ROS_time_valid_attach_detach) {
|
||||
rclcpp::TimeSource ts;
|
||||
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
|
||||
|
||||
@@ -126,6 +211,37 @@ TEST_F(TestTimeSource, ROS_time_valid) {
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
}
|
||||
|
||||
TEST_F(TestTimeSource, ROS_time_valid_wall_time) {
|
||||
rclcpp::TimeSource ts;
|
||||
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
|
||||
auto ros_clock2 = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
|
||||
|
||||
ts.attachClock(ros_clock);
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
|
||||
ts.attachNode(node);
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
|
||||
ts.attachClock(ros_clock2);
|
||||
EXPECT_FALSE(ros_clock2->ros_time_is_active());
|
||||
}
|
||||
|
||||
TEST_F(TestTimeSource, ROS_time_valid_sim_time) {
|
||||
rclcpp::TimeSource ts;
|
||||
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
|
||||
auto ros_clock2 = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
|
||||
|
||||
ts.attachClock(ros_clock);
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
|
||||
node->set_parameter_if_not_set("use_sim_time", true);
|
||||
ts.attachNode(node);
|
||||
EXPECT_TRUE(ros_clock->ros_time_is_active());
|
||||
|
||||
ts.attachClock(ros_clock2);
|
||||
EXPECT_TRUE(ros_clock2->ros_time_is_active());
|
||||
}
|
||||
|
||||
TEST_F(TestTimeSource, clock) {
|
||||
rclcpp::TimeSource ts(node);
|
||||
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
|
||||
@@ -133,17 +249,17 @@ TEST_F(TestTimeSource, clock) {
|
||||
ts.attachClock(ros_clock);
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
|
||||
trigger_clock_changes(node);
|
||||
trigger_clock_changes(node, ros_clock, false);
|
||||
|
||||
// Even now that we've recieved a message, ROS time should still not be active since the
|
||||
// parameter has not been explicitly set.
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
|
||||
// Activate ROS time.
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true), ros_clock);
|
||||
EXPECT_TRUE(ros_clock->ros_time_is_active());
|
||||
|
||||
trigger_clock_changes(node);
|
||||
trigger_clock_changes(node, ros_clock);
|
||||
|
||||
auto t_out = ros_clock->now();
|
||||
|
||||
@@ -200,7 +316,10 @@ TEST_F(TestTimeSource, callbacks) {
|
||||
ts.attachClock(ros_clock);
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
|
||||
trigger_clock_changes(node);
|
||||
// Last arg below is 'expect_time_update' Since ros_time is not active yet, we don't expect
|
||||
// the simulated time to be updated by trigger_clock_changes. The method will pump messages
|
||||
// anyway, but won't fail the test when the simulated time doesn't update
|
||||
trigger_clock_changes(node, ros_clock, false);
|
||||
auto t_low = rclcpp::Time(1, 0, RCL_ROS_TIME);
|
||||
auto t_high = rclcpp::Time(10, 100000, RCL_ROS_TIME);
|
||||
|
||||
@@ -209,10 +328,10 @@ TEST_F(TestTimeSource, callbacks) {
|
||||
EXPECT_EQ(0, cbo.last_postcallback_id_);
|
||||
|
||||
// Activate ROS time.
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true), ros_clock);
|
||||
EXPECT_TRUE(ros_clock->ros_time_is_active());
|
||||
|
||||
trigger_clock_changes(node);
|
||||
trigger_clock_changes(node, ros_clock);
|
||||
|
||||
auto t_out = ros_clock->now();
|
||||
|
||||
@@ -230,7 +349,7 @@ TEST_F(TestTimeSource, callbacks) {
|
||||
std::bind(&CallbackObject::post_callback, &cbo, std::placeholders::_1, 2),
|
||||
jump_threshold);
|
||||
|
||||
trigger_clock_changes(node);
|
||||
trigger_clock_changes(node, ros_clock);
|
||||
|
||||
EXPECT_EQ(2, cbo.last_precallback_id_);
|
||||
EXPECT_EQ(2, cbo.last_postcallback_id_);
|
||||
@@ -249,7 +368,7 @@ TEST_F(TestTimeSource, callbacks) {
|
||||
std::function<void(rcl_time_jump_t)>(),
|
||||
jump_threshold);
|
||||
|
||||
trigger_clock_changes(node);
|
||||
trigger_clock_changes(node, ros_clock);
|
||||
EXPECT_EQ(3, cbo.last_precallback_id_);
|
||||
EXPECT_EQ(2, cbo.last_postcallback_id_);
|
||||
|
||||
@@ -259,7 +378,7 @@ TEST_F(TestTimeSource, callbacks) {
|
||||
std::bind(&CallbackObject::post_callback, &cbo, std::placeholders::_1, 4),
|
||||
jump_threshold);
|
||||
|
||||
trigger_clock_changes(node);
|
||||
trigger_clock_changes(node, ros_clock);
|
||||
EXPECT_EQ(3, cbo.last_precallback_id_);
|
||||
EXPECT_EQ(4, cbo.last_postcallback_id_);
|
||||
}
|
||||
@@ -294,10 +413,10 @@ TEST_F(TestTimeSource, callback_handler_erasure) {
|
||||
EXPECT_EQ(0, cbo.last_postcallback_id_);
|
||||
|
||||
// Activate ROS time.
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true), ros_clock);
|
||||
EXPECT_TRUE(ros_clock->ros_time_is_active());
|
||||
|
||||
trigger_clock_changes(node);
|
||||
trigger_clock_changes(node, ros_clock);
|
||||
|
||||
auto t_low = rclcpp::Time(1, 0, RCL_ROS_TIME);
|
||||
auto t_high = rclcpp::Time(10, 100000, RCL_ROS_TIME);
|
||||
@@ -321,7 +440,7 @@ TEST_F(TestTimeSource, callback_handler_erasure) {
|
||||
// Remove the last callback in the vector
|
||||
callback_handler2.reset();
|
||||
|
||||
trigger_clock_changes(node);
|
||||
trigger_clock_changes(node, ros_clock);
|
||||
|
||||
EXPECT_EQ(2, cbo.last_precallback_id_);
|
||||
EXPECT_EQ(2, cbo.last_postcallback_id_);
|
||||
@@ -335,7 +454,6 @@ TEST_F(TestTimeSource, callback_handler_erasure) {
|
||||
EXPECT_GT(t_high.nanoseconds(), t_out.nanoseconds());
|
||||
}
|
||||
|
||||
|
||||
TEST_F(TestTimeSource, parameter_activation) {
|
||||
rclcpp::TimeSource ts(node);
|
||||
auto ros_clock = std::make_shared<rclcpp::Clock>(RCL_ROS_TIME);
|
||||
@@ -344,27 +462,25 @@ TEST_F(TestTimeSource, parameter_activation) {
|
||||
ts.attachClock(ros_clock);
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true), ros_clock);
|
||||
EXPECT_TRUE(ros_clock->ros_time_is_active());
|
||||
|
||||
set_use_sim_time_parameter(
|
||||
node, rclcpp::ParameterValue(rclcpp::ParameterType::PARAMETER_NOT_SET));
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(), ros_clock);
|
||||
EXPECT_TRUE(ros_clock->ros_time_is_active());
|
||||
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(false));
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(false), ros_clock);
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
|
||||
set_use_sim_time_parameter(
|
||||
node, rclcpp::ParameterValue(rclcpp::ParameterType::PARAMETER_NOT_SET));
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(), ros_clock);
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
|
||||
// If the use_sim_time parameter is not explicitly set to True, this clock's use of sim time
|
||||
// should not be affected by the presence of a clock publisher.
|
||||
trigger_clock_changes(node);
|
||||
trigger_clock_changes(node, ros_clock, false);
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(false));
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(false), ros_clock);
|
||||
EXPECT_FALSE(ros_clock->ros_time_is_active());
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true), ros_clock);
|
||||
EXPECT_TRUE(ros_clock->ros_time_is_active());
|
||||
}
|
||||
|
||||
@@ -389,7 +505,7 @@ TEST_F(TestTimeSource, no_pre_jump_callback) {
|
||||
ts.attachClock(ros_clock);
|
||||
|
||||
// Activate ROS time
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true));
|
||||
set_use_sim_time_parameter(node, rclcpp::ParameterValue(true), ros_clock);
|
||||
ASSERT_TRUE(ros_clock->ros_time_is_active());
|
||||
|
||||
EXPECT_EQ(0, cbo.last_precallback_id_);
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <string>
|
||||
#include <memory>
|
||||
|
||||
#include "rclcpp/contexts/default_context.hpp"
|
||||
#include "rclcpp/exceptions.hpp"
|
||||
#include "rclcpp/utilities.hpp"
|
||||
|
||||
@@ -49,5 +50,34 @@ TEST(TestUtilities, init_with_args) {
|
||||
ASSERT_EQ(1u, other_args.size());
|
||||
ASSERT_EQ(std::string{"process_name"}, other_args[0]);
|
||||
|
||||
EXPECT_TRUE(rclcpp::is_initialized());
|
||||
EXPECT_TRUE(rclcpp::ok());
|
||||
rclcpp::shutdown();
|
||||
}
|
||||
|
||||
TEST(TestUtilities, multi_init) {
|
||||
auto context1 = std::make_shared<rclcpp::contexts::default_context::DefaultContext>();
|
||||
auto context2 = std::make_shared<rclcpp::contexts::default_context::DefaultContext>();
|
||||
|
||||
EXPECT_FALSE(rclcpp::ok(context1));
|
||||
EXPECT_FALSE(rclcpp::ok(context2));
|
||||
|
||||
context1->init(0, nullptr);
|
||||
|
||||
EXPECT_TRUE(rclcpp::ok(context1));
|
||||
EXPECT_FALSE(rclcpp::ok(context2));
|
||||
|
||||
context2->init(0, nullptr);
|
||||
|
||||
EXPECT_TRUE(rclcpp::ok(context1));
|
||||
EXPECT_TRUE(rclcpp::ok(context2));
|
||||
|
||||
rclcpp::shutdown(context1);
|
||||
|
||||
EXPECT_FALSE(rclcpp::ok(context1));
|
||||
EXPECT_TRUE(rclcpp::ok(context2));
|
||||
|
||||
rclcpp::shutdown(context2);
|
||||
|
||||
EXPECT_FALSE(rclcpp::ok(context1));
|
||||
EXPECT_FALSE(rclcpp::ok(context2));
|
||||
}
|
||||
|
||||
24
rclcpp_action/CHANGELOG.rst
Normal file
24
rclcpp_action/CHANGELOG.rst
Normal file
@@ -0,0 +1,24 @@
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
Changelog for package rclcpp_action
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
0.7.0 (2019-04-14)
|
||||
------------------
|
||||
* Fixed hard-coded duration type representation so int64_t isn't assumed. (`#648 <https://github.com/ros2/rclcpp/issues/648>`_)
|
||||
* Added documentation to rclcpp_action. (`#650 <https://github.com/ros2/rclcpp/pull/650>`_)
|
||||
* Updated to use separated action types. (`#601 <https://github.com/ros2/rclcpp/issues/601>`_)
|
||||
* Updated to wait for action server before sending goal. (`#637 <https://github.com/ros2/rclcpp/issues/637>`_)
|
||||
* Refactored server goal handle's try_canceling() function. (`#603 <https://github.com/ros2/rclcpp/issues/603>`_)
|
||||
* Contributors: Emerson Knapp, Jacob Perron, Michel Hidalgo, Shane Loretz
|
||||
|
||||
0.6.2 (2018-12-13)
|
||||
------------------
|
||||
|
||||
0.6.1 (2018-12-07)
|
||||
------------------
|
||||
* Added wait_for_action_server() for action clients (`#598 <https://github.com/ros2/rclcpp/issues/598>`_)
|
||||
* Updated to adapt to action implicit changes (`#602 <https://github.com/ros2/rclcpp/issues/602>`_)
|
||||
* Added rclcpp_action Server implementation (`#593 <https://github.com/ros2/rclcpp/issues/593>`_)
|
||||
* Added action client implementation (`#594 <https://github.com/ros2/rclcpp/issues/594>`_)
|
||||
* Added skeleton for Action Server and Client (`#579 <https://github.com/ros2/rclcpp/issues/579>`_)
|
||||
* Contributors: Michel Hidalgo, Shane Loretz, William Woodall
|
||||
92
rclcpp_action/CMakeLists.txt
Normal file
92
rclcpp_action/CMakeLists.txt
Normal file
@@ -0,0 +1,92 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
project(rclcpp_action)
|
||||
|
||||
find_package(ament_cmake_ros REQUIRED)
|
||||
find_package(action_msgs REQUIRED)
|
||||
find_package(rclcpp REQUIRED)
|
||||
find_package(rcl_action REQUIRED)
|
||||
find_package(rosidl_generator_cpp REQUIRED)
|
||||
|
||||
# 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")
|
||||
add_compile_options(-Wall -Wextra -Wpedantic)
|
||||
endif()
|
||||
|
||||
include_directories(include)
|
||||
|
||||
set(${PROJECT_NAME}_SRCS
|
||||
src/client.cpp
|
||||
src/server.cpp
|
||||
src/server_goal_handle.cpp
|
||||
src/types.cpp
|
||||
)
|
||||
|
||||
add_library(${PROJECT_NAME}
|
||||
${${PROJECT_NAME}_SRCS})
|
||||
|
||||
ament_target_dependencies(${PROJECT_NAME}
|
||||
"action_msgs"
|
||||
"rcl_action"
|
||||
"rclcpp"
|
||||
"rosidl_generator_c"
|
||||
"rosidl_generator_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_ACTION_BUILDING_LIBRARY")
|
||||
|
||||
install(
|
||||
DIRECTORY include/
|
||||
DESTINATION include)
|
||||
|
||||
install(
|
||||
TARGETS ${PROJECT_NAME}
|
||||
ARCHIVE DESTINATION lib
|
||||
LIBRARY DESTINATION lib
|
||||
RUNTIME DESTINATION bin
|
||||
)
|
||||
|
||||
# specific order: dependents before dependencies
|
||||
ament_export_include_directories(include)
|
||||
ament_export_libraries(${PROJECT_NAME})
|
||||
|
||||
ament_export_dependencies(ament_cmake)
|
||||
ament_export_dependencies(action_msgs)
|
||||
ament_export_dependencies(rclcpp)
|
||||
ament_export_dependencies(rcl_action)
|
||||
ament_export_dependencies(rosidl_generator_c)
|
||||
ament_export_dependencies(rosidl_generator_cpp)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
find_package(ament_cmake_gtest REQUIRED)
|
||||
find_package(ament_lint_auto REQUIRED)
|
||||
ament_lint_auto_find_test_dependencies()
|
||||
|
||||
ament_add_gtest(test_client test/test_client.cpp)
|
||||
if(TARGET test_client)
|
||||
ament_target_dependencies(test_client
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_client
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
|
||||
ament_add_gtest(test_server test/test_server.cpp)
|
||||
if(TARGET test_server)
|
||||
ament_target_dependencies(test_server
|
||||
"test_msgs"
|
||||
)
|
||||
target_link_libraries(test_server
|
||||
${PROJECT_NAME}
|
||||
)
|
||||
endif()
|
||||
|
||||
endif()
|
||||
|
||||
ament_package()
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user